Files
2026-06-14 22:28:29 +09:00

516 lines
21 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| HITEntryEA.mq5 |
//| Copyright 2026, nanpin-martin.com |
//| https://www.nanpin-martin.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, nanpin-martin.com"
#property link "https://nanpin-martin.com/"
#property version "1.11"
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// Windowsプロセス起動・終了確認用
#define CREATE_NO_WINDOW 0x08000000
#define PROCESS_QUERY_LIMITED_INFORMATION 0x00001000
#define SYNCHRONIZE 0x00100000
#define WAIT_OBJECT_0 0
#define WAIT_TIMEOUT 258
#define STILL_ACTIVE 259
#define TASKKILL_WAIT_MILLISECONDS 5000
struct STARTUPINFO_W
{
uint cb;
string lpReserved;
string lpDesktop;
string lpTitle;
uint dwX;
uint dwY;
uint dwXSize;
uint dwYSize;
uint dwXCountChars;
uint dwYCountChars;
uint dwFillAttribute;
uint dwFlags;
ushort wShowWindow;
ushort cbReserved2;
long lpReserved2;
long hStdInput;
long hStdOutput;
long hStdError;
};
struct PROCESS_INFORMATION
{
long hProcess;
long hThread;
uint dwProcessId;
uint dwThreadId;
};
#import "kernel32.dll"
int CreateProcessW(string lpApplicationName, string lpCommandLine, long lpProcessAttributes, long lpThreadAttributes, int bInheritHandles, uint dwCreationFlags, long lpEnvironment, string lpCurrentDirectory, STARTUPINFO_W &lpStartupInfo, PROCESS_INFORMATION &lpProcessInformation);
int WaitForSingleObject(long hHandle, uint dwMilliseconds);
int GetExitCodeProcess(long hProcess, uint &lpExitCode);
int CloseHandle(long hObject);
long OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);
#import
// バッチファイルのパス
// Python helper scripts are managed under this MT5 data folder:
// <TerminalDataPath>\MQL5\python_for_ea
string python_app_dir = "";
string get_trend_reply_bat = ""; // H4: trend
string get_entry_reply_bat = ""; // H1: entry
string mt5_file_prefix = ""; // Python linkage file namespace
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
string ohlc_h4_file = "ohlc_H4.csv";
string ohlc_h1_file = "ohlc_H1.csv";
string trend_state_file = "trend_state.txt";
string target_prices_file = "target_prices.txt";
string target_zones_file = "target_zones.txt";
// Python実行中を判定するための管理ファイル
string running_trend_file = "process_running_trend.txt";
string running_entry_file = "process_running_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
enum SplitLotMode
{
SPLIT_LOT_TOTAL = 0, // 総ロットをN分割
SPLIT_LOT_FIXED = 1 // 1注文あたり固定ロット
};
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int input_entry_magic_number = 10001; // 発注EAのマジックナンバー
input ulong input_slippage_points = 10; // 許容スリッページ(point)
input int initial_order = 0; // 互換性維持用。起動時H4/H1再構築は常時実行
input int input_position_limit = 10; // 自EAの未約定注文+ポジション上限
input bool use_split_entry_zone = true; // H1予測ゾーンで分割エントリーする
input int split_entry_count = 3; // 分割エントリー本数(1..10)
input SplitLotMode split_lot_mode = SPLIT_LOT_FIXED; // 分割ロット配分モード
input double split_total_lot_size = 0.09; // 総量分割モードの合計ロット
input double split_fixed_lot_size = 0.01; // 固定ロットモードの1注文ロット
input bool cancel_old_split_pending_on_new_zone = true; // 新ゾーン読込時に旧分割pendingを取消
input int input_cancel_retry_cooldown_seconds = 60; // pending取消失敗時のticket単位再試行間隔
input bool use_m15_entry_filter = true; // M15確定足で発注タイミングを確認
input double m15_entry_zone_atr_multiplier = 1.50; // M15平均レンジ何本分まで候補価格への接近を許可
input bool use_m15_imbalance_confirmation = true; // T1/T3でM15初動確認を追加
input int m15_imbalance_avg_body_period = 20; // M15平均実体計算期間
input double m15_imbalance_sensitivity = 2.0; // M15初動検知感度
input double m15_imbalance_min_avg_body_points = 1.0; // 平均実体の最小値(point)
input bool use_m15_imbalance_debug_log = false; // M15初動確認ログ
input int input_entry_max_candidate_age_minutes = 120; // H1候補価格を新規発注に使う最大経過分数
input double input_tp_multiplier = 1.50; // TP distance multiplier (1.0=no expansion)
input int input_min_tp_points = 0; // Minimum TP distance in points (0=disabled)
input int input_max_tp_points = 0; // Maximum expanded TP distance in points (0=disabled)
input bool input_range_breakout_guard_enabled = true; // レンジ上抜け/下抜け時に逆張りLimitを退避
input ENUM_TIMEFRAMES input_range_breakout_guard_timeframe = PERIOD_M15; // レンジブレイク監視足
input int input_range_breakout_lookback_bars = 20; // レンジ高値安値の参照本数
input double input_range_breakout_max_width_multiplier = 6.0; // 平均レンジに対する最大レンジ幅
input double input_range_breakout_buffer_multiplier = 0.20; // ブレイク確定バッファ倍率
input double input_range_breakout_near_multiplier = 0.50; // ブレイク警戒の境界接近幅倍率
input double input_range_breakout_body_multiplier = 1.50; // 勢い判定の平均実体倍率
input int input_range_breakout_cooldown_bars = 4; // 逆張りLimit停止を継続する足数
// Existing shared HIT include files read these runtime globals.
int magic_number = 10001;
ulong slippage = 10;
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 2 // H1本数(=2時間)
#define HISTORY_BARS 72
#define OHLC_START_SHIFT 1 // 1: 確定足のみをPythonへ渡す
#define M15_CONFIRM_BARS 30
#define M15_MIN_ENTRY_ZONE_POINTS 10
#define M15_MIN_BODY_RATIO 0.25
#define M15_REJECTION_WICK_RATIO 0.35
#define ENTRY_RETRY_SECONDS 60
#define ENTRY_RETRY_LIMIT 10
// ANALYZE_TIMEFRAME は削除(H4/H1 両方使うため定数ではなく直接指定)
#define TARGET_SIZE 13
#define TARGET_ZONE_SCHEMA_VERSION 2
#define DEFAULT_TARGET_PRICE 0.0
#define PYTHON_TIMEOUT_SECONDS 600 // Python完了待ちの上限(秒)
#define MARKET_LOW_VOL_RANGE 0
#define MARKET_HIGH_VOL_RANGE 1
#define MARKET_LOW_VOL_UP 2
#define MARKET_HIGH_VOL_UP 3
#define MARKET_LOW_VOL_DOWN 4
#define MARKET_HIGH_VOL_DOWN 5
#define MARKET_TECHNICAL_ERROR_STOP 6
#define RANGE_BREAKOUT_NONE 0
#define RANGE_BREAKOUT_UP 1
#define RANGE_BREAKOUT_DOWN -1
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| EAの状態をまとめる構造体
//+------------------------------------------------------------------+
struct EAState
{
int trend_state; // H4 market_state0..5、6は技術エラー停止)を格納する変数
int res_chk; // GPTの回答が正しく取得できたか
double en_price[5]; // エントリー基準価格
double tp_price[5]; // 利益確定価格
double sl_price[5]; // ロスカット基準価格
int zone_res_chk; // 予測ゾーンが正しく取得できたか
string zone_candidate_id; // H1確定足由来の候補ID
double zone_low[5]; // 分割エントリー用ゾーン下限
double zone_high[5]; // 分割エントリー用ゾーン上限
double zone_tp[5]; // 分割エントリー用TP
double zone_sl[5]; // 分割エントリー用SL
bool load_trend_flg; // ★変更:トレンドを更新するタイミングか
bool load_target_flg; // ★変更:ターゲット価格を更新するタイミングか
int chk_cnt; // エントリー判定の試行回数
datetime last_trend_update; // ★追加:前回トレンドを更新した時刻
datetime last_target_update; // ★変更:前回ターゲット価格を更新した時刻
datetime target_loaded_at; // H1候補価格をEAへ読み込んだサーバー時刻
datetime target_candidate_at; // H1候補価格の元になった確定足時刻
datetime last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// Startup refresh flags for H4 trend and H1 entry candidates.
// Keep them separate to prevent repeated startup launches.
bool g_init_trend_pending = false;
bool g_init_entry_pending = false;
// H4/H1のバー更新状態
int g_pre_bars_H4 = 0;
int g_pre_bars_H1 = 0;
int g_pre_bars_M15 = 0;
bool g_bars_H1_check = false;
bool g_bars_M15_check = false;
// pending取消失敗時の過剰な取引サーバー要求を防止する。
ulong g_cancel_retry_tickets[];
datetime g_cancel_retry_at[];
// Range-breakout guard state for adverse reversal Limit orders.
int g_range_breakout_guard_direction = RANGE_BREAKOUT_NONE;
datetime g_range_breakout_guard_until = 0;
// ティックごとの価格情報
struct TickContext
{
double ask;
double bid;
double spread;
double spread_points;
int digits;
};
// 外部Pythonプロセスの状態
struct ExternalProcessState
{
long handle;
uint process_id;
bool active;
bool exit_code_ready;
uint exit_code;
datetime started_at;
};
ExternalProcessState g_trend_process;
ExternalProcessState g_entry_process;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Python連携パスをMT5データフォルダから組み立てる関数
//+------------------------------------------------------------------+
/**
* @brief MT5データフォルダ配下のMQL5ルートを返します。
*/
string MQL5RootPath()
{
return TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5";
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief EAと同じMQL5ツリー内にあるPython補助アプリのルートを返します。
*/
string PythonAppDir()
{
return MQL5RootPath() + "\\python_for_ea";
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief Python補助アプリ配下のbatファイル絶対パスを返します。
*/
string PythonBatchPath(const string filename)
{
return PythonAppDir() + "\\bat\\" + filename;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief ファイル名に使える安全なトークン文字か判定します。
*/
bool IsSafeFileTokenChar(const int ch)
{
return ((ch >= 48 && ch <= 57) || // 0-9
(ch >= 65 && ch <= 90) || // A-Z
(ch >= 97 && ch <= 122) || // a-z
ch == 95 || ch == 45); // _ or -
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief シンボル名などをファイル名向けの安全なトークンへ変換します。
*/
string SanitizeFileToken(const string value)
{
string result = "";
int length = StringLen(value);
for(int i = 0; i < length; i++)
{
int ch = StringGetCharacter(value, i);
if(IsSafeFileTokenChar(ch))
result += StringSubstr(value, i, 1);
else
result += "_";
}
if(result == "")
return "UNKNOWN";
return result;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief 同一Terminal内の複数EA/複数シンボルで連携ファイルが衝突しない接頭辞を返します。
*/
string BuildMT5FilePrefix()
{
return "HIT_" + SanitizeFileToken(_Symbol) + "_" + IntegerToString(magic_number);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief Python連携ファイル名へEA固有の接頭辞を付けます。
*/
string PrefixedMT5FileName(const string base_name)
{
if(mt5_file_prefix == "")
return base_name;
return mt5_file_prefix + "_" + base_name;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief Python連携で使用するMT5 Files配下のファイル名を初期化します。
*/
void ConfigurePythonGatewayFileNames()
{
mt5_file_prefix = BuildMT5FilePrefix();
ohlc_h4_file = PrefixedMT5FileName("ohlc_H4.csv");
ohlc_h1_file = PrefixedMT5FileName("ohlc_H1.csv");
done_trend_file = PrefixedMT5FileName("process_done_trend.txt");
done_entry_file = PrefixedMT5FileName("process_done_entry.txt");
trend_state_file = PrefixedMT5FileName("trend_state.txt");
target_prices_file = PrefixedMT5FileName("target_prices.txt");
target_zones_file = PrefixedMT5FileName("target_zones.txt");
running_trend_file = PrefixedMT5FileName("process_running_trend.txt");
running_entry_file = PrefixedMT5FileName("process_running_entry.txt");
Print("Python file prefix: ", mt5_file_prefix);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief Python連携で使用するbatパスを初期化します。
*/
void ConfigurePythonGatewayPaths()
{
ConfigurePythonGatewayFileNames();
python_app_dir = PythonAppDir();
get_trend_reply_bat = PythonBatchPath("get_trend_reply.bat");
get_entry_reply_bat = PythonBatchPath("get_entry_reply.bat");
Print("Python app dir: ", python_app_dir);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief The split entry/manager design requires ticket-based hedging positions.
*/
bool IsHedgingAccount()
{
return (AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
}
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、起動直後は安全側として
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4結果を再読込するまでは技術エラー停止値を維持し、古い連携結果での発注を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
magic_number = input_entry_magic_number;
slippage = input_slippage_points;
if(!IsHedgingAccount())
{
Print("HITEntryEA requires a retail hedging account. account_margin_mode=",
AccountInfoInteger(ACCOUNT_MARGIN_MODE));
return INIT_FAILED;
}
ConfigurePythonGatewayPaths();
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// Always rebuild startup state before allowing new orders.
g_ea.trend_state = MARKET_TECHNICAL_ERROR_STOP;
g_ea.res_chk = 0;
g_ea.zone_res_chk = 0;
g_ea.zone_candidate_id = "0";
g_init_trend_pending = true;
g_init_entry_pending = true;
ArrayResize(g_cancel_retry_tickets, 0);
ArrayResize(g_cancel_retry_at, 0);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
// Clear chart status text left by Comment() when the EA is removed.
Comment("");
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| ティック毎の処理
//+------------------------------------------------------------------+
/**
* @brief ティック受信ごとにEA全体の処理を制御します。
*
* 処理順序は、ティック情報取得、期限切れ注文/ポジション管理、H4トレンド更新、
* H1エントリー価格更新、M15エントリータイミング更新、ステータス表示、新規注文判定の順です。
* 時間制限処理はPython完了待ちやスプレッド判定より前に実行します。
*/
void OnTick()
{
TickContext ctx;
if(!GetTickContext(ctx))
return;
// Expired pending orders are managed by the entry EA before any Python wait state.
ManageExpiredEntryOrders();
// Delete and pause adverse limit orders before breakout spikes can fill them.
ManageRangeBreakoutLimitGuard(ctx);
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// Read completed H1 results before spread gating so stale split orders and state updates are not delayed.
bool entry_result_ready = IsEntryResultReady();
if(entry_result_ready)
RefreshTargetPrices(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!entry_result_ready)
return;
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| Entry order lifecycle
//+------------------------------------------------------------------+
void ManageExpiredEntryOrders()
{
CleanupPendingOrderCancelAttempts();
if(OrdersTotal() > 0)
CancelExpiredOrders();
}
// Function implementations are kept in project headers so the EA entry points stay compact.
#include <MyLib/Common/HITRuntimeController.mqh>
#include <MyLib/Signals/HITEntrySignal.mqh>
#include <MyLib/Common/HITExternalProcess.mqh>
#include <MyLib/Signals/HITPythonSignalGateway.mqh>
#include <MyLib/Trading/HITTradeManager.mqh>