バックテストでは正常に稼働するけど、リアル口座ではOrderSend時に、エラーコード130(Invalid Stops)が出てしまう。かなりあれこれ調べまくって、なんとかある程度は解決しました。記事に残しておこうと思います。普通に仕様通りにプログラム組むだけではだめで、対処は必須ですね。
エラーコード130の意味
主に以下です。
- 損切りや利益の設定価格が今現在価格にとても近い
- 損切り、利益の計算が正しくない
- 損切り、利益の小数点の桁数が多すぎる
取引所によってはOrderSend時にSL、TPを設定するとエラー
なんと、取引所によっては、OrderSend時に、引数でSL、TPを設定するとエラーになるそうです。マジかよ~。
XMはたまに通るけどエラーが帰って来るときもありました。
その場合は、損切り、利益は0で設定して、OrderModifyで変更するのが鉄板らしい。
ただし、成功しないときもある(マジかよ~)
以下のようにする事で対処しました。Long、Short、それぞれ用でオリジナル関数を作りました。こうすることで、メイン処理がごちゃつかない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
//+------------------------------------------------------------------+ //| ロング //| OP_BUYのエントリー。リトライあり //| 引数: my_lot = ロット //| my_slipage = スリッページ //| my_sl = 損切り(pips) //| my_tp = 利益(pips) //+------------------------------------------------------------------+ void Org_Long(double my_lot,int my_slipage , double my_sl, double my_tp){ //ticket no int ticket_no=0; int errorcode=0; // エラーコード double losscut_normalize=0; double profit_normalize=0; double losscut_rate=0; double profit_rate=0; // ロスカット価格 losscut_rate = Ask - ( 10 * Point() * my_sl); // 決済価格 profit_rate = Ask + ( 10 * Point() * my_tp); //正規化 losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); profit_normalize = NormalizeDouble(profit_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); //ロングエントリー処理 ticket_no = OrderSend(Symbol(),OP_BUY,my_lot,Ask,my_slipage,losscut_normalize,profit_normalize,"Buy order",MAGIC,0,clrBlue); printf("OrderSend OP_BUY , price=%f , sl=%f , tp=%f",Ask,losscut_normalize,profit_normalize); if(ticket_no < 0){ errorcode = GetLastError(); // エラーコード取得 printf("Send Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode)); //再設定 losscut_rate = Ask - ( 10 * Point() * my_sl); profit_rate = Ask + ( 10 * Point() * my_tp); losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); profit_normalize = NormalizeDouble(profit_normalize,int(MarketInfo(Symbol(),MODE_DIGITS))); //オーダー送信と同時に損切り、利益設定エラー対策 ticket_no = OrderSend(Symbol(),OP_BUY,my_lot,Ask,my_slipage,0,0,"Buy order",MAGIC,0,clrBlue); printf("Resend OrderSend OP_BUY , price=%f , sl=%f , tp=%f",Ask,losscut_normalize,profit_normalize); //オーダー修正 LimitStop_Set(ticket_no,OP_BUY,clrBlue); }else { printf("Send Done. Ticket NO = %d",ticket_no); } } //+------------------------------------------------------------------+ //| ショート //| OP_SELLのエントリー。リトライあり //| 引数: my_lot = ロット //| my_slipage = スリッページ //| my_sl = 損切り //| my_tp = 利益 //+------------------------------------------------------------------+ void Org_Short(double my_lot,int my_slipage , double my_sl, double my_tp){ //ticket no int ticket_no=0; int errorcode=0; // エラーコード double losscut_normalize=0; double profit_normalize=0; double losscut_rate=0; double profit_rate=0; // ロスカット価格 losscut_rate = Bid + ( 10 * Point() * my_sl); // 決済価格 profit_rate = Bid - ( 10 * Point() * my_tp); //正規化 losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); profit_normalize = NormalizeDouble(profit_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); //shortエントリー処理 ticket_no = OrderSend(Symbol(),OP_SELL,my_lot,Bid,my_slipage,losscut_normalize,profit_normalize,"Sell order",MAGIC,0,clrRed); printf("OrderSend OP_SELL , price=%f , sl=%f , tp=%f",Bid,losscut_normalize,profit_normalize); //エラー? if(ticket_no < 0){ errorcode = GetLastError(); // エラーコード取得 printf("Send Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode)); // 再設定 losscut_rate = Bid + ( 10 * Point() * my_sl); profit_rate = Bid - ( 10 * Point() * my_tp); losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS))); profit_normalize = NormalizeDouble(profit_normalize,int(MarketInfo(Symbol(),MODE_DIGITS))); //オーダー送信と同時に損切り、利益設定エラー対策 ticket_no = OrderSend(Symbol(),OP_SELL,my_lot,Bid,my_slipage,0,0,"Sell order",MAGIC,0,clrRed); printf("Resend sOrderSend OP_SELL , price=%f , sl=%f , tp=%f",Bid,losscut_normalize,profit_normalize); //オーダー修正 LimitStop_Set(ticket_no,OP_SELL,clrRed); }else { printf("Send Done. Ticket NO = %d",ticket_no); } } |
オーダー修正するリトライ部分は以下のような関数を作りました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
//+------------------------------------------------------------------+ //| エントリー中のポジションのリミット・ストップを変更 //| 引数 int in_ticket_no = Ticket no //| double my_sl = Stopp Loss //| double my_tp = Take Profit //+------------------------------------------------------------------+ // void LimitStop_Set( int in_ticket_no, int buysell_type , color mycolor) { int modify_resend_num; // 変更試行回数 bool modify_ret; // 変更判定 int errorcode; bool selbool; double limit_rate,stop_rate; int my_ticket_no; // オーダー中のチケット選択(チケットNo指定) selbool = OrderSelect(in_ticket_no, SELECT_BY_TICKET); if (buysell_type == OP_BUY){ limit_rate = OrderOpenPrice() + ( 10 * Point() * Profitrate); stop_rate = OrderOpenPrice() - ( 10 * Point() * SLrate); }else{ limit_rate = OrderOpenPrice() - ( 10 * Point() * Profitrate); stop_rate = OrderOpenPrice() + ( 10 * Point() * SLrate); } limit_rate = NormalizeDouble(limit_rate , int(MarketInfo(Symbol(),MODE_DIGITS))); // リミット価格 を正規化 stop_rate = NormalizeDouble(stop_rate , int(MarketInfo(Symbol(),MODE_DIGITS)) ); // ストップロス価格を正規化 my_ticket_no = OrderTicket(); for( modify_resend_num = 0; modify_resend_num < 30; modify_resend_num++ ) { modify_ret = OrderModify( my_ticket_no, // チケットNo OrderOpenPrice(), // 注文価格 stop_rate, // ストップロス価格 limit_rate, // リミット価格 OrderExpiration(), // 有効期限 mycolor // 色 ); printf("OrderModify ticket_no:%d , sl=%f , tp=%f",my_ticket_no , stop_rate,limit_rate); if ( modify_ret == false ) { // 注文変更拒否 Sleep(300); // 300msec待ち errorcode = GetLastError(); // エラーコード取得 printf( "[%d]Modify Error! error_code:%d ,detail:%s ", modify_resend_num+1, errorcode , ErrorDescription(errorcode)); } else { // 決済注文約定 Print("Done. Ticket NO=",in_ticket_no); break; } } } |
30回ほどリトライさせてます。やってみると分かるのですが、結構失敗します。
上記は、以下の方の処理を参考にさせて頂きました。
損切り、利益の価格が狭い時も発生
現在価格と比べて、損切りや、利益の金額設定が近すぎる時も、エラーコード130が発生します。ここで関係があるのが、ストップレベルです。
MQL4の公式ページに、これについて言及があります。
結構盲点だったかも。
ストップレベルの確認方法は通貨ごと
最小設定値は、取引所ごと、通貨毎に異なります。
MT4上で、該当通貨にて「右クリック」⇒「仕様」
ストップレベルは40と表示されています。これは、単位はポイントです。10分の1にすれば、pipsで表せます。
上記はXMTradingのUSDJPYのペアの表示。OrderSend時に損切り、利確最低ともに、4pipsは空けないといけないということが分かります。
選択通貨のストップレベルの取得
MarketInfo関数を使います。
1 |
int my_stop_level = MarketInfo(Symbol(), MODE_STOPLEVEL); |
小数点の桁数も重要
同じく小数点の桁数もかなり重要です。XMTradingのUSDJPYでは、3桁です。
ところが、普通にMQL4でdouble型で扱うと、小数点はdoubleの型の有効桁数で扱われます。桁数は15桁。
これも、桁数を揃えないと、リアルトレードでエラー130が発生します。
選択した通貨の桁数をチェックするには?
MarketInfoを使って、2番めの引数に、DIGITSを渡すと、チャート上の通貨の有効桁数が帰ってきます。
1 |
int ketasuu = MarketInfo(Symbol(),MODE_DIGITS); |
小数点の桁数を丸めるには?
NormalizeDouble関数を使います。1番めの引数は丸めたい数値、2番めは桁数です。
1 |
double my_normalize = NormalizeDouble(数値,int(MarketInfo(Symbol(),MODE_DIGITS))); |
それでもポジションが残る事もあり
これで解決かと思ったんですが、30回リトライしても、SL、TPが設定できないときがあります。その場合は、逆指値、指値、ともに0のままなので、オーダーが残ったままになります。
うーん、困ったね。みんなどうしてんだろ。。
多少スリープかませるかとか、色々実験中です。
ご質問はコメント欄からお願いします