- iBarShift関数の働き・役割
- iBarShift関数の引数について
- iBarShift関数の戻り値について
- iBarShift関数を使ったサンプルコード
- サンプルコードの解説その1-グローバル領域での定義-
- サンプルコードの解説その2-OnTesterInit関数部分-
- サンプルコードの解説その3-OnTesterDeinit関数部分-
- サンプルコードの解説その4-OnInit関数部分-
- サンプルコードの解説その5-CheckSignal関数部分-
- サンプルコードの解説その6-isNewBar関数部分-
- サンプルコードの解説その7-Buy関数とSell関数部分-
- サンプルコードの解説その8-ClosePositionsByBars関数部分-
- サンプルコードの解説その9-MarketOrder関数部分-
iBarShift関数の働き・役割
iBarShift関数は、指定された時間に基づいて過去のバーのインデックス(通し番号)を検索するために使用されます。このインデックスは、指定した金融商品の特定の時間枠のバーの位置を示すものであり、時刻に基づいた分析や条件設定を行う際に便利です。
iBarShift関数を用いることで、例えば「特定の日付や時間に最も近いバー」を特定し、そのバーに対する価格やインジケータデータを参照することができます。金融商品の価格変動の過去データを取得し、システム的な分析やエキスパートアドバイザーの条件設定に役立てる際に効果的です。
iBarShift関数の引数について
iBarShift関数の引数構成は以下の通りです。
書式
int iBarShift(
const string symbol, // 銘柄
ENUM_TIMEFRAMES timeframe, // 期間
datetime time, // 時間
bool exact=false // モード
);
引数詳細
symbol
第1引数は銘柄名を指定します。これは価格データを検索する対象の金融商品の名称を表し、例えば「EURUSD」や「GBPUSD」などのように指定します。NULLを指定すると、現在のチャートで使用されているシンボル(特定の文字や記号)が適用されます。
timeframe
第2引数には時間枠(タイムフレーム)を指定します。これはデータを取得したい時間の単位を表し、ENUM_TIMEFRAMES列挙体から選択した値を使用します。例えばPERIOD_H1は1時間足、PERIOD_M1は1分足を意味します。また、PERIOD_CURRENTを指定すると、現在のチャートの時間枠が使用されます。
time
第3引数には検索する基準となる時刻を指定します。この値はdateTime型で、特定の日時を示します。指定された日時に最も近いバーを検索し、そのバーのインデックス(通し番号)を返します。
exact
第4引数はオプションで、バー検索の厳密さを設定するモードです。このモードには以下の2つの設定があります。
- false(初期値)を指定した場合、iBarShift関数は指定された時刻に最も近いバーを検索し、そのインデックスを返します。もし履歴が足りずに指定された時刻のバーが見つからない場合、-1を返します。
- trueを指定した場合は、指定された時刻に完全に一致するバーがある場合のみインデックスを返し、見つからない場合は-1を返します。
iBarShift関数の戻り値について
iBarShift関数は、指定された時刻に対応するバーのインデックス(通し番号)を整数値で返します。このインデックスは、過去データのバーにアクセスする際に使用できる重要な情報です。戻り値は以下の通りです。
- 指定された時刻に対応するバーが見つかった場合、そのバーのインデックスが整数で返されます。このインデックスは0から始まる値で、0は直近のバーを表し、1はその次に古いバーといったように過去へさかのぼるバーの位置を表します。
- exact引数がfalseの場合、指定された時刻にぴったり一致するバーが見つからなくても、最も近い(指定された時刻よりも過去で最も新しい)バーのインデックスが返されます。ただし、履歴が不足していてそのようなバーが存在しない場合は-1が返されます。
- exact引数がtrueの場合、指定された時刻にぴったり一致するバーがない場合、関数は-1を返します。
iBarShift関数を使ったサンプルコード
//--- EAの設定パラメータ
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の識別用のマジックナンバー
//--- 変数宣言
int atr_handle; // ATR指標のハンドルを格納する変数
double last_atr, last_body; // 最後に取得したATR値とローソク足の実体サイズを格納
datetime lastbar_timeopen; // 最後のバーが開いた時間
double trade_lot; // 実行する取引のロット数を格納
datetime optimization_start; // 最適化開始時刻を記録
string report; // 結果を格納する文字列
//+------------------------------------------------------------------+
//| TesterInit関数 |
//| 最適化開始前に呼び出され、EAパラメータの初期化を行う |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//--- 最適化パラメータの範囲を設定
ParameterSetRange("lots", false, 0.1, 0, 0, 0); // 取引量の最小値、最大値、初期値の設定
ParameterSetRange("kATR", true, 3.0, 1.0, 0.3, 7.0); // ATR倍率の設定範囲
ParameterSetRange("ATRperiod", true, 10, 15, 1, 30); // ATR計算期間の設定範囲
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); // EA識別用マジックナンバーの設定
//--- 初期化完了メッセージを出力
Print("初期化と最適化パラメータが設定されました");
//--- 最適化開始時刻を記録
optimization_start = TimeLocal();
//--- チャートおよびログに最適化開始のメッセージを表示
report = StringFormat("%s: 最適化は %s に開始されました", __FUNCTION__, TimeToString(TimeLocal(), TIME_MINUTES|TIME_SECONDS));
Print(report); // ログ出力
Comment(report); // チャート上に表示
}
//+------------------------------------------------------------------+
//| TesterDeinit関数 |
//| 最適化終了後に呼び出され、実行時間を出力する |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//--- 実行時間を計算してログ出力
string log_message = StringFormat("%s: 最適化にかかった時間は %d 秒", __FUNCTION__, TimeLocal() - optimization_start);
PrintFormat(log_message); // 実行時間をログに出力
//--- 結果をレポートに追加し、画面表示を更新
report = report + "\r\n" + log_message;
Comment(report);
}
//+------------------------------------------------------------------+
//| EA初期化関数 |
//| EAの開始時に呼び出され、変数やATR指標の初期化を行う |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 初期値設定
last_atr = 0; // ATR値の初期化
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); // ATRのハンドルを作成
if (atr_handle == INVALID_HANDLE) // ハンドル取得に失敗した場合
{
// ATRの作成に失敗した場合、エラーメッセージを出力
PrintFormat("%s: iATRの作成に失敗しました。エラーコード: %d", __FUNCTION__, GetLastError());
return (INIT_FAILED); // 初期化失敗を返す
}
//--- EAの初期化が成功したことを示す戻り値
return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 新しいティックが到着したときに呼び出される |
//| 各バーごとに取引シグナルを確認し、必要ならばポジションを取る |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 取引シグナルを保持する変数(0はシグナルなし、1は買い、-1は売り)
static int signal = 0;
//--- 保持バー数に基づいて古いポジションを決済
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; // 新しいティックイベントを終了
}
//--- ヘッジモードの場合、シグナルに基づいてポジションを取る
if (signal != 0)
{
//--- 買いシグナルの場合
if (signal > 0)
{
PrintFormat("%s: 買いシグナル!Revers = %s", __FUNCTION__, string(revers));
if (Buy(trade_lot, slippage, EXPERT_MAGIC)) signal = 0; // 買い注文が成功したらシグナルをリセット
}
//--- 売りシグナルの場合
else if (signal < 0)
{
PrintFormat("%s: 売りシグナル!Revers = %s", __FUNCTION__, string(revers));
if (Sell(trade_lot, slippage, EXPERT_MAGIC)) signal = 0; // 売り注文が成功したらシグナルをリセット
}
}
}
//+------------------------------------------------------------------+
//| 取引シグナルを生成する関数 |
//| ATRを使用してシグナルを確認し、シグナルがあれば1(買い)または-1(売り)を返す |
//+------------------------------------------------------------------+
int CheckSignal()
{
int res = 0; // 初期値(シグナルがない状態)
//--- ATR指標の値を取得
double atr_value[1]; // ATR値を格納する配列
if (CopyBuffer(atr_handle, 0, 2, 1, atr_value) != -1) // ATR指標から値を取得
{
last_atr = atr_value[0]; // 最新のATR値を変数に格納
//--- 最新のバーのデータを取得
MqlRates bar[1]; // ローソク足のデータを格納する変数
if (CopyRates(_Symbol, _Period, 1, 1, bar) != -1) // 最新のローソク足データを取得
{
last_body = bar[0].close - bar[0].open; // 実体サイズを計算(終値-始値)
//--- 実体サイズがATRの指定倍率を超えた場合、シグナルを設定
if (MathAbs(last_body) > kATR * last_atr) // 実体が一定以上の場合
res = last_body > 0 ? 1 : -1; // 上昇ローソクなら買いシグナル、下降なら売りシグナル
}
else // ローソク足データの取得に失敗した場合
{
PrintFormat("%s: 最新のバー取得に失敗!エラー: %d", __FUNCTION__, GetLastError());
}
}
else // ATR指標値の取得に失敗した場合
{
PrintFormat("%s: ATR指標値取得に失敗!エラー: %d", __FUNCTION__, GetLastError());
}
//--- シグナル反転の設定がされている場合、シグナルを反転
res = revers ? -res : res; // シグナルを反転
return (res); // シグナル値を返す
}
//+------------------------------------------------------------------+
//| 新しいバーが生成されたか確認する関数 |
//| 新しいバーが生成された場合にtrueを返し、そうでない場合はfalseを返す|
//+------------------------------------------------------------------+
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: %s %s に新しいバーが生成されました", __FUNCTION__, _Symbol, StringSubstr(EnumToString(_Period), 7));
//--- 最後のティックデータを取得
MqlTick last_tick;
if (!SymbolInfoTick(Symbol(), last_tick)) // ティック情報の取得に失敗した場合
Print("SymbolInfoTick() に失敗しました。エラーコード:", GetLastError());
//--- 最後のティックの時刻をミリ秒まで表示
PrintFormat("最終ティックの時刻: %s.%03d", TimeToString(last_tick.time, TIME_SECONDS), last_tick.time_msc % 1000);
}
return (true); // 新しいバーが生成された場合はtrueを返す
}
return (false); // 新しいバーが生成されていない場合はfalseを返す
}
//+------------------------------------------------------------------+
//| 成行価格で指定の量を買う関数 |
//| 指定されたボリュームで成行買い注文を実行し、成功でtrueを返す |
//+------------------------------------------------------------------+
bool Buy(double volume, ulong deviation = 10, ulong magicnumber = 0)
{
//--- 買い注文を行う
return (MarketOrder(ORDER_TYPE_BUY, volume, deviation, magicnumber));
}
//+------------------------------------------------------------------+
//| 成行価格で指定の量を売る関数 |
//| 指定されたボリュームで成行売り注文を実行し、成功でtrueを返す |
//+------------------------------------------------------------------+
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("ポジション #%I64u %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);
}
}
}
//+------------------------------------------------------------------+
//| 成行注文を準備して送信する関数 |
//| 指定された注文内容で成行注文を送信し、成功でtrueを返す |
//+------------------------------------------------------------------+
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); // 成行注文の価格を取得
//--- 注文タイプが買いの場合は買い価格(ASK)を設定
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; // マジックナンバーを設定
//--- 注文を送信し、成功すればtrueを返す
if (!OrderSend(request, result))
{
// 注文送信に失敗した場合、エラーメッセージを出力
PrintFormat("OrderSend %s %s %.2f at %.5f エラーコード: %d", request.symbol, EnumToString(type), volume, request.price, GetLastError());
return (false); // 注文が失敗した場合にfalseを返す
}
//--- 注文が成功した場合の結果を表示
PrintFormat("リクエストコード = %u、取引 = %I64u、注文番号 = %I64u", result.retcode, result.deal, result.order);
return (true); // 成功時にtrueを返す
}
このコードは、エキスパートアドバイザー(EA)が最適化プロセスを行う際に、特定の初期化とパラメータ設定を実施し、ATR(Average True Range)指標を使って取引シグナルを判断します。EAが起動されると、最初にOnTesterInit関数が呼ばれて最適化のためのパラメータ範囲が設定されます。その後、OnInit関数でATR指標を初期化し、最適化実行時間などの初期データをセットします。
次に、新しいティックが到着するたびにOnTick関数が呼ばれ、ATR指標を基にして取引シグナルを生成します。ATRを用いることで、指定された条件を満たすローソク足の変動が検出されたときに売買シグナルを発生させます。例えば、一定のATR倍数を超えるローソク足の実体が形成された場合、そのローソク足の動きに応じて「買い」もしくは「売り」のシグナルを生成します。また、指定されたバー数(holdbars)を超えたポジションがあれば自動的に決済されます。
シグナルが生成され、現在のポジションが空の場合、取引が実行されます。取引実行の際には、設定されたスリッページやEA識別用のマジックナンバーが適用され、取引の正確な管理が行われます。最適化プロセスが終了すると、OnTesterDeinit関数で最適化にかかった時間が表示され、レポートが画面に表示されます。
サンプルコードの解説その1-グローバル領域での定義-
//--- EAの設定パラメータ
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の識別用のマジックナンバー
//--- 変数宣言
int atr_handle; // ATR指標のハンドルを格納する変数
double last_atr, last_body; // 最後に取得したATR値とローソク足の実体サイズを格納
datetime lastbar_timeopen; // 最後のバーが開いた時間
double trade_lot; // 実行する取引のロット数を格納
datetime optimization_start; // 最適化開始時刻を記録
string report; // 結果を格納する文字列
このコードでは、エキスパートアドバイザー(EA)の初期設定パラメータと、取引に必要な変数が宣言されています。これらの設定はEAの挙動に直接影響を与え、最適化プロセスや取引の詳細を制御するために使用されます。それぞれのパラメータと変数について説明します。
EAの設定パラメータ
- lotsは取引量を設定するためのパラメータで、単位はロットです。たとえば0.1ロットの場合、通常の取引量の10分の1を意味します。
- kATRはATR指標の値に基づくシグナル判定で使用される倍率です。この倍率を変えることで、シグナルの発生タイミングや感度を調整できます。
- ATRperiodはATR(Average True Range)指標の計算に用いる期間(ローソク足の数)を指定します。期間を長くすると、相場の平均変動幅を広い視点で捉えることができます。
- holdbarsは保持するバー数で、ポジションをどのくらいの期間保持するかを指定します。指定バー数を超えたポジションは決済されます。
- slippageは許容されるスリッページ(価格のずれ)の値です。取引時に、指定した価格からの許容範囲を超えると注文は実行されません。
- reversはシグナルを反転するかどうかを決定するフラグです。trueに設定すると、シグナルが逆の意味を持ちます。
- EXPERT_MAGICはEAごとに固有のマジックナンバーを設定するためのパラメータで、このEAが開いたポジションを識別するために使用されます。
変数宣言
- atr_handleはATR指標の計算を行うためのハンドルです。EAはこのハンドルを用いてATRの値を取得します。
- last_atrは最新のATR値を格納するための変数です。取引シグナルを生成する際に、現在のATR値を参考にします。
- last_bodyは最新のローソク足の実体サイズ(始値と終値の差)を保持します。この値も取引シグナルの生成に利用されます。
- lastbar_timeopenは最新のバーが開いた時間を記録します。この時間を基に新しいバーが生成されたかどうかを判断します。
- trade_lotは実行する取引のロット数を保持する変数です。EAはこの変数を用いて、取引の際に実際に使用するロット数を決定します。
- optimization_startは最適化が開始された時刻を記録します。EAの最適化時間を測るために使用され、最適化のログ表示やレポート出力に役立ちます。
- reportは結果やメッセージを格納するための文字列変数です。最適化や取引実行時の結果がこの変数に格納され、レポートとして出力されます。
これらの設定パラメータと変数により、EAの柔軟な運用と最適化が可能になります。ATR指標を基にしたシグナル生成や、スリッページ、マジックナンバーといった要素を組み合わせることで、EAの精度と管理性を高められます。
サンプルコードの解説その2-OnTesterInit関数部分-
//+------------------------------------------------------------------+
//| TesterInit関数 |
//| 最適化開始前に呼び出され、EAパラメータの初期化を行う |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//--- 最適化パラメータの範囲を設定
ParameterSetRange("lots", false, 0.1, 0, 0, 0); // 取引量の最小値、最大値、初期値の設定
ParameterSetRange("kATR", true, 3.0, 1.0, 0.3, 7.0); // ATR倍率の設定範囲
ParameterSetRange("ATRperiod", true, 10, 15, 1, 30); // ATR計算期間の設定範囲
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); // EA識別用マジックナンバーの設定
//--- 初期化完了メッセージを出力
Print("初期化と最適化パラメータが設定されました");
//--- 最適化開始時刻を記録
optimization_start = TimeLocal();
//--- チャートおよびログに最適化開始のメッセージを表示
report = StringFormat("%s: 最適化は %s に開始されました", __FUNCTION__, TimeToString(TimeLocal(), TIME_MINUTES|TIME_SECONDS));
Print(report); // ログ出力
Comment(report); // チャート上に表示
}
このコードは、ストラテジーテスターでEAの最適化が開始される前に実行されるOnTesterInit関数を定義しています。この関数では、最適化で使用するパラメータの範囲や初期値を設定し、EAの最適化開始時刻やメッセージの表示を行います。
コードの詳細解説
- ParameterSetRange関数を用いて、最適化する各パラメータの設定範囲と初期値を指定しています。
- lotsでは取引量を設定します。最適化対象から除外され、初期値として0.1が指定されています。
- kATRではATR指標を利用したシグナル生成の倍率を設定します。最適化対象であり、初期値3.0、最小1.0、最大7.0、ステップ0.3で設定されています。
- ATRperiodではATRの計算に使用する期間(ローソク足の数)を指定します。最適化対象で、初期値10、範囲は15から30、ステップ1で設定されています。
- holdbarsはポジションを保持するバー数を設定します。最適化対象で、初期値5、範囲は3から15、ステップ1です。
- slippageは許容スリッページを設定し、取引実行時の価格のずれを防止する基準を定めています。このパラメータは最適化対象外で、値は10です。
- reversはシグナル反転のフラグです。最適化対象で、初期値と最小値はfalse、最大値true、ステップ1で設定されています。
- EXPERT_MAGICはEAのマジックナンバーです。EAが開いたポジションを識別するために使用され、最適化対象外で値は123456です。
- Print関数を使い、EAの初期化と最適化パラメータの設定完了をログに出力しています。
- TimeLocal関数を利用し、最適化の開始時刻をoptimization_start変数に記録します。最適化プロセスが完了した際にこの開始時刻を参照し、所要時間を計算するのに利用します。
- StringFormat関数を使用し、最適化開始メッセージを作成してreport変数に格納しています。このメッセージは、ログ(Print関数)とチャート上(Comment関数)に表示され、EAの最適化が開始されたことを視覚的に確認するために役立ちます。
このように、OnTesterInit関数ではEAの動作に関わる重要なパラメータの最適化範囲を設定し、開始時刻やメッセージの表示など、最適化準備に必要な処理をまとめて行っています。
サンプルコードの解説その3-OnTesterDeinit関数部分-
//+------------------------------------------------------------------+
//| TesterDeinit関数 |
//| 最適化終了後に呼び出され、実行時間を出力する |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//--- 実行時間を計算してログ出力
string log_message = StringFormat("%s: 最適化にかかった時間は %d 秒", __FUNCTION__, TimeLocal() - optimization_start);
PrintFormat(log_message); // 実行時間をログに出力
//--- 結果をレポートに追加し、画面表示を更新
report = report + "\r\n" + log_message;
Comment(report);
}
このコードは、ストラテジーテスターでの最適化が終了した際に呼び出されるOnTesterDeinit関数を定義しています。この関数では、最適化の実行時間を計算し、ログや画面に出力します。
コードの詳細解説
- 最初に、最適化にかかった実行時間を計算しています。optimization_start変数に記録されている最適化開始時刻を基に、最適化が完了した現在の時刻(TimeLocal)との差分を取り、秒単位の経過時間を計算します。
- 次に、StringFormat関数を使用して、実行時間を含むメッセージ(log_message)を作成します。このメッセージは関数名(__FUNCTION__)と経過時間を組み合わせた形式で、最適化プロセスの所要時間を表します。
- PrintFormat関数を使用して、生成したメッセージをエキスパートログに出力します。これにより、実行時間が記録され、後で確認することが可能です。
- report変数に実行時間のメッセージを追加し、レポート全体をComment関数で画面に表示します。これにより、EAの最適化結果を視覚的に確認できるようになります。
このように、OnTesterDeinit関数では、最適化プロセスの実行時間を計測し、EAのログと画面表示に反映する処理を行っています。
サンプルコードの解説その4-OnInit関数部分-
//+------------------------------------------------------------------+
//| EA初期化関数 |
//| EAの開始時に呼び出され、変数やATR指標の初期化を行う |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 初期値設定
last_atr = 0; // ATR値の初期化
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); // ATRのハンドルを作成
if (atr_handle == INVALID_HANDLE) // ハンドル取得に失敗した場合
{
// ATRの作成に失敗した場合、エラーメッセージを出力
PrintFormat("%s: iATRの作成に失敗しました。エラーコード: %d", __FUNCTION__, GetLastError());
return (INIT_FAILED); // 初期化失敗を返す
}
//--- EAの初期化が成功したことを示す戻り値
return (INIT_SUCCEEDED);
}
このコードは、EAが開始される際に呼び出されるOnInit関数を定義しており、EAに必要な初期化処理を行います。この関数では、変数やATR指標の初期化を行い、EAの正常な動作準備を整えます。
コードの詳細解説
- まず、ATR指標に関連する変数の初期化を行います。last_atr変数にATR値の初期値0を、last_body変数にローソク足の実体サイズの初期値0を設定します。これにより、EAが過去の取引データを使って新たな取引シグナルを生成する際の準備が整います。
- 次に、EAで取引に使用するロット数を設定します。最小ロットサイズはSymbolInfoDouble関数を使って取得し、設定したロット数(lots)がこの最小ロットよりも大きければ指定ロットを採用し、小さい場合には最小ロットサイズを使用します。これにより、取引量が設定される際に取引所の最小取引量を満たすように調整されます。
- その後、iATR関数を使用してATR指標のハンドル(atr_handle)を作成します。ATRはボラティリティ指標で、シグナルを生成する際に使用されます。ここでは、シンボル(_Symbol)、時間足(_Period)、ATRの計算期間(ATRperiod)を指定してATRのハンドルを生成します。
- ハンドルの作成に失敗した場合は、エラーメッセージをログに出力し、関数はINIT_FAILEDを返して初期化が失敗したことを示します。
- ATRのハンドルが正常に作成され、その他の初期化も問題なく完了した場合、関数はINIT_SUCCEEDEDを返して初期化が成功したことを示します。
このように、OnInit関数はEAが必要とする各種の設定と初期化を行い、EAの正常な稼働に必要な環境を整えています。
サンプルコードの解説その5-CheckSignal関数部分-
//+------------------------------------------------------------------+
//| 取引シグナルを生成する関数 |
//| ATRを使用してシグナルを確認し、シグナルがあれば1(買い)または-1(売り)を返す |
//+------------------------------------------------------------------+
int CheckSignal()
{
int res = 0; // 初期値(シグナルがない状態)
//--- ATR指標の値を取得
double atr_value[1]; // ATR値を格納する配列
if (CopyBuffer(atr_handle, 0, 2, 1, atr_value) != -1) // ATR指標から値を取得
{
last_atr = atr_value[0]; // 最新のATR値を変数に格納
//--- 最新のバーのデータを取得
MqlRates bar[1]; // ローソク足のデータを格納する変数
if (CopyRates(_Symbol, _Period, 1, 1, bar) != -1) // 最新のローソク足データを取得
{
last_body = bar[0].close - bar[0].open; // 実体サイズを計算(終値-始値)
//--- 実体サイズがATRの指定倍率を超えた場合、シグナルを設定
if (MathAbs(last_body) > kATR * last_atr) // 実体が一定以上の場合
res = last_body > 0 ? 1 : -1; // 上昇ローソクなら買いシグナル、下降なら売りシグナル
}
else // ローソク足データの取得に失敗した場合
{
PrintFormat("%s: 最新のバー取得に失敗!エラー: %d", __FUNCTION__, GetLastError());
}
}
else // ATR指標値の取得に失敗した場合
{
PrintFormat("%s: ATR指標値取得に失敗!エラー: %d", __FUNCTION__, GetLastError());
}
//--- シグナル反転の設定がされている場合、シグナルを反転
res = revers ? -res : res; // シグナルを反転
return (res); // シグナル値を返す
}
このコードは、取引シグナルを生成するためのCheckSignal関数を定義しています。ATR(Average True Range)指標を基に、ローソク足の変動が一定の基準を超えた場合にシグナルを生成します。買いシグナルの場合は1、売りシグナルの場合は-1を返し、シグナルがない場合は0を返します。
コードの詳細解説
- 最初にシグナルの初期値として0を設定します。この値はシグナルがない状態を表します。
- 次に、ATR指標の最新値を取得します。atr_valueという配列にATRの最新値を格納するため、CopyBuffer関数を使用してatr_handleからATR値を取得します。取得が成功すると、ATR値をlast_atr変数に格納します。
- 続いて、最新のローソク足データを取得します。MqlRates構造体型のbar配列にローソク足の情報を保存し、CopyRates関数を使用して最新のローソク足データを取得します。取得に成功すると、ローソク足の実体サイズ(始値と終値の差)を計算し、last_body変数に格納します。
- 実体サイズがATR値の指定倍率(kATR)を超えた場合、取引シグナルが生成されます。ここで、MathAbs関数を使ってlast_bodyの絶対値を計算しています。この絶対値がATR値の指定倍率を超えるかどうかを判断し、条件を満たす場合のみ取引シグナルが発生します。
- 三項演算子を使用して、シグナルの方向を設定します。last_bodyが正の値(上昇ローソク)であれば買いシグナルとして1を、負の値(下降ローソク)であれば売りシグナルとして-1をresに格納します。
- ATR値またはローソク足データの取得に失敗した場合は、エラーメッセージを出力します。このとき、PrintFormat関数を使用して、関数名( __FUNCTION__)とエラーコードを含むメッセージをフォーマットし、ログに出力します。 __FUNCTION__は現在の関数名を文字列として返す特殊な識別子で、デバッグ時にどの関数でエラーが発生したかを確認するのに便利です。GetLastError関数を使用して最後のエラーコードを取得し、その内容をログ出力に含めることで、エラーの詳細な原因を把握できます。
- 最後に、シグナル反転の設定が有効な場合、シグナルの符号を反転します。reversがtrueであれば、resに格納されたシグナルの符号が反転し、買いシグナルが売りに、売りシグナルが買いに変わります。
- シグナル値がresに格納された状態で関数の処理を終了し、最終的なシグナル値を返します。
サンプルコードの解説その6-isNewBar関数部分-
//+------------------------------------------------------------------+
//| 新しいバーが生成されたか確認する関数 |
//| 新しいバーが生成された場合にtrueを返し、そうでない場合はfalseを返す|
//+------------------------------------------------------------------+
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: %s %s に新しいバーが生成されました", __FUNCTION__, _Symbol, StringSubstr(EnumToString(_Period), 7));
//--- 最後のティックデータを取得
MqlTick last_tick;
if (!SymbolInfoTick(Symbol(), last_tick)) // ティック情報の取得に失敗した場合
Print("SymbolInfoTick() に失敗しました。エラーコード:", GetLastError());
//--- 最後のティックの時刻をミリ秒まで表示
PrintFormat("最終ティックの時刻: %s.%03d", TimeToString(last_tick.time, TIME_SECONDS), last_tick.time_msc % 1000);
}
return (true); // 新しいバーが生成された場合はtrueを返す
}
return (false); // 新しいバーが生成されていない場合はfalseを返す
}
isNewBar関数の記述解説その1
//+------------------------------------------------------------------+
//| 新しいバーが生成されたか確認する関数 |
//| 新しいバーが生成された場合にtrueを返し、そうでない場合はfalseを返す|
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log = true)
{
static datetime bartime = 0; // 現在のバーの開始時刻を保持する変数
//--- 現在のバー(ゼロバー)の開始時刻を取得
datetime currbar_time = iTime(_Symbol, _Period, 0);
isNewBar関数は、新しいバーが生成されたかどうかを確認し、生成された場合にtrueを、そうでない場合にfalseを返します。この判定により、エキスパートアドバイザー(EA)がティックごとではなく、新しいバーが形成されたタイミングで特定の処理を行うことが可能になります。
引数の解説
- print_log
関数の引数であるprint_logは、ログ出力を行うかどうかを制御します。この引数がtrueの場合、新しいバーが生成された際に詳細なログが出力されます。falseに設定すると、ログ出力はスキップされ、バーの生成確認のみが行われます。デフォルト値としてtrueが指定されているため、引数を指定しない場合でもログ出力が有効な状態になります。
変数の解説
- bartime
static指定子を持つdateTime型のbartimeは、関数呼び出しの間も値が保持される変数です。この変数には「前回取得したバーの開始時刻」が記録され、現在のバーと前回のバーの時刻を比較することで、新しいバーが生成されたかを判定します。初期値は0に設定されています。
処理の解説
関数内で、iTime関数を使って現在のバー(ゼロバー)の開始時刻を取得し、dateTime型のcurrbar_timeに格納しています。この時刻が前回のバー時刻と異なる場合、新しいバーが生成されたと判定します。
isNewBar関数の記述解説その2
//--- 以前のバーと異なる時刻なら新しいバーが生成されたと判断
if (bartime != currbar_time)
{
bartime = currbar_time; // 現在のバー開始時刻を更新
lastbar_timeopen = bartime; // 最後のバー開始時刻を保存
この部分のコードでは、現在のバーが前回のバーと異なる時刻であれば「新しいバーが生成された」と判断します。bartimeとcurrbar_timeを比較することで、この判定を行っています。
処理の詳細
- 新しいバーの生成判定
if文で、前回のバーの開始時刻(bartime)と現在のバーの開始時刻(currbar_time)を比較しています。もし異なる時刻であれば、新しいバーが生成されたと判断され、if文の内部の処理が実行されます。 - bartimeの更新
新しいバーが生成された場合、bartimeを現在のバー開始時刻であるcurrbar_timeに更新します。この処理により、次回以降の関数呼び出し時には新しいバーの開始時刻が基準として使用され、正確にバー生成を検出できるようになります。 - lastbar_timeopenの更新
最新のバーの開始時刻をlastbar_timeopen変数に格納します。この変数は他の関数やプロセスでも新しいバーの時刻を利用できるようにするためのもので、新しいバーが生成されたタイミングで更新されます。
これらの処理により、EAは新しいバーが形成されたタイミングを認識し、そのタイミングで取引戦略のロジックを実行することが可能になります。
isNewBar関数の記述解説その3
//--- 新しいバーが生成されたことをログに表示(最適化またはテスト中でない場合)
if (print_log && !(MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_TESTER)))
{
PrintFormat("%s: %s %s に新しいバーが生成されました",
__FUNCTION__, _Symbol, StringSubstr(EnumToString(_Period), 7));
このコードでは、新しいバーが生成されたときにその情報をログに出力しています。最適化またはテスト中でない場合のみ、ログが表示されます。この処理によって、EAの動作状況を確認しやすくなり、開発やデバッグに役立ちます。
条件文の詳細と日本語和訳
if (print_log && !(MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_TESTER)))
この条件文は、「print_logがtrueであり、かつ現在が最適化中またはテスト中でない場合」にのみログを出力するためのものです。日本語に和訳すると、以下のようになります。
「print_logがtrueであり、かつ最適化またはテスト中ではない場合」
使用されている関数の解説
- MQLInfoInteger関数
この関数は、MQL5プログラムの情報を整数として取得します。引数として渡した定数に応じて、最適化やテストなどの現在のプログラムの状態を確認できます。- MQL_OPTIMIZATION
定数MQL_OPTIMIZATIONは、現在が最適化中であるかどうかを判定するためのもので、最適化中なら1、そうでない場合は0を返します。 - MQL_TESTER
定数MQL_TESTERは、現在がテスト中であるかどうかを判定するためのもので、テスト中なら1、そうでない場合は0を返します。
- MQL_OPTIMIZATION
この条件文では、MQLInfoInteger(MQL_OPTIMIZATION)とMQLInfoInteger(MQL_TESTER)のどちらかが1の場合、EAが最適化またはテスト中であると判断されます。この場合、ログ出力を行わないようにしています。
ログ出力の詳細
PrintFormat("%s: %s %s に新しいバーが生成されました",
__FUNCTION__, _Symbol, StringSubstr(EnumToString(_Period), 7));
- PrintFormat関数
PrintFormat関数は、指定した書式に従ってメッセージをフォーマットし、エキスパートログに出力します。この関数ではフォーマット指定子を使って表示内容を整え、ログの見やすさを向上させています。
フォーマット指定子と対応する値
このコードで使用されているフォーマット指定子と対応する値は以下の通りです。
- %s: 現在の関数名(__FUNCTION__)
__FUNCTION__は現在実行中の関数名(ここではisNewBar)を文字列として返します。この指定子を使って、エラー発生や特定の処理がどの関数で行われたかを確認できるようにしています。 - %s: シンボル名(_Symbol)
_Symbolは、現在取引中の通貨ペアや金融商品を示すシンボル名を返します。ここでの%指定子は、シンボル名をログに表示し、EAがどのシンボルで動作しているかを明示します。 - %s: 時間足(StringSubstr(EnumToString(_Period), 7)) _Periodは、現在の時間足を示す定数です。EnumToString関数関数を使用して数値を文字列に変換した後、StringSubstr関数で一部を抽出して表示しています。このコードでは、”PERIOD”の文字列を除くために7文字目以降を取り出し、M1、M5、H1などの時間足形式で表示しています。
このように、PrintFormat関数はログメッセージに各情報を表示するために使われており、新しいバーが生成されたタイミングを確認するために役立ちます。
isNewBar関数の記述解説その4
//--- 最後のティックデータを取得
MqlTick last_tick;
if (!SymbolInfoTick(Symbol(), last_tick)) // ティック情報の取得に失敗した場合
Print("SymbolInfoTick() に失敗しました。エラーコード:", GetLastError());
//--- 最後のティックの時刻をミリ秒まで表示
PrintFormat("最終ティックの時刻: %s.%03d", TimeToString(last_tick.time, TIME_SECONDS), last_tick.time_msc % 1000);
}
return (true); // 新しいバーが生成された場合はtrueを返す
}
return (false); // 新しいバーが生成されていない場合はfalseを返す
}
この部分では、新しいバーが生成された場合に、最後のティックデータを取得してその詳細をログに出力しています。また、新しいバーが生成されているかどうかに応じてtrueまたはfalseを返しています。
処理の詳細
- MqlTick構造体の宣言
MqlTick構造体型のlast_tickを宣言し、最後のティックデータ(最新の価格情報や時間など)を格納するための準備を行います。MqlTick構造体には、ティックデータの詳細情報が含まれており、取引に関するさまざまな判断に役立ちます。 - SymbolInfoTick関数を使用してティックデータを取得
SymbolInfoTick関数を用いて、現在のシンボルに関する最新のティック情報を取得し、last_tick構造体に格納します。この関数は、取得が成功すればtrueを、失敗すればfalseを返します。取得に失敗した場合には、Print関数でエラーメッセージを出力し、GetLastError関数でエラーコードを取得して表示します。このエラーコードにより、何が原因でティックデータの取得に失敗したのかを把握できます。 - 最後のティック時刻をミリ秒までログ出力
PrintFormat関数を使い、取得したティックの時刻をミリ秒単位で表示します。時刻を文字列として表示するために、TimeToString関数を使用しています。この関数にTIME_SECONDSフラグを渡すことで、時刻が「時:分:秒」の形式で表示されます。ティックの詳細な時間は、last_tick.time_mscを用いてミリ秒単位で取得し、「%03d」のフォーマット指定子を使って、3桁のミリ秒部分をゼロ埋めして表示しています。
処理の戻り値
- 新しいバーが生成された場合
新しいバーが生成されたと判断された場合、関数はtrueを返します。これは、他の関数や条件が「新しいバーが生成された」状態で実行されるべき処理をトリガーするために使用されます。 - 新しいバーが生成されていない場合
if文の条件に合致しなかった場合、新しいバーは生成されていないと判断され、falseが返されます。この戻り値を受け取り、EAの内部でバー生成時のみに行うべき処理と、ティックごとに実行する処理を分けることが可能になります。
サンプルコードの解説その7-Buy関数とSell関数部分-
//+------------------------------------------------------------------+
//| 成行価格で指定の量を買う関数 |
//| 指定されたボリュームで成行買い注文を実行し、成功でtrueを返す |
//+------------------------------------------------------------------+
bool Buy(double volume, ulong deviation = 10, ulong magicnumber = 0)
{
//--- 買い注文を行う
return (MarketOrder(ORDER_TYPE_BUY, volume, deviation, magicnumber));
}
//+------------------------------------------------------------------+
//| 成行価格で指定の量を売る関数 |
//| 指定されたボリュームで成行売り注文を実行し、成功でtrueを返す |
//+------------------------------------------------------------------+
bool Sell(double volume, ulong deviation = 10, ulong magicnumber = 0)
{
//--- 売り注文を行う
return (MarketOrder(ORDER_TYPE_SELL, volume, deviation, magicnumber));
}
このコードは、指定されたボリューム(取引量)で成行注文を行うためのBuy関数とSell関数を定義しています。Buy関数は成行で買い注文を行い、Sell関数は成行で売り注文を行います。それぞれの関数は内部でMarketOrder関数を呼び出して実際の注文を実行し、成功すればtrue、失敗すればfalseを返します。
Buy関数の詳細
- Buy関数の目的
この関数は、指定された量で成行買い注文を行うために使用されます。成行注文は、現在の市場価格で即時に実行される注文です。 - 引数の解説
- volume
成行買い注文で指定する取引量です。例えば、0.1を指定すると0.1ロットで買い注文を実行します。 - deviation
価格の許容スリッページ(価格のズレ)を指定します。デフォルトでは10が設定されています。スリッページとは、指定した価格と実際の約定価格の差を許容する範囲を意味し、市場の急激な変動時でも指定範囲内であれば注文を実行します。 - magicnumber
マジックナンバーと呼ばれる識別番号で、EAが出した注文を区別するために使用されます。デフォルトでは0が設定されています。 - 関数の戻り値
MarketOrder関数の戻り値をそのまま返します。MarketOrder関数が成功した場合はtrue、失敗した場合はfalseを返し、注文が成功したかどうかを確認できます。
Sell関数の詳細
- Sell関数の目的
Sell関数は、指定された量で成行売り注文を行うために使用されます。こちらも成行注文で、現在の市場価格で即時に売り注文を実行します。 - 引数の解説
- volume
成行売り注文で指定する取引量です。例えば、0.1を指定すると0.1ロットで売り注文を実行します。 - deviation
価格の許容スリッページで、デフォルト値は10です。 - magicnumber
マジックナンバーを指定し、EAが出した注文を識別します。 - 関数の戻り値
MarketOrder関数の戻り値をそのまま返します。MarketOrder関数が成功した場合はtrue、失敗した場合はfalseを返し、注文の成功可否を確認できます。
MarketOrder関数について
このコードでは、MarketOrder関数を呼び出すことで、実際の買い注文または売り注文を実行しています。MarketOrder関数は、注文の種類(ORDER_TYPE_BUYやORDER_TYPE_SELL)、取引量、スリッページ、マジックナンバーといった引数を受け取り、指定した内容で成行注文を出します。
サンプルコードの解説その8-ClosePositionsByBars関数部分-
//+------------------------------------------------------------------+
//| 保持バー数に基づき古いポジションを決済する関数 |
//| 指定されたバー数より前に開かれたポジションを確認し、条件が合えば決済|
//+------------------------------------------------------------------+
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("ポジション #%I64u %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);
}
}
}
ClosePositionsByBarsコード解説1
//+------------------------------------------------------------------+
//| 保持バー数に基づき古いポジションを決済する関数 |
//| 指定されたバー数より前に開かれたポジションを確認し、条件が合えば決済|
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars, ulong deviation = 10, ulong magicnumber = 0)
{
int total = PositionsTotal(); // 現在のポジション数を取得
ClosePositionsByBars関数は、指定されたバー数以上前に開かれたポジションを自動的に決済するための関数です。指定の保有期間(バー数)を超えたポジションを確認し、条件を満たしていれば自動的に決済します。
引数の解説
- holdtimebars
ポジションの保有期間を指定するバー数です。この数より古いバーで開かれたポジションが決済対象となります。例えば、holdtimebarsを8と設定すると、8バー以上前に開かれたポジションが決済されます。 - deviation
許容スリッページを設定するための引数です。デフォルト値は10で、取引時に価格がこの範囲内でずれていれば注文を実行します。スリッページは、市場の変動が激しいときでも指定範囲内ならば取引を成立させるためのもので、価格変動に対応します。 - magicnumber
ポジションの識別用マジックナンバーです。デフォルト値は0ですが、特定のEAで開かれたポジションのみを管理したい場合には、EAごとのマジックナンバーを設定することで、この関数が指定EAのポジションのみを決済できるようになります。
処理の解説
関数内でPositionsTotal関数を使用して現在のポジション数を取得し、total変数に格納します。これにより、現在のポジション数を基準に古いポジションをチェックして、次の決済条件を満たすか確認します。
ClosePositionsByBarsコード解説2
//--- ポジションリストを逆順に確認(一番古いポジションを優先)
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;
この部分では、ClosePositionsByBars関数が保持するポジションリストを逆順に確認し、一番古いポジションから優先的に処理を行っています。これにより、保有期間が長いポジションを先にチェックし、指定された条件に合致すれば決済を行うことができます。
処理の詳細
- ポジションリストを逆順に確認
for文により、ポジションリストを最後のインデックス(total – 1)から順に確認しています。一番古いポジションから優先的に処理を行うことで、EAが保有期間を満たしたポジションを効率的に管理できます。 - ポジション情報の取得
各ポジションに対して、PositionGetTicket関数、PositionGetString関数、PositionGetInteger関数を使用して以下の情報を取得しています。- position_ticket
PositionGetTicket関数でポジションのチケット番号を取得します。このチケット番号は、ポジションを一意(同じものがない)に識別するために使用されます。 - position_symbol
PositionGetString関数を使用し、ポジションが関連するシンボル(通貨ペアや商品名)を取得します。これにより、どのシンボルでポジションが開かれているかが分かります。 - magic
PositionGetInteger関数で、ポジションのマジックナンバーを取得します。マジックナンバーは、EAが開いたポジションを識別するための番号で、この関数のmagicnumber引数と一致するポジションのみを決済対象とします。 - position_open
PositionGetInteger関数から、ポジションが開かれた時刻(POSITION_TIME)を取得します。この時刻はdateTime型に変換され、ポジションがどのバーで開かれたかを確認する際に利用されます。
- position_ticket
- ポジションが開かれたバー数の計算
iBarShift関数を使用し、現在のシンボルと時間足に基づき、position_openからのバー数を計算します。これにより、ポジションが保持されている期間(バー数)を確認でき、holdtimebarsで指定した期間と比較することが可能になります。+1を加えることで、現在のバーも含めて計算しています。
この処理により、保有期間が長いポジションを優先的に確認し、指定したバー数以上保持されたポジションを自動的に決済するための準備が整います。
ClosePositionsByBarsコード解説3
//--- ポジションが指定バー数以上前に開かれている場合かつ条件一致時に決済
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("ポジション #%I64u %s %s %.2f の決済を行います", position_ticket, position_symbol, str_type, volume);
この部分のコードでは、指定した条件に合致するポジションが見つかった場合に、そのポジションを決済するための準備を行います。特に、指定した保持期間(バー数)を満たし、マジックナンバーとシンボルが一致するポジションに対して、ポジションのタイプやボリュームを取得して、決済を行う際のログを出力します。
処理の詳細
- 決済条件の判定
if文では以下の条件がすべて満たされた場合に、ポジションを決済対象とします。 - 決済のための情報取得
条件を満たすポジションについて、以下の情報を取得します。- digits
SymbolInfoInteger関数でSYMBOL_DIGITSを取得し、小数点以下の桁数をdigitsに格納します。これにより、シンボルの精度に合った価格フォーマットが可能になります。 - volume
PositionGetDouble関数でポジションのボリューム(取引量)を取得し、決済時に必要な取引量を確認します。 - type
PositionGetInteger関数でポジションタイプ(POSITION_TYPE)を取得し、買いまたは売りのポジションであるかを確認します。取得した数値をENUM_POSITION_TYPE型にキャストすることで、買い(POSITION_TYPE_BUY)や売り(POSITION_TYPE_SELL)の具体的な値が分かります。 - str_type
EnumToString関数を使用してtype(ポジションのタイプ)を文字列に変換し、StringSubstr関数でその文字列の一部を取り出して表示用のタイプ(「buy」または「sell」など)を準備します。StringToLower関数で小文字に変換することで、ログ表示を整えます。
- digits
- 決済操作のログ出力
PrintFormat関数を使用して、決済操作の内容をログに出力します。ポジションのチケット番号(position_ticket)、シンボル(position_symbol)、ポジションのタイプ(str_type)、ボリューム(volume)を含むメッセージを表示し、どのポジションが決済されるかを視覚的に確認できるようにします。
この処理により、指定条件を満たすポジションが見つかった場合、決済準備が整い、ユーザーがログで決済内容を確認できるようになっています。
ClosePositionsByBarsコード解説4
//--- ポジションタイプに応じて売りまたは買い注文を行い決済
if (type == POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL, volume, deviation, magicnumber, position_ticket);
else
MarketOrder(ORDER_TYPE_BUY, volume, deviation, magicnumber, position_ticket);
}
}
}
この部分では、ポジションのタイプ(買いまたは売り)に応じて、反対方向の成行注文を発行し、ポジションを決済する処理を行っています。決済にはMarketOrder関数が使用され、指定のポジションチケットを対象に売買を実行します。
処理の詳細
- ポジションタイプに応じた決済判断
if文により、ポジションタイプが「買い(POSITION_TYPE_BUY)」である場合には売り注文を、ポジションタイプが「売り」である場合には買い注文を実行します。このように、保有中のポジションと逆方向の注文を出すことでポジションを決済します。- POSITION_TYPE_BUYの場合
ポジションが買いポジションの場合、MarketOrder関数を使って売り注文(ORDER_TYPE_SELL)を実行します。これにより、指定のvolume(取引量)で買いポジションが決済されます。 - POSITION_TYPE_SELLの場合
ポジションが売りポジションの場合、MarketOrder関数で買い注文(ORDER_TYPE_BUY)を発行し、同様にポジションが決済されます。
- POSITION_TYPE_BUYの場合
- MarketOrder関数の引数
- ORDER_TYPE_SELL/ ORDER_TYPE_BUY
注文の種類を指定し、買いポジションに対しては売り注文を、売りポジションに対しては買い注文を発行します。 - volume
ポジションの取引量を指定します。決済対象のポジションと同じ取引量を指定することで、完全にポジションを閉じます。 - deviation
許容スリッページの範囲を指定し、市場価格の変動に対応します。 - magicnumber
特定のEAが開いたポジションを識別するために使用します。 - position_ticket
決済対象のポジションチケット番号です。特定のポジションに対してのみ決済注文が行われるようにします。
- ORDER_TYPE_SELL/ ORDER_TYPE_BUY
この処理により、指定したバー数以上保有されているポジションが逆方向の注文で決済され、EAがポジション管理を自動で行います。
サンプルコードの解説その9-MarketOrder関数部分-
//+------------------------------------------------------------------+
//| 成行注文を準備して送信する関数 |
//| 指定された注文内容で成行注文を送信し、成功でtrueを返す |
//+------------------------------------------------------------------+
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); // 成行注文の価格を取得
//--- 注文タイプが買いの場合は買い価格(ASK)を設定
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; // マジックナンバーを設定
//--- 注文を送信し、成功すればtrueを返す
if (!OrderSend(request, result))
{
// 注文送信に失敗した場合、エラーメッセージを出力
PrintFormat("OrderSend %s %s %.2f at %.5f エラーコード: %d", request.symbol, EnumToString(type), volume, request.price, GetLastError());
return (false); // 注文が失敗した場合にfalseを返す
}
//--- 注文が成功した場合の結果を表示
PrintFormat("リクエストコード = %u、取引 = %I64u、注文番号 = %I64u", result.retcode, result.deal, result.order);
return (true); // 成功時にtrueを返す
}
MarketOrderコード解説1
//+------------------------------------------------------------------+
//| 成行注文を準備して送信する関数 |
//| 指定された注文内容で成行注文を送信し、成功でtrueを返す |
//+------------------------------------------------------------------+
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); // 成行注文の価格を取得
この部分では、指定された条件で成行注文を準備し、実際に注文を送信するためのMarketOrder関数です。取引リクエストの詳細を設定し、指定した注文内容で成行注文を送信します。注文が正常に実行されればtrue、失敗すればfalseを返します。
関数の引数について
- type(ENUM_ORDER_TYPE型)
注文の種類を指定する引数です。ORDER_TYPE_BUYは成行買い注文、ORDER_TYPE_SELLは成行売り注文を表します。この引数を使って、注文が買いか売りかを指定します。 - volume(double型)
注文の取引量を指定する引数で、ロット単位で表されます。たとえば、0.1を指定すると0.1ロットの成行注文が行われます。ロット数は取引量を決定し、取引の規模を設定します。 - slip(ulong型)
許容スリッページ(価格のずれ)を指定します。スリッページとは、発注時の価格と約定価格の差を指します。値が大きいほど、指定価格からの許容範囲が広がります。 - magicnumber(ulong型)
EAが出した注文を識別するためのマジックナンバーです。これを使用すると、複数のEAが動作している場合でも、それぞれのEAが出した注文を区別できます。 - pos_ticket(ulong型、デフォルト値0)
決済対象のポジションチケット番号です。既存のポジションを指定したボリュームで決済する際に使用します。デフォルト値は0で、新規注文時には指定する必要はありません。
変数と構造体の初期化
- request(MqlTradeRequest構造体)
MqlTradeRequest構造体を初期化し、取引のリクエスト内容を格納します。この構造体には注文の詳細(注文タイプ、取引量、価格など)が含まれており、取引サーバーに送信される内容を設定するために使用します。 - result(MqlTradeResult構造体型)
MqlTradeResult構造体を初期化し、取引の結果を格納します。注文が実行された後、この構造体に取引結果(注文が成功したかどうか、エラーコード、取引の詳細など)が返されます。
成行注文の価格取得
- price
成行注文を実行する価格を格納するための変数です。SymbolInfoDouble関数を使用して、現在のシンボルのBID価格を取得しています。成行注文の種類によっては、このBID価格が実際の注文価格として設定されます(通常、買い注文ではASK価格、売り注文ではBID価格が適用されます)。
この初期化と価格取得により、MarketOrder関数は成行注文を準備し、次のステップで取引内容をリクエストに設定して送信する準備が整います。
MarketOrderコード解説2
この部分では、成行注文のリクエスト内容を設定するために、MqlTradeRequest構造体の各フィールドに具体的な値を割り当てています。これにより、リクエストに必要な注文内容が揃い、サーバーに送信する準備が整います。
注文価格の設定
- 注文タイプが買いの場合に価格を設定
if文で、注文タイプが買いである場合に取引価格として買い価格(ASK)を設定します。typeがORDER_TYPE_BUY(買い注文)の場合、SymbolInfoDouble関数で現在のシンボルのASK価格を取得し、price変数に代入します。通常、買い注文はASK価格で実行され、売り注文はBID価格で実行されます。
リクエストパラメータの設定
- request.action
リクエストの取引操作タイプを指定します。ここではTRADE_ACTION_DEALが設定され、成行注文として即時に実行される取引リクエストを表します成行注文は、現在の市場価格で即時に実行されます。 - request.position
ポジションチケットを指定します。この値は、既存のポジションを決済する際に使用されます。新規の成行注文の場合にはデフォルト値の0が適用されますが、ポジションを特定して決済する際には指定されたチケット番号で対象ポジションを指定します。 - request.symbol
取引を行うシンボルを設定します。Symbol関数で現在のシンボル(通貨ペアや銘柄)を取得し、リクエスト内に設定することで、どのシンボルに対して注文を行うかが決まります。 - request.volume
取引量(ボリューム)を設定します。volume引数の値が設定され、取引のロット数を決定します。 - request.type
注文のタイプを設定します。type引数には、買い注文の場合ORDER_TYPE_BUY、売り注文の場合ORDER_TYPE_SELLが指定されます。これにより、リクエストが買い注文か売り注文かを明確にします。 - request.price
取引価格を設定します。ここで指定された価格は、通常は買い注文でASK価格、売り注文でBID価格を使用します。リクエストが送信される時点での適切な価格が設定されます。 - request.deviation
許容スリッページの範囲を設定します。スリッページとは、指定価格と実際の約定価格の差です。この範囲内であれば、価格がずれても注文が実行されます。 - request.magic
マジックナンバーを設定します。EAが出した注文を識別するための番号で、magicnumber引数の値が設定されます。これにより、他のEAや手動取引と区別でき、ポジション管理がしやすくなります。
この設定により、Request構造体にすべての注文情報が揃い、サーバーに送信される具体的な取引内容が整います。
MarketOrderコード解説3
//--- 注文を送信し、成功すればtrueを返す
if (!OrderSend(request, result))
{
// 注文送信に失敗した場合、エラーメッセージを出力
PrintFormat("OrderSend %s %s %.2f at %.5f エラーコード: %d", request.symbol, EnumToString(type), volume, request.price, GetLastError());
return (false); // 注文が失敗した場合にfalseを返す
}
//--- 注文が成功した場合の結果を表示
PrintFormat("リクエストコード = %u、取引 = %I64u、注文番号 = %I64u", result.retcode, result.deal, result.order);
return (true); // 成功時にtrueを返す
}
この部分のコードは、リクエストに基づいて注文を送信し、成功したかどうかに応じてtrueまたはfalseを返します。注文送信が成功した場合には取引結果の詳細を表示し、失敗した場合にはエラーメッセージを表示します。
注文送信の処理
- OrderSend関数
OrderSend関数は、MqlTradeRequest構造体で設定された注文内容をサーバーに送信し、その結果をMqlTradeResult構造体に格納します。関数がtrueを返した場合は注文が成功し、falseを返した場合は注文が失敗したことを意味します。
注文送信に失敗した場合の処理
- PrintFormat関数
PrintFormat関数を使い、エラーメッセージをフォーマットしてログに出力します。このメッセージには、シンボル名、注文のタイプ、取引量、価格、エラーコードが含まれており、どの注文がどのような理由で失敗したのかを確認できます。
PrintFormatの引数に使われている関数と値の解説
- request.symbol
現在の取引シンボル(通貨ペアや銘柄)を取得します。これにより、どのシンボルの注文でエラーが発生したかが確認できます。 - EnumToString(type)
注文タイプ(type)の値を文字列に変換します。EnumToString関数により、数値として保持されているenum列挙型の値が文字列として表示され、ここでは「ORDER_TYPE_BUY」または「ORDER_TYPE_SELL」と表示されます。 - volume
取引ボリュームを表示します。ロット数として表示されるため、注文の規模が確認できます。 - request.price
指定した価格での注文が失敗したことを示します。価格は成行注文が実行されるべき価格を示し、価格に問題がある場合などの確認に使えます。 - GetLastError()
エラーコードを取得します。注文が失敗した場合、GetLastError関数により発生したエラーコードを取得し、エラーメッセージに含めることで原因がわかります。
フォーマット指定子の解説
- %s
文字列を表示する指定子です。request.symbolやEnumToString(type)の値を表示します。 - %.2f
小数点以下2桁まで表示する指定子です。volumeの値が取引量として表示されます。 - %.5f
小数点以下5桁まで表示する指定子です。request.priceが注文価格として表示されます。 - %d
整数値を表示する指定子です。GetLastError関数の戻り値であるエラーコードが表示されます。
注文成功時の処理
- PrintFormat関数による結果表示
PrintFormat関数を使い、注文が成功した場合に取引結果をログに出力します。これにより、リクエストコード、取引ID、注文番号が表示され、注文が正しく実行されたことが確認できます。
フォーマット指定子の解説(注文成功時)
- %u
符号なし整数を表示する指定子です。リクエスト結果のコード(retcode)に対応し、取引が正常に実行されたかを示します。 - %I64u
64ビット符号なし整数を表示する指定子です。取引のID(deal)と注文番号(order)が表示されます。
このように、PrintFormat関数を使ってログを詳細に出力し、注文が成功したかどうかやエラーの原因を確認する手がかりを提供します。