豊嶋久道著Kindle版「EA実践プログラミング」ライブラリーの優れた点については既述の通りであるが、先日来、少し厄介な状況に直面してきた。トラブルの元はFX業者側にあるが、ライブラリーではそのような想定外の事態には対応していなかったのである。
LibEA4.mqhを利用したEA例で説明してみよう。仕掛けシグナルをsig_entry、手仕舞いシグナルをsig_exit、ストップロスをSLpipsとすると、そのEAは次のように簡便なものとなる。(本体部分のみ)
//ティック時実行関数
void OnTick()
{
//損切りオーダーの付加
if(MyOrderStopLoss() == 0) MyOrderModify(0, MyOrderShiftPrice(-SLpips), 0);
//成行売買
MyOrderSendMarket(sig_entry, sig_exit, Lots);
}
このEAを4時間足以上の時間足で稼働させると、損切り決済に遭遇することが多くなる。その場合、仕掛けシグナルが有効であると、再エントリーとなり、ボラティリティが高い場合、二度三度と損切りを繰り返してしまうことがある。そこで同一時間足(バー)内では、注文を繰り返さないよう、MyMarketSendMarket()に次の条件を付加してみた。
//成行売買
static datetime time = Time[0];
if(Time[0] != time){
MyOrderSendMarket(sig_filter, sig_exit, Lots);
time = Time[0];}
これで一安心と就寝したものの、翌朝チャートを開いてみると、6時に仕掛けシグナルが出ているのにエントリーがないのである。不審に思って、ターミナルのエキスパートタブを開いてみると、休日でもないのに132 Market is closedなるエラーメッセージが出ている。
エラーとなった原因は二つあった。
① 海外業者の中には、平日の日付変更時(夏時間では日本時間の午前6時)には、ロールオーバーのため1~2分間トレードできない仕様にしているところがある。当然この間は休日同様の扱いとなり、オーダーが出されても市場は休場(Market is closed)とのエラーとなってしまう。豪州系業者にはこのような仕様のところが多く,Pepperstone社では、その警告を正式に発している。https://pepperstone.com/en/support/about-trading/market-opening-hours
② MyOrderSendMarket()に条件を付加したこと
新ライブラリーでは、エラーが発生した場合にはエラーの原因が消滅して正常に復帰した際には、自動的に再注文が出される仕組みになっている。ところが、この関数はvoidで定義されているため、たとえエラーであっても、「同じ時間足では再注文を出さない」との付加条件が働いて、サーバーが正常に復帰しても同一時間足内(同一バー内)では再注文が出せなくなっていた。
解決策
① voidで定義されていたMyOrderSendMarket()をbool関数に変更し、正常に発注が終了した時に限り、「同じ時間足内では再注文を出さない」ことにした。
//シグナルによる成行注文
bool MyOrderSendMarket(int sig_entry, int sig_exit, double lots, int pos_id=0)
{
//ポジション決済
MyOrderCloseMarket(sig_entry, sig_exit, pos_id);
//買い注文
if(sig_entry > 0) MyOrderSend(OP_BUY, lots, 0, pos_id);
//売り注文
if(sig_entry < 0) MyOrderSend(OP_SELL, lots, 0, pos_id);
//エラーの場合
int err = GetLastError();
if(err > 0)
{
return false;
}
return true; //正常終了
}
② MyOrderSend()を変更
MyOrderSendMarket()では、エラーの有無は毎チックで行われる。そのため、一旦エラーが発生すると、この関数の裏側で働くMyOrderSend()もエラーが解消するまで毎チックで再注文が繰り返されることになる。事例のように、毎朝1~2分間EAが稼働できない状況では、その間夥しい数の再注文が出し続けられる。4時間足以上の長い時間足でトレードする場合では、10秒間に一度再注文が出されれば十分なので、MyOrderSend()関数を次のように変更した。
//注文の送信
bool MyOrderSend(int type, double lots, double price=0, int pos_id=0)
{
color ArrowColor[6] = {clrBlue, clrRed, clrBlue, clrRed, clrBlue, clrRed}; //矢印の色データ
CheckPosID(pos_id); //ポジション番号のチェック
if(MyOrderType(pos_id) != OP_NONE) return true; //注文済み
price = NormalizeDouble(price, _Digits); //価格の正規化
//RefreshRates();
if(type == OP_BUY) price = Ask; //成行注文の買値
if(type == OP_SELL) price = Bid; //成行注文の売値
//注文送信
while(true)
{
if(IsTradeAllowed() == true)
{
RefreshRates();
int ret = OrderSend(_Symbol, type, lots, price, Slippage, 0, 0,
IntegerToString(MagicNumber[pos_id]),
MagicNumber[pos_id], 0, ArrowColor[type]);
if(ret != -1) return true;
int err = GetLastError();
Print("MyOrderSend : ", err, " " , ErrorDescription(err));
//return false;
}
//return true; //正常終了
Sleep (10000);
}
}
③ 結果検証
本日の朝6時にもたまたま買いシグナルが出ており、EAは正常に作動したようで、シグナル通りのポジションが形成されていた。ターミナルのエキスパートには、企図した通り132 Market is closedが10秒間隔で6回出ていて、6:01には正常発注がなされている。このことからサーバーは、ロールオーバーのため毎朝1分間注文を受け付けないことも確認された。利用している業者は、Pepperstone同様豪州系である。
結果として、著者開発のライブラリーに変更を加えてしまったので、著者に迷惑が掛からぬよう変更後のライブラリーは別名にして使用している。