//+------------------------------------------------------------------+
//| OnTesterInit_Sample.mq5 |
//| Copyright 2018, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property description "Sample EA with the OnTesterInit() handler,"
#property description "in which values and limitations of "
#property description "inputs during optimization are set"
input double lots=0. 1; // ロット単位のボリューム
input double kATR=3; // ATRのシグナルローソク足の長さ
input int ATRperiod=20; // ATR指標期間
input int holdbars=8; // ポジションのバー数
input int slippage=10; // 許容されるスリッページ
input bool revers=false; // シグナルを反転するかどうか
input ulong EXPERT_MAGIC=0; // EAのマジックナンバー
//--- ATR指標ハンドルを格納
int atr_handle;
//--- ここで最後のATR値とローソク足のの実体を保存する
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- 最適化開始時間を記憶する
datetime optimization_start;
//--- 最適化が終了した後にチャートに期間を表示する
string report;
//+------------------------------------------------------------------+
//| TesterInit 関数 |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//--- 最適化のための入力値を設定する
ParameterSetRange("lots",false,0.1,0,0,0);
ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0);
ParameterSetRange("ATRperiod",true,10,15,1,30);
ParameterSetRange("holdbars",true,5,3,1,15);
ParameterSetRange("slippage",false,10,0,0,0);
ParameterSetRange("revers",true,false,false,1,true);
ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0);
Print("Initial values and optimization parameter limitations are set");
//--- 最適化開始時間を記憶する
optimization_start=TimeLocal();
report=StringFormat("%s: optimization launched at %s",
__FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- メッセージをチャートと端末操作ログに表示する
Print(report);
Comment(report);
//---
}
//+------------------------------------------------------------------+
//| TesterDeinit 関数 |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//--- 最適化期間
string log_message=StringFormat("%s: optimization took %d seconds",
__FUNCTION__,TimeLocal()-optimization_start);
PrintFormat(log_message);
report=report+"\r\n"+log_message;
Comment(report);
}
//+------------------------------------------------------------------+
//| エキスパート初期化関数 |
//+------------------------------------------------------------------+
int OnInit()
{
//--- グローバル変数を初期化する
last_atr=0;
last_body=0;
//--- 正しいボリュームを設定する
double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
trade_lot=lots>min_lot?lots:min_lot;
//--- ATR指標ハンドルを作成する
atr_handle=iATR(_Symbol,_Period,ATRperiod);
if(atr_handle==INVALID_HANDLE)
{
PrintFormat("%s: failed to create iATR, error code %d",__FUNCTION__,GetLastError());
return(INIT_FAILED);
}
//--- EA初期化が成功した
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| エキスパートティック関数 |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 取引シグナル
static int signal=0; // +1は買いシグナル、 -1は売りシグナル
//--- 'holdbars'バーより前に開かれた以前のポジションを確認して決済する
ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- 新しいバーを確認する
if(isNewBar())
{
//--- シグナルの存在を確認する
signal=CheckSignal();
}
//--- ネットポジションが開かれている場合は、信号をスキップして終了する
if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
{
signal=0;
return; // NewTickイベントハンドラを終了し、新しいバーが現れる前に市場に入らない
}
//--- ヘッジ口座の場合、各ポジションは個別に保持され、決済される
if(signal!=0)
{
//--- 買いシグナル
if(signal>0)
{
PrintFormat("%s: Buy signal!Revers=%s",__FUNCTION__,string(revers));
if(Buy(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
//--- 売りシグナル
if(signal<0)
{
PrintFormat("%s: Sell signal!Revers=%s",__FUNCTION__,string(revers));
if(Sell(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
}
//--- OnTick関数の終わり
}
//+------------------------------------------------------------------+
//| 新しい取引シグナルを確認する |
//+------------------------------------------------------------------+
int CheckSignal()
{
//--- 0はシグナルが不在なことを意味する
int res=0;
//--- 終わりから2番目の完全なバーにATR値を取得する(バーのインデックスは2)
double atr_value[1];
if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
{
last_atr=atr_value[0];
//--- 最後に閉じたバーのデータをMqlRates型配列に取得する
MqlRates bar[1];
if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
{
//--- 最後の完全なバーでバー本体のサイズを計算する
last_body=bar[0].close-bar[0].open;
//--- 最後のバー(インデックス1)の本体が以前のATR値(インデックス2のバー上)を超える場合、取引シグナルが受信される
if(MathAbs(last_body)>kATR*last_atr)
res=last_body>0?1:-1; // 上昇ローソク足では正の値
}
else
PrintFormat("%s: Failed to receive the last bar!Error",__FUNCTION__,GetLastError());
}
else
PrintFormat("%s: Failed to receive ATR indicator value!Error",__FUNCTION__,GetLastError());
//--- 反転取引モードが有効な場合
res=revers?-res:res; // 必要に応じてシグナルを反転させる(1の代わりに-1を返し、逆も同様)
//--- 取引シグナル値を返す
return (res);
}
//+------------------------------------------------------------------+
//| 新しいバーが現れると'true' |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
{
static datetime bartime=0; // 現在のバーの開いた時刻を格納する
//--- ゼロバーの開いた時間を取得する
datetime currbar_time=iTime(_Symbol,_Period,0);
//--- 新しいバーが到着すると開いた時刻が変わる
if(bartime!=currbar_time)
{
bartime=currbar_time;
lastbar_timeopen=bartime;
//--- ログに新しいバーが開いている時間のデータを表示する
if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
{
//--- 新しいバーを開いた時間のメッセージを表示する
PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
StringSubstr(EnumToString(_Period),7),
TimeToString(TimeCurrent(),TIME_SECONDS));
//--- 最後のティックのデータを取得する
MqlTick last_tick;
if(!SymbolInfoTick(Symbol(),last_tick))
Print("SymbolInfoTick() failed, error = ",GetLastError());
//--- 最後のティックタイムをミリ秒まで表示する
PrintFormat("Last tick was at %s.%03d",
TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
}
//--- 新しいバーがある
return (true);
}
//--- 新しいバーがない
return (false);
}
//+------------------------------------------------------------------+
//| 成行価格で指定された量で買う |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- 成行価格で買う
return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| 成行価格で指定された量で売る |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- 成行価格で売る
return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| バー数によるポジション保留時間ごとにポジションを決済する |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0)
{
int total=PositionsTotal(); // ポジション数
//--- ポジションをすべて見る
for(int i=total-1; i>=0; i--)
{
//--- ポジションパラメータ
ulong position_ticket=PositionGetTicket(i); // ポジションチケット
string position_symbol=PositionGetString(POSITION_SYMBOL); // シンボル
ulong magic=PositionGetInteger(POSITION_MAGIC); // ポジションマジックナンバー
datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // ポジションの開かれた時刻
int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // ポジションがどれだけ前(バー数)に開かれたか
//--- マジックナンバーとシンボルが一致していてポジションがすでに長くある場合
if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
{
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小数点以下桁数
double volume=PositionGetDouble(POSITION_VOLUME); // ポジションボリューム
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // ポジションの種類
string str_type=StringSubstr(EnumToString(type),14);
StringToLower(str_type); // 正しいメッセージ書式設定のために小文字にする
PrintFormat("Close position #%d %s %s %.2f",
position_ticket,position_symbol,str_type,volume);
//--- 注文タイプを設定し、取引リクエストを送信する
if(type==POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
else
MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
}
}
}
//+------------------------------------------------------------------+
//| 取引リクエストを準備して送信する |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
{
//--- 構造体の宣言と初期化
MqlTradeRequest request={};
MqlTradeResult result={};
double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
if(type==ORDER_TYPE_BUY)
price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- リクエストパラメータ
request.action =TRADE_ACTION_DEAL; // 取引操作の種類
request.position =pos_ticket; // 決済する場合のポジションチケット
request.symbol =Symbol(); // シンボル
request.volume =volume; // ボリューム
request.type =type; // 注文の種類
request.price =price; // 取引価格
request.deviation=slip; // 価格からの許容偏差
request.magic =magicnumber; // 注文のマジックナンバー
//--- リクエストを送信する
if(!OrderSend(request,result))
{
//--- 失敗したらデータを表示する
PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
request.symbol,EnumToString(type),volume,request.price,GetLastError());
return (false);
}
//--- 操作の成功を知らせる
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
return (true);
}
|