Consolidate Python ignore rules into root gitignore
This commit is contained in:
@@ -0,0 +1,528 @@
|
||||
#ifndef HIT_EXTERNAL_PROCESS_MQH
|
||||
#define HIT_EXTERNAL_PROCESS_MQH
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| "process_done.txt"を作成する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 指定したdoneファイルを作成します。
|
||||
*
|
||||
* @param name 作成するファイル名。MT5のMQL5\Files配下を基準に扱います。
|
||||
*/
|
||||
void CreateDoneFile(const string name)
|
||||
{
|
||||
if(!FileIsExist(name))
|
||||
{
|
||||
int h = FileOpen(name, FILE_WRITE|FILE_TXT);
|
||||
if(h != INVALID_HANDLE)
|
||||
FileClose(h);
|
||||
else
|
||||
Print("Failed to create file: ", name, " err=", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| "process_done.txt"を削除する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 指定したdoneファイルを削除します。
|
||||
*
|
||||
* @param name 削除するファイル名。存在しない場合は何もしません。
|
||||
*/
|
||||
void DeleteDoneFile(const string name)
|
||||
{
|
||||
if(FileIsExist(name))
|
||||
FileDelete(name);
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| "process_done.txt"の存在を確認し、存在する場合はtrueを返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 指定したdoneファイルの存在有無を確認します。
|
||||
*
|
||||
* @param name 確認するファイル名。
|
||||
* @return ファイルが存在する場合はtrue、存在しない場合はfalse。
|
||||
*/
|
||||
bool CheckDoneFile(const string name)
|
||||
{
|
||||
return FileIsExist(name);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python実行中ファイルを作成する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief Python起動時刻を実行中ファイルへ記録します。
|
||||
*
|
||||
* @param name 作成するrunningファイル名。
|
||||
*/
|
||||
void CreateRunningFile(const string name, const uint process_id)
|
||||
{
|
||||
int h = FileOpen(name, FILE_WRITE | FILE_TXT);
|
||||
if(h != INVALID_HANDLE)
|
||||
{
|
||||
FileWrite(h, IntegerToString((long)TimeCurrent()));
|
||||
FileWrite(h, IntegerToString((long)process_id));
|
||||
FileClose(h);
|
||||
}
|
||||
else
|
||||
Print("Failed to create running file: ", name, " err=", GetLastError());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python実行中ファイルを削除する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 指定したrunningファイルを削除します。
|
||||
*
|
||||
* @param name 削除するrunningファイル名。
|
||||
*/
|
||||
void DeleteRunningFile(const string name)
|
||||
{
|
||||
if(FileIsExist(name))
|
||||
FileDelete(name);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python実行開始時刻を読み込む関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief runningファイルに記録されたPython起動時刻を読み込みます。
|
||||
*
|
||||
* @param name 読み込むrunningファイル名。
|
||||
* @return 読み込めた起動時刻。失敗時は0。
|
||||
*/
|
||||
datetime LoadRunningStartedAt(const string name)
|
||||
{
|
||||
if(!FileIsExist(name))
|
||||
return 0;
|
||||
|
||||
int h = FileOpen(name, FILE_READ | FILE_TXT);
|
||||
if(h == INVALID_HANDLE)
|
||||
{
|
||||
Print("Failed to open running file: ", name, " err=", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
string line = FileReadString(h);
|
||||
FileClose(h);
|
||||
return (datetime)StringToInteger(line);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python実行プロセスIDを読み込む関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief runningファイルに記録されたプロセスIDを読み込みます。
|
||||
*
|
||||
* @param name 読み込むrunningファイル名。
|
||||
* @return 読み込めたプロセスID。旧形式または失敗時は0。
|
||||
*/
|
||||
uint LoadRunningProcessId(const string name)
|
||||
{
|
||||
if(!FileIsExist(name))
|
||||
return 0;
|
||||
|
||||
int h = FileOpen(name, FILE_READ | FILE_TXT);
|
||||
if(h == INVALID_HANDLE)
|
||||
{
|
||||
Print("Failed to open running file: ", name, " err=", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileReadString(h); // started_at
|
||||
if(FileIsEnding(h))
|
||||
{
|
||||
FileClose(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
string line = FileReadString(h);
|
||||
FileClose(h);
|
||||
return (uint)StringToInteger(line);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python実行中ファイルがタイムアウトしているか判定する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief runningファイルの起動時刻からPython待ち上限を超えているか判定します。
|
||||
*
|
||||
* @param name 判定するrunningファイル名。
|
||||
* @return タイムアウトしている場合はtrue。
|
||||
*/
|
||||
bool IsRunningFileTimedOut(const string name)
|
||||
{
|
||||
datetime started_at = LoadRunningStartedAt(name);
|
||||
if(started_at <= 0)
|
||||
return true;
|
||||
|
||||
return (TimeCurrent() - started_at >= PYTHON_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 外部プロセス状態を初期値に戻す関数
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetExternalProcessState(ExternalProcessState &process)
|
||||
{
|
||||
if(process.handle != 0)
|
||||
CloseHandle(process.handle);
|
||||
|
||||
process.handle = 0;
|
||||
process.process_id = 0;
|
||||
process.active = false;
|
||||
process.exit_code_ready = false;
|
||||
process.exit_code = 0;
|
||||
process.started_at = 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| runningファイルのPIDからプロセスハンドルを復元する関数
|
||||
//+------------------------------------------------------------------+
|
||||
bool AttachRunningProcess(const string running_file, const string label, ExternalProcessState &process)
|
||||
{
|
||||
if(process.active && process.handle != 0)
|
||||
return true;
|
||||
|
||||
uint process_id = LoadRunningProcessId(running_file);
|
||||
if(process_id == 0)
|
||||
return false;
|
||||
|
||||
long handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, 0, process_id);
|
||||
if(handle == 0)
|
||||
{
|
||||
Print("[", label, "] failed to open running process. pid=", process_id, " err=", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
process.handle = handle;
|
||||
process.process_id = process_id;
|
||||
process.active = true;
|
||||
process.exit_code_ready = false;
|
||||
process.exit_code = STILL_ACTIVE;
|
||||
process.started_at = LoadRunningStartedAt(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 外部プロセスの終了状態を更新する関数
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateExternalProcessStatus(ExternalProcessState &process, const string label)
|
||||
{
|
||||
if(!process.active || process.handle == 0)
|
||||
return true;
|
||||
|
||||
int wait_result = WaitForSingleObject(process.handle, 0);
|
||||
if(wait_result == WAIT_TIMEOUT)
|
||||
return false;
|
||||
|
||||
uint exit_code = 1;
|
||||
if(wait_result == WAIT_OBJECT_0)
|
||||
{
|
||||
if(GetExitCodeProcess(process.handle, exit_code) == 0)
|
||||
{
|
||||
Print("[", label, "] failed to get process exit code. err=", GetLastError());
|
||||
exit_code = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[", label, "] WaitForSingleObject failed. result=", wait_result, " err=", GetLastError());
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
CloseHandle(process.handle);
|
||||
process.handle = 0;
|
||||
process.active = false;
|
||||
process.exit_code_ready = true;
|
||||
process.exit_code = exit_code;
|
||||
Print("[", label, "] process finished. pid=", process.process_id, " exit_code=", exit_code);
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 外部プロセスがタイムアウトしているか判定する関数
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsExternalProcessTimedOut(ExternalProcessState &process, const string running_file)
|
||||
{
|
||||
datetime started_at = process.started_at;
|
||||
if(started_at <= 0)
|
||||
started_at = LoadRunningStartedAt(running_file);
|
||||
if(started_at <= 0)
|
||||
return true;
|
||||
|
||||
return (TimeCurrent() - started_at >= PYTHON_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| バッチファイルをプロセスハンドル付きで起動する関数
|
||||
//+------------------------------------------------------------------+
|
||||
bool StartBatchProcess(const string bat_file, const string running_file, const string label, ExternalProcessState &process)
|
||||
{
|
||||
STARTUPINFO_W startup_info = {};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
|
||||
startup_info.cb = (uint)sizeof(startup_info);
|
||||
|
||||
string cmd_exe = "C:\\Windows\\System32\\cmd.exe";
|
||||
string command_line = "\"" + cmd_exe + "\" /c \"\"" + bat_file + "\"\"";
|
||||
|
||||
int created = CreateProcessW(
|
||||
cmd_exe,
|
||||
command_line,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
CREATE_NO_WINDOW,
|
||||
0,
|
||||
"C:\\ea_py",
|
||||
startup_info,
|
||||
process_info
|
||||
);
|
||||
|
||||
if(created == 0 || process_info.hProcess == 0)
|
||||
{
|
||||
Print("[", label, "] CreateProcessW failed. file=", bat_file, " err=", GetLastError());
|
||||
ResetExternalProcessState(process);
|
||||
DeleteRunningFile(running_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(process_info.hThread != 0)
|
||||
CloseHandle(process_info.hThread);
|
||||
|
||||
process.handle = process_info.hProcess;
|
||||
process.process_id = process_info.dwProcessId;
|
||||
process.active = true;
|
||||
process.exit_code_ready = false;
|
||||
process.exit_code = STILL_ACTIVE;
|
||||
process.started_at = TimeCurrent();
|
||||
|
||||
CreateRunningFile(running_file, process.process_id);
|
||||
Print("[", label, "] process started. pid=", process.process_id, " file=", bat_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| EA起動時のdoneファイル状態を整える関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief EA起動時にPython実行中状態を壊さない範囲でdoneファイルを準備します。
|
||||
*
|
||||
* @param done_file 完了判定ファイル名。
|
||||
* @param running_file 実行中判定ファイル名。
|
||||
* @param label ログ表示用ラベル。
|
||||
*/
|
||||
void PrepareDoneFileOnInit(const string done_file, const string running_file, const string label)
|
||||
{
|
||||
if(CheckDoneFile(done_file))
|
||||
{
|
||||
DeleteRunningFile(running_file);
|
||||
return;
|
||||
}
|
||||
|
||||
if(FileIsExist(running_file))
|
||||
{
|
||||
if(IsRunningFileTimedOut(running_file))
|
||||
{
|
||||
Print("[", label, "] stale running file found on init. Remove marker without creating done.");
|
||||
DeleteRunningFile(running_file);
|
||||
}
|
||||
else
|
||||
Print("[", label, "] Python seems to be running on init. Keep waiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
Print("[", label, "] done file not found on init. Keep as not-ready until the next trigger.");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python完了状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief doneファイルがあれば完了扱いにし、runningファイルを片付けます。
|
||||
*
|
||||
* @param done_file 完了判定ファイル名。
|
||||
* @param running_file 実行中判定ファイル名。
|
||||
* @return 完了済みの場合はtrue。
|
||||
*/
|
||||
bool IsProcessResultReady(const string done_file, const string running_file, const string result_file, const string label, ExternalProcessState &process)
|
||||
{
|
||||
if(process.active || FileIsExist(running_file))
|
||||
{
|
||||
if(process.active || AttachRunningProcess(running_file, label, process))
|
||||
{
|
||||
if(!UpdateExternalProcessStatus(process, label))
|
||||
return false;
|
||||
|
||||
DeleteRunningFile(running_file);
|
||||
if(process.exit_code_ready && process.exit_code != 0)
|
||||
{
|
||||
Print("[", label, "] process failed. exit_code=", process.exit_code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(!CheckDoneFile(done_file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!CheckDoneFile(done_file))
|
||||
return false;
|
||||
|
||||
if(!FileIsExist(result_file))
|
||||
{
|
||||
Print("[", label, "] done exists but result file is missing: ", result_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteRunningFile(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Python開始可能状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief done/runningファイル状態からPythonを新規起動できるか判定します。
|
||||
*
|
||||
* @param done_file 完了判定ファイル名。
|
||||
* @param running_file 実行中判定ファイル名。
|
||||
* @param label ログ表示用ラベル。
|
||||
* @return 起動可能な場合はtrue。
|
||||
*/
|
||||
bool IsProcessStartAllowed(const string done_file, const string running_file, const string label, ExternalProcessState &process)
|
||||
{
|
||||
if(process.active || FileIsExist(running_file))
|
||||
{
|
||||
if(process.active || AttachRunningProcess(running_file, label, process))
|
||||
{
|
||||
if(!UpdateExternalProcessStatus(process, label))
|
||||
{
|
||||
if(IsExternalProcessTimedOut(process, running_file))
|
||||
Print("[", label, "] Python timed out, but the process is still running. Keep waiting.");
|
||||
else
|
||||
Print("[", label, "] Python is still running. Skip execute.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteRunningFile(running_file);
|
||||
if(process.exit_code_ready && process.exit_code != 0)
|
||||
Print("[", label, "] previous process failed. exit_code=", process.exit_code, ". Retry is allowed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(CheckDoneFile(done_file))
|
||||
{
|
||||
DeleteRunningFile(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(IsRunningFileTimedOut(running_file))
|
||||
{
|
||||
Print("[", label, "] stale running marker without live process. Retry is allowed.");
|
||||
DeleteRunningFile(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
Print("[", label, "] running marker exists, but process cannot be verified yet. Skip execute.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(CheckDoneFile(done_file))
|
||||
{
|
||||
DeleteRunningFile(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
Print("[", label, "] done file missing without running marker. Start new process.");
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| タイムアウトしたPython待ちを復旧する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief Pythonがdoneファイルを返さない状態を検知し、再実行できる状態へ戻します。
|
||||
*/
|
||||
void RecoverTimedOutPythonProcesses()
|
||||
{
|
||||
if(RecoverTimedOutProcess(done_trend_file, running_trend_file, "trend", g_trend_process))
|
||||
{
|
||||
g_ea.load_trend_flg = false;
|
||||
g_init_trend_pending = true;
|
||||
}
|
||||
|
||||
if(RecoverTimedOutProcess(done_entry_file, running_entry_file, "entry", g_entry_process))
|
||||
{
|
||||
g_ea.load_target_flg = false;
|
||||
g_bars_H1_check = false;
|
||||
g_bars_M15_check = false;
|
||||
g_ea.chk_cnt = 0;
|
||||
g_init_entry_pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 個別Python処理のタイムアウトを復旧する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 1種類のPython処理について、実行中ファイルのタイムアウトを検知します。
|
||||
*
|
||||
* @param done_file 完了判定ファイル名。
|
||||
* @param running_file 実行中判定ファイル名。
|
||||
* @param label ログ表示用ラベル。
|
||||
* @return タイムアウト復旧を行った場合はtrue。
|
||||
*/
|
||||
bool RecoverTimedOutProcess(const string done_file, const string running_file, const string label, ExternalProcessState &process)
|
||||
{
|
||||
if(CheckDoneFile(done_file) && !process.active)
|
||||
{
|
||||
DeleteRunningFile(running_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!process.active && !FileIsExist(running_file))
|
||||
return false;
|
||||
|
||||
if(process.active || AttachRunningProcess(running_file, label, process))
|
||||
{
|
||||
if(!UpdateExternalProcessStatus(process, label))
|
||||
{
|
||||
if(IsExternalProcessTimedOut(process, running_file))
|
||||
Print("[", label, "] Python exceeded ", PYTHON_TIMEOUT_SECONDS,
|
||||
" seconds, but the process is still running. Keep waiting.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteRunningFile(running_file);
|
||||
if(process.exit_code_ready && process.exit_code != 0)
|
||||
{
|
||||
Print("[", label, "] Python process failed. Retry on next trigger.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!CheckDoneFile(done_file))
|
||||
{
|
||||
Print("[", label, "] Python process ended without done file. Retry on next trigger.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!IsRunningFileTimedOut(running_file))
|
||||
return false;
|
||||
|
||||
Print("[", label, "] Python did not finish within ",
|
||||
PYTHON_TIMEOUT_SECONDS, " seconds, and no live process was found. Retry is allowed.");
|
||||
DeleteRunningFile(running_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,289 @@
|
||||
#ifndef HIT_RUNTIME_CONTROLLER_MQH
|
||||
#define HIT_RUNTIME_CONTROLLER_MQH
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| ティック情報を取得する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 現在のAsk/Bid/スプレッド情報を取得してTickContextへ格納します。
|
||||
*
|
||||
* @param ctx 取得したティック情報を格納する構造体参照。
|
||||
* @return 取得成功時はtrue、ティック情報を取得できない場合はfalse。
|
||||
*/
|
||||
bool GetTickContext(TickContext &ctx)
|
||||
{
|
||||
MqlTick last_tick;
|
||||
if(!SymbolInfoTick(_Symbol, last_tick))
|
||||
return false;
|
||||
|
||||
ctx.ask = last_tick.ask;
|
||||
ctx.bid = last_tick.bid;
|
||||
ctx.spread_points = MathRound((ctx.ask - ctx.bid) / Point());
|
||||
ctx.spread = ctx.spread_points * Point();
|
||||
ctx.digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 期限切れ注文・期限切れポジションを処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 期限切れの未約定注文と保有ポジションを処理します。
|
||||
*
|
||||
* 未約定注文はENTRY_H1_LIMIT時間、保有ポジションはCLOSE_H1_LIMIT時間を基準に判定します。
|
||||
* 新規注文条件とは独立して、OnTickの早い段階で実行される想定です。
|
||||
*/
|
||||
void ManageExpiredTrades()
|
||||
{
|
||||
if(OrdersTotal() > 0)
|
||||
CancelExpiredOrders();
|
||||
|
||||
if(PositionsTotal() > 0)
|
||||
CloseExpiredPositions();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H4更新検知とトレンド判定Python起動を処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4新バーまたは初回起動を検知し、トレンド判定Pythonを起動します。
|
||||
*
|
||||
* @param state EA全体の状態。トレンド更新開始フラグを更新します。
|
||||
*
|
||||
* Python処理中はdoneファイルが存在しないため、CSVの上書きと二重起動を抑止します。
|
||||
*/
|
||||
void ProcessTrendUpdate(EAState &state)
|
||||
{
|
||||
int current_bars_H4 = iBars(NULL, PERIOD_H4);
|
||||
if(g_pre_bars_H4 == 0)
|
||||
g_pre_bars_H4 = current_bars_H4;
|
||||
|
||||
int bars_H4_change = current_bars_H4 - g_pre_bars_H4;
|
||||
bool trend_trigger = (bars_H4_change > 0 || g_init_trend_pending);
|
||||
|
||||
if(!trend_trigger)
|
||||
return;
|
||||
|
||||
if(RecordOHLCAndExecuteBatch_Trend(state))
|
||||
{
|
||||
g_init_trend_pending = false;
|
||||
g_pre_bars_H4 = current_bars_H4;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| トレンド判定Pythonの完了状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4トレンド判定Pythonの完了状態を確認します。
|
||||
*
|
||||
* @return `process_done_trend.txt` が存在する場合はtrue、未完了の場合はfalse。
|
||||
*/
|
||||
bool IsTrendResultReady()
|
||||
{
|
||||
return IsProcessResultReady(done_trend_file, running_trend_file, trend_state_file, "trend", g_trend_process);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| トレンド判定結果をEA状態へ反映する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 完了済みのH4トレンド判定結果をEA状態へ反映します。
|
||||
*
|
||||
* @param state EA全体の状態。`trend_state` と最終更新時刻が更新されます。
|
||||
*/
|
||||
void RefreshTrendState(EAState &state)
|
||||
{
|
||||
GetTrendState(state);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1更新検知とエントリー価格生成Python起動を処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H1新バーまたは初回起動を検知し、エントリー価格生成Pythonを起動します。
|
||||
*
|
||||
* @param state EA全体の状態。ターゲット価格更新開始フラグや判定リトライ状態を更新します。
|
||||
*
|
||||
* Python処理中はdoneファイルが存在しないため、H1 CSVの上書きと二重起動を抑止します。
|
||||
*/
|
||||
void ProcessEntryUpdate(EAState &state)
|
||||
{
|
||||
int current_bars_H1 = iBars(NULL, PERIOD_H1);
|
||||
if(g_pre_bars_H1 == 0)
|
||||
g_pre_bars_H1 = current_bars_H1;
|
||||
|
||||
int bars_H1_change = current_bars_H1 - g_pre_bars_H1;
|
||||
bool entry_trigger = (bars_H1_change > 0 || g_init_entry_pending);
|
||||
|
||||
if(!entry_trigger)
|
||||
return;
|
||||
|
||||
if(RecordOHLCAndExecuteBatch_Entry(state))
|
||||
{
|
||||
g_init_entry_pending = false;
|
||||
g_bars_H1_check = true;
|
||||
state.chk_cnt = 0;
|
||||
state.last_chk = 0;
|
||||
g_pre_bars_H1 = current_bars_H1;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| M15更新検知とエントリータイミング判定トリガーを処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief M15新バーを検知し、H1候補価格の発注判定を実行できる状態にします。
|
||||
*
|
||||
* M15は方向判定を上書きする足ではなく、H4/H1で決めた候補価格を発注する
|
||||
* タイミング確認として使います。初回は直近の確定M15足で一度だけ判定可能にします。
|
||||
*/
|
||||
void ProcessM15EntryTimingUpdate()
|
||||
{
|
||||
int current_bars_M15 = iBars(NULL, PERIOD_M15);
|
||||
if(current_bars_M15 <= 0)
|
||||
return;
|
||||
|
||||
if(g_pre_bars_M15 == 0)
|
||||
{
|
||||
g_pre_bars_M15 = current_bars_M15;
|
||||
g_bars_M15_check = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int bars_M15_change = current_bars_M15 - g_pre_bars_M15;
|
||||
if(bars_M15_change <= 0)
|
||||
return;
|
||||
|
||||
g_bars_M15_check = true;
|
||||
g_pre_bars_M15 = current_bars_M15;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| チャートコメント文字列を作成する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief チャート左上に表示するステータスメッセージを組み立てます。
|
||||
*
|
||||
* @param ctx 現在のAsk/Bid/スプレッド情報。
|
||||
* @return Comment()へ渡す表示用文字列。
|
||||
*/
|
||||
string BuildStatusMessage(TickContext &ctx)
|
||||
{
|
||||
string message = StringFormat(
|
||||
" \nAsk: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nBid: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nSpread: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nSpread Points: %.0f"
|
||||
"\n\nLast Trend Update:\n %s"
|
||||
"\n\nMarket State: %d (%s)"
|
||||
"\n\nLast Target Update:\n %s"
|
||||
"\n\nMy Used Count: %d / %d"
|
||||
"\nSplit Zone: %s N=%d ZoneID=%s"
|
||||
"\n\n[T1 Buy Stop ] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T2 Buy Limit] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T3 Sell Stop] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T4 SellLimit] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f\n",
|
||||
ctx.ask, ctx.bid, ctx.spread, ctx.spread_points,
|
||||
TimeToString(g_ea.last_trend_update, TIME_DATE | TIME_MINUTES),
|
||||
g_ea.trend_state, MarketStateName(g_ea.trend_state),
|
||||
TimeToString(g_ea.last_target_update, TIME_DATE | TIME_MINUTES),
|
||||
CountMyUsed(), EffectivePositionLimit(),
|
||||
(use_split_entry_zone ? "ON" : "OFF"), EffectiveSplitEntryCount(), g_ea.zone_candidate_id,
|
||||
g_ea.en_price[1], g_ea.tp_price[1], g_ea.sl_price[1],
|
||||
g_ea.en_price[2], g_ea.tp_price[2], g_ea.sl_price[2],
|
||||
g_ea.en_price[3], g_ea.tp_price[3], g_ea.sl_price[3],
|
||||
g_ea.en_price[4], g_ea.tp_price[4], g_ea.sl_price[4]
|
||||
);
|
||||
return message;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| チャートコメントを更新する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief チャート上のステータスコメントを更新します。
|
||||
*
|
||||
* @param ctx 現在のAsk/Bid/スプレッド情報。
|
||||
*/
|
||||
void UpdateStatusComment(TickContext &ctx)
|
||||
{
|
||||
Comment(BuildStatusMessage(ctx));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| market_stateの表示名を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4 market_stateの人間可読名を返します。
|
||||
*
|
||||
* @param market_state Pythonが出力したH4環境分類(0..5、6は技術エラー停止)。
|
||||
* @return 表示用ラベル。
|
||||
*/
|
||||
string MarketStateName(const int market_state)
|
||||
{
|
||||
switch(market_state)
|
||||
{
|
||||
case MARKET_LOW_VOL_RANGE:
|
||||
return "LOW_VOL_RANGE";
|
||||
case MARKET_HIGH_VOL_RANGE:
|
||||
return "HIGH_VOL_RANGE";
|
||||
case MARKET_LOW_VOL_UP:
|
||||
return "LOW_VOL_UP";
|
||||
case MARKET_HIGH_VOL_UP:
|
||||
return "HIGH_VOL_UP";
|
||||
case MARKET_LOW_VOL_DOWN:
|
||||
return "LOW_VOL_DOWN";
|
||||
case MARKET_HIGH_VOL_DOWN:
|
||||
return "HIGH_VOL_DOWN";
|
||||
case MARKET_TECHNICAL_ERROR_STOP:
|
||||
return "TECHNICAL_ERROR_STOP";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| スプレッドが許容範囲内か判定する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 現在スプレッドが入力パラメータの許容範囲内か判定します。
|
||||
*
|
||||
* @param ctx 現在のスプレッド情報。
|
||||
* @return 許容範囲内ならtrue、超過している場合はfalse。
|
||||
*/
|
||||
bool IsSpreadAllowed(TickContext &ctx)
|
||||
{
|
||||
return (ctx.spread <= spread_limit * Point());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| エントリー価格生成Pythonの完了状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H1エントリー価格生成Pythonの完了状態を確認します。
|
||||
*
|
||||
* @return `process_done_entry.txt` が存在する場合はtrue、未完了の場合はfalse。
|
||||
*/
|
||||
bool IsEntryResultReady()
|
||||
{
|
||||
return IsProcessResultReady(done_entry_file, running_entry_file, target_prices_file, "entry", g_entry_process);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| ターゲット価格をEA状態へ反映する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 完了済みのH1エントリー価格生成結果をEA状態へ反映します。
|
||||
*
|
||||
* @param state EA全体の状態。`res_chk` と各注文タイプのen/tp/slが更新されます。
|
||||
*/
|
||||
void RefreshTargetPrices(EAState &state)
|
||||
{
|
||||
GetTargetPrices(state);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,287 @@
|
||||
#ifndef HIT_RUNTIME_CONTROLLER_MQH
|
||||
#define HIT_RUNTIME_CONTROLLER_MQH
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| ティック情報を取得する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 現在のAsk/Bid/スプレッド情報を取得してTickContextへ格納します。
|
||||
*
|
||||
* @param ctx 取得したティック情報を格納する構造体参照。
|
||||
* @return 取得成功時はtrue、ティック情報を取得できない場合はfalse。
|
||||
*/
|
||||
bool GetTickContext(TickContext &ctx)
|
||||
{
|
||||
MqlTick last_tick;
|
||||
if(!SymbolInfoTick(_Symbol, last_tick))
|
||||
return false;
|
||||
|
||||
ctx.ask = last_tick.ask;
|
||||
ctx.bid = last_tick.bid;
|
||||
ctx.spread_points = MathRound((ctx.ask - ctx.bid) / Point());
|
||||
ctx.spread = ctx.spread_points * Point();
|
||||
ctx.digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 期限切れ注文・期限切れポジションを処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 期限切れの未約定注文と保有ポジションを処理します。
|
||||
*
|
||||
* 未約定注文はENTRY_H1_LIMIT時間、保有ポジションはCLOSE_H1_LIMIT時間を基準に判定します。
|
||||
* 新規注文条件とは独立して、OnTickの早い段階で実行される想定です。
|
||||
*/
|
||||
void ManageExpiredTrades()
|
||||
{
|
||||
if(OrdersTotal() > 0)
|
||||
CancelExpiredOrders();
|
||||
|
||||
if(PositionsTotal() > 0)
|
||||
CloseExpiredPositions();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H4更新検知とトレンド判定Python起動を処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4新バーまたは初回起動を検知し、トレンド判定Pythonを起動します。
|
||||
*
|
||||
* @param state EA全体の状態。トレンド更新開始フラグを更新します。
|
||||
*
|
||||
* Python処理中はdoneファイルが存在しないため、CSVの上書きと二重起動を抑止します。
|
||||
*/
|
||||
void ProcessTrendUpdate(EAState &state)
|
||||
{
|
||||
int current_bars_H4 = iBars(NULL, PERIOD_H4);
|
||||
if(g_pre_bars_H4 == 0)
|
||||
g_pre_bars_H4 = current_bars_H4;
|
||||
|
||||
int bars_H4_change = current_bars_H4 - g_pre_bars_H4;
|
||||
bool trend_trigger = (bars_H4_change > 0 || g_init_trend_pending);
|
||||
|
||||
if(!trend_trigger)
|
||||
return;
|
||||
|
||||
if(RecordOHLCAndExecuteBatch_Trend(state))
|
||||
{
|
||||
g_init_trend_pending = false;
|
||||
g_pre_bars_H4 = current_bars_H4;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| トレンド判定Pythonの完了状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4トレンド判定Pythonの完了状態を確認します。
|
||||
*
|
||||
* @return `process_done_trend.txt` が存在する場合はtrue、未完了の場合はfalse。
|
||||
*/
|
||||
bool IsTrendResultReady()
|
||||
{
|
||||
return IsProcessResultReady(done_trend_file, running_trend_file, trend_state_file, "trend", g_trend_process);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| トレンド判定結果をEA状態へ反映する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 完了済みのH4トレンド判定結果をEA状態へ反映します。
|
||||
*
|
||||
* @param state EA全体の状態。`trend_state` と最終更新時刻が更新されます。
|
||||
*/
|
||||
void RefreshTrendState(EAState &state)
|
||||
{
|
||||
GetTrendState(state);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1更新検知とエントリー価格生成Python起動を処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H1新バーまたは初回起動を検知し、エントリー価格生成Pythonを起動します。
|
||||
*
|
||||
* @param state EA全体の状態。ターゲット価格更新開始フラグや判定リトライ状態を更新します。
|
||||
*
|
||||
* Python処理中はdoneファイルが存在しないため、H1 CSVの上書きと二重起動を抑止します。
|
||||
*/
|
||||
void ProcessEntryUpdate(EAState &state)
|
||||
{
|
||||
int current_bars_H1 = iBars(NULL, PERIOD_H1);
|
||||
if(g_pre_bars_H1 == 0)
|
||||
g_pre_bars_H1 = current_bars_H1;
|
||||
|
||||
int bars_H1_change = current_bars_H1 - g_pre_bars_H1;
|
||||
bool entry_trigger = (bars_H1_change > 0 || g_init_entry_pending);
|
||||
|
||||
if(!entry_trigger)
|
||||
return;
|
||||
|
||||
if(RecordOHLCAndExecuteBatch_Entry(state))
|
||||
{
|
||||
g_init_entry_pending = false;
|
||||
g_bars_H1_check = true;
|
||||
state.chk_cnt = 0;
|
||||
state.last_chk = 0;
|
||||
g_pre_bars_H1 = current_bars_H1;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| M15更新検知とエントリータイミング判定トリガーを処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief M15新バーを検知し、H1候補価格の発注判定を実行できる状態にします。
|
||||
*
|
||||
* M15は方向判定を上書きする足ではなく、H4/H1で決めた候補価格を発注する
|
||||
* タイミング確認として使います。初回は直近の確定M15足で一度だけ判定可能にします。
|
||||
*/
|
||||
void ProcessM15EntryTimingUpdate()
|
||||
{
|
||||
int current_bars_M15 = iBars(NULL, PERIOD_M15);
|
||||
if(current_bars_M15 <= 0)
|
||||
return;
|
||||
|
||||
if(g_pre_bars_M15 == 0)
|
||||
{
|
||||
g_pre_bars_M15 = current_bars_M15;
|
||||
g_bars_M15_check = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int bars_M15_change = current_bars_M15 - g_pre_bars_M15;
|
||||
if(bars_M15_change <= 0)
|
||||
return;
|
||||
|
||||
g_bars_M15_check = true;
|
||||
g_pre_bars_M15 = current_bars_M15;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| チャートコメント文字列を作成する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief チャート左上に表示するステータスメッセージを組み立てます。
|
||||
*
|
||||
* @param ctx 現在のAsk/Bid/スプレッド情報。
|
||||
* @return Comment()へ渡す表示用文字列。
|
||||
*/
|
||||
string BuildStatusMessage(TickContext &ctx)
|
||||
{
|
||||
string message = StringFormat(
|
||||
" \nAsk: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nBid: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nSpread: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\nSpread Points: %.0f"
|
||||
"\n\nLast Trend Update:\n %s"
|
||||
"\n\nMarket State: %d (%s)"
|
||||
"\n\nLast Target Update:\n %s"
|
||||
"\n\nMy Used Count: %d / %d"
|
||||
"\n\n[T1 Buy Stop ] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T2 Buy Limit] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T3 Sell Stop] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f"
|
||||
"\n[T4 SellLimit] en: %." + IntegerToString(ctx.digits) + "f tp: %." + IntegerToString(ctx.digits) + "f sl: %." + IntegerToString(ctx.digits) + "f\n",
|
||||
ctx.ask, ctx.bid, ctx.spread, ctx.spread_points,
|
||||
TimeToString(g_ea.last_trend_update, TIME_DATE | TIME_MINUTES),
|
||||
g_ea.trend_state, MarketStateName(g_ea.trend_state),
|
||||
TimeToString(g_ea.last_target_update, TIME_DATE | TIME_MINUTES),
|
||||
CountMyUsed(), POSITION_LIMIT,
|
||||
g_ea.en_price[1], g_ea.tp_price[1], g_ea.sl_price[1],
|
||||
g_ea.en_price[2], g_ea.tp_price[2], g_ea.sl_price[2],
|
||||
g_ea.en_price[3], g_ea.tp_price[3], g_ea.sl_price[3],
|
||||
g_ea.en_price[4], g_ea.tp_price[4], g_ea.sl_price[4]
|
||||
);
|
||||
return message;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| チャートコメントを更新する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief チャート上のステータスコメントを更新します。
|
||||
*
|
||||
* @param ctx 現在のAsk/Bid/スプレッド情報。
|
||||
*/
|
||||
void UpdateStatusComment(TickContext &ctx)
|
||||
{
|
||||
Comment(BuildStatusMessage(ctx));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| market_stateの表示名を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H4 market_stateの人間可読名を返します。
|
||||
*
|
||||
* @param market_state Pythonが出力したH4環境分類(0..5、6は技術エラー停止)。
|
||||
* @return 表示用ラベル。
|
||||
*/
|
||||
string MarketStateName(const int market_state)
|
||||
{
|
||||
switch(market_state)
|
||||
{
|
||||
case MARKET_LOW_VOL_RANGE:
|
||||
return "LOW_VOL_RANGE";
|
||||
case MARKET_HIGH_VOL_RANGE:
|
||||
return "HIGH_VOL_RANGE";
|
||||
case MARKET_LOW_VOL_UP:
|
||||
return "LOW_VOL_UP";
|
||||
case MARKET_HIGH_VOL_UP:
|
||||
return "HIGH_VOL_UP";
|
||||
case MARKET_LOW_VOL_DOWN:
|
||||
return "LOW_VOL_DOWN";
|
||||
case MARKET_HIGH_VOL_DOWN:
|
||||
return "HIGH_VOL_DOWN";
|
||||
case MARKET_TECHNICAL_ERROR_STOP:
|
||||
return "TECHNICAL_ERROR_STOP";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| スプレッドが許容範囲内か判定する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 現在スプレッドが入力パラメータの許容範囲内か判定します。
|
||||
*
|
||||
* @param ctx 現在のスプレッド情報。
|
||||
* @return 許容範囲内ならtrue、超過している場合はfalse。
|
||||
*/
|
||||
bool IsSpreadAllowed(TickContext &ctx)
|
||||
{
|
||||
return (ctx.spread <= spread_limit * Point());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| エントリー価格生成Pythonの完了状態を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H1エントリー価格生成Pythonの完了状態を確認します。
|
||||
*
|
||||
* @return `process_done_entry.txt` が存在する場合はtrue、未完了の場合はfalse。
|
||||
*/
|
||||
bool IsEntryResultReady()
|
||||
{
|
||||
return IsProcessResultReady(done_entry_file, running_entry_file, target_prices_file, "entry", g_entry_process);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| ターゲット価格をEA状態へ反映する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief 完了済みのH1エントリー価格生成結果をEA状態へ反映します。
|
||||
*
|
||||
* @param state EA全体の状態。`res_chk` と各注文タイプのen/tp/slが更新されます。
|
||||
*/
|
||||
void RefreshTargetPrices(EAState &state)
|
||||
{
|
||||
GetTargetPrices(state);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef TRADING_PANEL_SYMBOL_UTILS_MQH
|
||||
#define TRADING_PANEL_SYMBOL_UTILS_MQH
|
||||
|
||||
double TradingPanelAdjustPoint(const string symbol)
|
||||
{
|
||||
// Keep the legacy XAUUSD/GOLD pip conversion for backtest reproducibility.
|
||||
if (symbol == "XAUUSD" || symbol == "GOLD")
|
||||
return 0.1;
|
||||
|
||||
const int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
|
||||
switch (symbol_digits)
|
||||
{
|
||||
case 2:
|
||||
case 3:
|
||||
return 0.01;
|
||||
case 4:
|
||||
case 5:
|
||||
return 0.0001;
|
||||
}
|
||||
|
||||
PrintFormat("Unsupported symbol digits for pip conversion: symbol=%s digits=%d",
|
||||
symbol, symbol_digits);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
ENUM_ORDER_TYPE_FILLING TradingPanelGetOrderFillingPolicy(const string symbol)
|
||||
{
|
||||
const long filling_mode = SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
|
||||
|
||||
if ((filling_mode & SYMBOL_FILLING_IOC) != 0)
|
||||
return ORDER_FILLING_IOC;
|
||||
if ((filling_mode & SYMBOL_FILLING_FOK) != 0)
|
||||
return ORDER_FILLING_FOK;
|
||||
|
||||
return ORDER_FILLING_RETURN;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user