MT4のEAで、 ナンピンを用いた物がよくあります。
これまで、私が作ってきたサンプルソースはナンピンを一切しないタイプの物ばかりでしたので、今回はこのナンピン手法をFXのEAに取り入れていきたいと思います。
ナンピンするとなった場合、実際プログラムでナンピンする方法はいたって簡単です。難しいのは、ナンピンする場所・タイミングだと思いますが、現在私が公開しているZigZagのサンプルEAはここでナンピンできたらなーというタイミングが結構あったのでZigZagのサンプルEAにナンピン手法を取り入れていきます。
※ナンピンしないタイプのZigZagサンプルEAの記事はこちら
主な構文
ナンピンはポジション管理になるので、こういう関数を使えばナンピンできるとかはありません。
プログラムでポジションを持っているかどうかを確認していく形になります。
ポジションを複数持たせたい
私が作るプログラムの場合、エントリー前にポジションを持っているかどうかを判断している場所があります。既にポジションがあればエントリーしない作りになっているためここを修正します。
●修正する部分if(total ==0 && orderPtn > 0)
●修正するとこんな感じ
if(total > 0 && orderPtn > 0) { OrderKekka = OrderSelect(total-1,SELECT_BY_POS,MODE_TRADES); if(OrderType()==OP_BUY && orderPtn == 2){ orderPtn =0; }else if(OrderType()==OP_SELL && orderPtn == 1){ orderPtn =0; } if(OrderType()==OP_BUY && (OrderOpenPrice() - 40 * Point < Ask)){ orderPtn =0; }else if(OrderType()==OP_SELL && (OrderOpenPrice() + 40 * Point > Bid)){ orderPtn =0; } //既にナンピンを4回している場合は追加ナンピンしない if(total>3){ orderPtn =0; } } if(orderPtn > 0)
こんな感じに修正すると、前回ポジションが4pips以上マイナスになっている場合に、同一方向に4ポジションまで持つようになります。5pips以上にしたい場合は2つの『40 * Point』の箇所を『50 * Point』にすればOKです。
また、ポジションを最大5つまで持たせたい場合は『if(total>3){』の箇所を『if(total>4){』にすればOKです。
今回の修正について

要は何がしたかったのかと言うと、修正前のZigZagEAはデメリットとして上図の事が発生していましたが、それを補う感じでデメリットが発生したら追加でエントリーするという感じの修正をしました。
この修正を加える前と後の違いを実際のエントリーポイントで確認してみるとこんな感じです。


黄色〇の部分を見て頂ければわかるように、エントリー後悪い方向に値が動くとナンピンして3つエントリーが増えている事がわかります。
ソースコード
//+------------------------------------------------------------------+ //| ZigZag_Sample.mq4 | //| Copyright 2020, mef Software. | //| https://fx-prog.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, mef Software." #property link "https://fx-prog.com/" #property version "1.00" input int E_Depth = 16; input int E_Deviation = 5; input int E_Backstep = 3; input int A_SPREAD = 10; input double Lots = 0.01; input int STOP_LOSS = 400; input int TP = 100; double ZigTop[5]; //ジグザグの山保存用 double ZigBottom[5]; //ジグザグの谷保存用 int TopPoint; int BottomPoint; datetime prevtime; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int init() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ int start() { int orderPtn=0; //0:何もしない 1:買い 2:売り int total=0; double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート bool OrderKekka; //--- //新しい足ができた時だけやる if(Time[0] != prevtime){ prevtime = Time[0]; }else{ return(0); } //***売買判断箇所***// //チェック用変数の初期化 TopPoint=0; BottomPoint=0; //ZigZagの高値安値を構造体に再セット getZigZag(1); //高値切り上げ安値切り上げでロング if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1) { orderPtn=1; } //高値切り下げ安値切り下げでショート else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] && ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1) { orderPtn=2; } else { orderPtn=0; } //***売買判断箇所***// //***決済判断箇所***// total=OrdersTotal(); //ナンピン処理 if(total > 0 && orderPtn > 0){ OrderKekka = OrderSelect(total-1,SELECT_BY_POS,MODE_TRADES); if(OrderType()==OP_BUY && orderPtn == 2){ orderPtn =0; }else if(OrderType()==OP_SELL && orderPtn == 1){ orderPtn =0; } if(OrderType()==OP_BUY && (OrderOpenPrice() - 40 * Point < Ask)){ orderPtn =0; }else if(OrderType()==OP_SELL && (OrderOpenPrice() + 40 * Point > Bid)){ orderPtn =0; } //既にナンピンを4回している場合は追加ナンピンしない if(total>3){ orderPtn =0; } } if(orderPtn > 0) { if(orderPtn == 1) { ea_order_stop_price = Ask - STOP_LOSS * Point; ea_order_good_price = Ask + TP * Point; } else if(orderPtn == 2) { ea_order_stop_price = Bid + STOP_LOSS * Point; ea_order_good_price = Bid - TP * Point; } //新規注文 OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0); } return(0); } //+------------------------------------------------------------------+ //|【関数】新規注文関数 | //| | //|【引数】 ea_order_entry_Type:売買(0:買 1:売) | //|【引数】 ea_order_stop_price:損切値 ea_order_good_price:利確値 | //|【引数】 orderComment:オーダーコメント ea_order_MagicNo:マジックNo | //| | //|【戻値】True:成功 | //| | //| bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo) { int order_resend_num; // エントリー試行回数 int ea_ticket_res; // チケットNo int errorcode; // エラーコード double ea_order_entry_price; // エントリーレート color order_Color; bool kekka; for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) { // エントリー試行回数上限:10回 if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD ){ if(ea_order_entry_Type == OP_BUY){ ea_order_entry_price = Ask; // 現在の買値でエントリー order_Color = clrBlue; Print(Ask+"でロングするよ!"); }else if(ea_order_entry_Type == OP_SELL){ ea_order_entry_price = Bid; // 現在の売値でエントリー order_Color = clrRed; Print(Bid+"でショートするよ!"); } // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。 ea_ticket_res = OrderSend( // 新規エントリー注文 NULL, // 通貨ペア ea_order_entry_Type, // オーダータイプ[OP_BUY / OP_SELL] Lots , // ロット[0.01単位](FXTFは1=10Lot) ea_order_entry_price, // オーダープライスレート 20, // スリップ上限 (int)[分解能 0.1pips] ea_order_stop_price, // ストップレート ea_order_good_price, // リミットレート orderComment, // オーダーコメント ea_order_MagicNo, // マジックナンバー(識別用) 0, // オーダーリミット時間 order_Color // オーダーアイコンカラー ); Print("結果:"+ea_ticket_res); if ( ea_ticket_res == -1) { // オーダーエラー errorcode = GetLastError(); // エラーコード取得 if( errorcode != ERR_NO_ERROR) { // エラー発生 printf("エラー"); } Sleep(2000); // 1000msec待ち RefreshRates(); // レート更新 printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price); } else { // 注文約定 Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price); Sleep(300); // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為) break; } }else{ Print("スプレッドNG 許容スプレッド:"+A_SPREAD+"pips未満 環境スプレッド:"+MarketInfo(NULL,MODE_SPREAD)+"pips"); Sleep(2000); // 2000msec待ち RefreshRates(); } } return kekka; } //+------------------------------------------------------------------+ //|【関数】ジグザグの値をセット //| //| //|【備考】なし //+------------------------------------------------------------------+ bool getZigZag(int i) { int n,m,t; bool kekka; m=0; t=0; kekka=false; for(n=i;n<=i+200;n++) { //ZigZagの値を取得 double Zg=NormalizeDouble(iCustom(NULL,0,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5); //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット if(Zg!=0 && Zg==NormalizeDouble(High[n],5)) { if(n==1){ TopPoint=1; } ZigTop[m++]=Zg; if(m>=4)break; } //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット if(Zg!=0 && Zg==NormalizeDouble(Low[n],5)) { if(n==1){ BottomPoint=1; } ZigBottom[t++]=Zg; if(t>=4)break; } } kekka=true; //目視確認用コメント Comment( "│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n", "│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n", "│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n", "│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n", "│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n" ); return(kekka); }
一応前述したナンピン修正を加えたZigZagEAのサンプルソースコードになります。
あと設定で、ZigZagのDepth値、指値、逆指値を少し変えてます。
それ以外の処理はZigZagのサンプルEAから何も変えていません。
EAレポート結果

修正前のZigZagのEAと同じ期間でバックテストを行いました。プロフィットファクターの値が1.09から1.15に改善されています。逆指値40pipsに対して指値を10pipsにしたので、勝率が52%から82%にまであがってますね。また、ここ近年の成績はかなり良い感じになってます。
カーブフィッティングしてしまうようなナンピン手法の修正を加えてはいないと思うので、ZigZagEAに対して、ナンピンは結構有効なのかなという印象です。
このナンピン型のZigZagロジックの調整例としては、ナンピン時に既に保持しているポジションの指値を変更してやったり、ナンピン時のロット数を変える等があると思います。ロット数を変える方法は、かなりマーチンゲール手法に近くなるので安定性はさらに増すと思います。(また別のタイミングでサンプルソースを作ってみようと思います)
以上、ZigZagのEAサンプルでナンピン手法を使うでした。
※2021/04/03追記
このZigZagEA(ナンピン版)をメンテして、ロット数や変更注文をするZigZagEAのサンプルソースを公開しました。
※ EAのサンプルソースを一覧表にまとめました
※ オススメ記事(EAが使える国内FX業者の一覧)
※ 1からEAの作り方を学びたい人はこちら
コメント