OnTesterInit関数について解説した記事内↓にて、
OnTesterInit関数を利用したサンプルコードも掲載しているのですが、同じ記事内で解説するには、ちょっと長くて複雑だったもので、別記事にして数回に分けて解説しています。
前回はBuy関数とSell関数部分についての解説をおこないました↓
今回解説する記述は以下の箇所です。
//+------------------------------------------------------------------+
//| 保持バー数に基づき古いポジションを決済する関数 |
//| 指定されたバー数より前に開かれたポジションを確認し、条件が合えば決済|
//+------------------------------------------------------------------+
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);
}
}
}
コード解説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変数に格納します。これにより、現在のポジション数を基準に古いポジションをチェックして、次の決済条件を満たすか確認します。
コード解説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を加えることで、現在のバーも含めて計算しています。
この処理により、保有期間が長いポジションを優先的に確認し、指定したバー数以上保持されたポジションを自動的に決済するための準備が整います。
コード解説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)を含むメッセージを表示し、どのポジションが決済されるかを視覚的に確認できるようにします。
この処理により、指定条件を満たすポジションが見つかった場合、決済準備が整い、ユーザーがログで決済内容を確認できるようになっています。
コード解説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がポジション管理を自動で行います。
サンプルコードの全体記述
OnTesterInit関数を利用したサンプルコードの全体記述は以下の通りです。
//--- 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を返す
}