Consolidate Python ignore rules into root gitignore

This commit is contained in:
Hiroaki86
2026-05-27 23:01:28 +09:00
commit fa3394415d
399 changed files with 509103 additions and 0 deletions
+528
View File
@@ -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