13 Commits

Author SHA1 Message Date
Hiroaki86 52067c1973 Harden HIT entry and manager state handling 2026-06-14 22:02:17 +09:00
Hiroaki86 7b11d44d66 Refactor EA codebase and update project docs 2026-06-13 23:22:06 +09:00
Hiroaki86 f9012e19e2 Expand API-derived TP distances 2026-06-13 11:58:49 +09:00
Hiroaki86 4de3d24dcd Add range breakout guard for reverse limit orders 2026-06-13 11:27:10 +09:00
Hiroaki86 2532d416fa EAを注文用とパネル管理用に分離 2026-06-07 22:24:58 +09:00
Hiroaki86 b7789d4572 Split HIT EA into entry and position manager 2026-06-07 22:06:30 +09:00
HiroK f49769dc1e Merge pull request #4 from HirorihirorihK/codex/20260606_gpt_model_change
Codex/20260606 gpt model change
2026-06-06 20:47:43 +09:00
Hiroaki86 e3fb298b82 Update GPT settings for gpt-5.5 2026-06-06 20:46:28 +09:00
Hiroaki86 cc17fe2e04 Remove obsolete refactor artifacts and update specs 2026-06-06 14:45:43 +09:00
Hiroaki86 bdecd2d074 Add ver5 compatibility for shared headers 2026-06-06 14:44:28 +09:00
HiroK 35dc842839 Merge pull request #3 from HirorihirorihK/codex/20260606_refactor
Restore entry response schema version constant
2026-06-06 14:18:13 +09:00
Hiroaki86 286804ece3 Restore entry response schema version constant 2026-06-06 14:16:31 +09:00
HiroK 14cc991ab3 Merge pull request #2 from HirorihirorihK/codex/20260530_api
Codex/20260530 api
2026-06-04 20:12:20 +09:00
42 changed files with 1832 additions and 13322 deletions
-935
View File
@@ -1,935 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.00"
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
#import "Shell32.dll"
int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
#import
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 20; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#define HISTORY_BARS 72
// ANALYZE_TIMEFRAME は削除(H4/H1 両方使うため定数ではなく直接指定)
#define TARGET_SIZE 13
#define DEFAULT_TARGET_PRICE 0.0
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| EAの状態をまとめる構造体
//+------------------------------------------------------------------+
struct EAState
{
int trend_state; // ★追加:トレンドを格納する変数
int res_chk; // GPTの回答が正しく取得できたか
double en_price[5]; // エントリー基準価格
double tp_price[5]; // 利益確定価格
double sl_price[5]; // ロスカット基準価格
bool load_trend_flg; // ★変更:トレンドを更新するタイミングか
bool load_target_flg; // ★変更:ターゲット価格を更新するタイミングか
int chk_cnt; // エントリー判定の試行回数
datetime last_trend_update; // ★追加:前回トレンドを更新した時刻
datetime last_target_update; // ★変更:前回ターゲット価格を更新した時刻
datetime last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 必要な関数プロトタイプ宣言
//| プロセス完了ファイル("process_done.txt")関連
//+------------------------------------------------------------------+
// "process_done.txt"を作成する関数
void CreateDoneFile(const string name);
// "process_done.txt"を削除する関数
void DeleteDoneFile(const string name);
// "process_done.txt"の存在を確認し、存在する場合はtrueを返す関数
bool CheckDoneFile(const string name);
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 必要な関数プロトタイプ宣言
//| OHLCデータの取得からターゲット価格の取得まで
//+------------------------------------------------------------------+
//| 最新のOHLCデータを取得する関数
bool GetLatestOHLC(datetime &times[], double &open_prices[], double &high_prices[], double &low_prices[], double &close_prices[], ENUM_TIMEFRAMES tf, int bars_count);
//| "ohlc.csv"を出力する関数
void RecordOHLC(const string filename, const datetime &times[], const double &open_prices[], const double &high_prices[], const double &low_prices[], const double &close_prices[]);
//| バッチファイル(Pythonスクリプト)を実行する関数
void ExecuteBatchTrend();
void ExecuteBatchEntry();
// | "ohlc.csv"を出力後、バッチファイルの実行する関数
bool RecordOHLCAndExecuteBatch_Trend(EAState &state);
bool RecordOHLCAndExecuteBatch_Entry(EAState &state);
//| ターゲット価格を取得する関数
bool GetTargetPrices(EAState &state);
//| "target_prices.txt"を読み込む関数
void LoadTargetPrices(double &target_prices[]);
//| "xxx"を読み込む関数
bool GetTrendState(EAState &state);
void LoadTrendState(int &trend_state);
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 必要な関数プロトタイプ宣言
//| エントリー注文関連
//+------------------------------------------------------------------+
//| エントリー注文を送信する関数 (1:buy-stop, 2:buy-limit, 3:sell-stop, 4:sell-limit)
bool SendOrder(int orderType, double price, double tp, double sl);
//| 注文の執行ポリシーを取得する関数
ENUM_ORDER_TYPE_FILLING GetOrderFillingPolicy(string symbol);
//| 注文コメント(ローカル時刻)を生成する関数(先頭に orderType を付与)
string RequestComment(int orderType);
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 必要な関数プロトタイプ宣言
//| 時間制限処理関連
//+------------------------------------------------------------------+
//| 時間が経過したエントリー注文をキャンセルする関数
bool CancelExpiredOrders();
//| 時間が経過したポジションをクローズする関数
bool CloseExpiredPositions();
//| 指定された時間に最も近いバーのインデックスを返す関数
// int GetBarIndexByTime(datetime time, int default_index = 0);
int GetBarShiftByTime(datetime time, ENUM_TIMEFRAMES tf, int default_shift = -1);
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
void OnInit()
// プロセス完了ファイル("process_done.txt)"を作成
{
CreateDoneFile(done_trend_file);
CreateDoneFile(done_entry_file);
}
//+------------------------------------------------------------------+
//| ティック毎の処理
//+------------------------------------------------------------------+
void OnTick()
{
static int init_order_flg = initial_order;
// ─────────────────────────────────────────────
// (1) H4更新検知:トレンド用
// ─────────────────────────────────────────────
int current_bars_H4 = iBars(NULL, PERIOD_H4);
static int pre_bars_H4 = current_bars_H4;
int bars_H4_change = current_bars_H4 - pre_bars_H4;
if(bars_H4_change > 0 || init_order_flg == 1)
{
RecordOHLCAndExecuteBatch_Trend(g_ea);
}
pre_bars_H4 = current_bars_H4;
// trend done待ち(なければ以降をスキップ)
if(!CheckDoneFile(done_trend_file))
return;
// トレンド取得
GetTrendState(g_ea);
// ─────────────────────────────────────────────
// (2) H1更新検知:エントリー用
// ─────────────────────────────────────────────
static bool bars_H1_check = false;
int current_bars_H1 = iBars(NULL, PERIOD_H1);
static int pre_bars_H1 = current_bars_H1;
int bars_H1_change = current_bars_H1 - pre_bars_H1;
if(bars_H1_change > 0 || init_order_flg == 1)
{
init_order_flg = 0;
bars_H1_check = true;
g_ea.chk_cnt = 0;
g_ea.last_chk = 0;
RecordOHLCAndExecuteBatch_Entry(g_ea);
}
pre_bars_H1 = current_bars_H1;
// ── ティック情報・コメント表示 ──
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick))
{
return;
}
double Ask = last_tick.ask;
double Bid = last_tick.bid;
double Spread = MathRound((Ask - Bid) / Point()) * Point();
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
string message = StringFormat(
" \nAsk: %." + IntegerToString(digits) + "f"
"\nBid: %." + IntegerToString(digits) + "f"
"\nSpread: %." + IntegerToString(digits) + "f"
"\n\nLast Trend Update:\n %s"
"\n\nTrend State: %.0f" // ★追加
"\n\nLast Target Update:\n %s"
"\n\n[T1 Buy Stop ] en: %." + IntegerToString(digits) + "f tp: %." + IntegerToString(digits) + "f sl: %." + IntegerToString(digits) + "f"
"\n[T2 Buy Limit] en: %." + IntegerToString(digits) + "f tp: %." + IntegerToString(digits) + "f sl: %." + IntegerToString(digits) + "f"
"\n[T3 Sell Stop] en: %." + IntegerToString(digits) + "f tp: %." + IntegerToString(digits) + "f sl: %." + IntegerToString(digits) + "f"
"\n[T4 SellLimit] en: %." + IntegerToString(digits) + "f tp: %." + IntegerToString(digits) + "f sl: %." + IntegerToString(digits) + "f\n",
Ask, Bid, Spread,
TimeToString(g_ea.last_trend_update, TIME_DATE | TIME_MINUTES),
g_ea.trend_state, // ★追加
TimeToString(g_ea.last_target_update, TIME_DATE | TIME_MINUTES),
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]
);
Comment(message);
// スプレッド制限
if(Spread > spread_limit * Point())
return;
// entry done待ち
if(!CheckDoneFile(done_entry_file))
return;
// ターゲット取得
GetTargetPrices(g_ea);
// 期限切れ注文キャンセル・ポジクローズ
if(OrdersTotal() > 0)
CancelExpiredOrders();
if(PositionsTotal() > 0)
CloseExpiredPositions();
// ─────────────────────────────────────────────
// エントリー条件判定&注文送信(H1更新タイミングで1回)
// ─────────────────────────────────────────────
// if(bars_H4_check && TimeCurrent() - g_ea.last_chk >= 60)
// {
// bars_H4_check = false;
// g_ea.last_chk = TimeCurrent();
if(bars_H1_check && TimeCurrent() - g_ea.last_chk >= 60)
{
bars_H1_check = false;
g_ea.last_chk = TimeCurrent();
if(g_ea.res_chk == 0)
{
Print("[Entry Skip] res_chk=0 (range). No entry orders are sent.");
g_ea.chk_cnt = 0;
}
else
{
int used = OrdersTotal() + PositionsTotal();
if(used >= POSITION_LIMIT)
{
Print("Position limit exceeded: used=", used, " limit=", POSITION_LIMIT);
}
else
{
int sent_success = 0; // ★ 送信成功数(= SendOrder true の回数)
// ★ 1..4 を順に判定して、条件OKなら送る
for(int t=1; t<=4; t++)
{
// ポジション上限チェック(途中で到達したら打ち切り)
used = OrdersTotal() + PositionsTotal();
if(used >= POSITION_LIMIT)
{
Print("Position limit reached while sending. used=", used, " limit=", POSITION_LIMIT);
break;
}
string entry_type;
double cur_price; // ログ用(Ask/Bid
if(t==1)
{
entry_type="Buy Stop";
cur_price=Ask;
}
else
if(t==2)
{
entry_type="Buy Limit";
cur_price=Ask;
}
else
if(t==3)
{
entry_type="Sell Stop";
cur_price=Bid;
}
else
{
entry_type="Sell Limit"; // t==4
cur_price=Bid;
}
double en = g_ea.en_price[t];
double tp = g_ea.tp_price[t];
double sl = g_ea.sl_price[t];
bool ok=false;
if(t==1)
ok = (Ask < en && tp > en && sl < en); // buy-stop
else
if(t==2)
ok = (Ask > en && tp > en && sl < en); // buy-limit
else
if(t==3)
ok = (Bid > en && tp < en && sl > en); // sell-stop
else
ok = (Bid < en && tp < en && sl > en); // sell-limit
if(ok)
{
Print("[", entry_type, " Order Try at ", cur_price, "] en=", en, " tp=", tp, " sl=", sl);
if(SendOrder(t, en, tp, sl))
{
sent_success++;
Print("[", entry_type, " Order Sent] ticket ok. en=", en, " tp=", tp, " sl=", sl);
}
else
{
Print("[", entry_type, " Order Failed] en=", en, " tp=", tp, " sl=", sl);
}
}
else
{
Print("[No ", entry_type, "] cur=", cur_price, " en=", en, " tp=", tp, " sl=", sl);
}
}
if(sent_success > 0)
{
g_ea.chk_cnt = 0; // 1件でも通ったら終了
}
else
{
g_ea.chk_cnt += 1;
Print("[Retry] no order sent. chk_cnt=", g_ea.chk_cnt, "/10");
if(g_ea.chk_cnt < 10)
{
// bars_H4_check = true; // 60秒後に再度このブロックに入る
bars_H1_check = true; // ★ GPT-EAの bars_H4_check → bars_H1_check に変更
}
else
{
Print("[Retry End] reached max tries. reset chk_cnt.");
g_ea.chk_cnt = 0;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| "process_done.txt"を作成する関数
//+------------------------------------------------------------------+
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"を削除する関数
//+------------------------------------------------------------------+
void DeleteDoneFile(const string name)
{
if(FileIsExist(name))
FileDelete(name);
}
//+------------------------------------------------------------------+
//| "process_done.txt"の存在を確認し、存在する場合はtrueを返す関数
//+------------------------------------------------------------------+
bool CheckDoneFile(const string name)
{
return FileIsExist(name);
}
//+------------------------------------------------------------------+
//| 最新のOHLCデータを取得する関数
//+------------------------------------------------------------------+
bool GetLatestOHLC(datetime &times[], double &open_prices[], double &high_prices[], double &low_prices[], double &close_prices[], ENUM_TIMEFRAMES tf, int bars_count)
{
ArrayResize(times, 0);
ArrayResize(open_prices, 0);
ArrayResize(high_prices, 0);
ArrayResize(low_prices, 0);
ArrayResize(close_prices, 0);
if(bars_count < 2)
return false;
// 時間データを取得
if(CopyTime(_Symbol, tf, 0, bars_count, times) <= 0)
{
Print(__FUNCTION__, ": Failed to copy time data (bars=", bars_count, ") err=", GetLastError());
return false;
}
// OHLCデータを取得
MqlRates rates[];
if(CopyRates(_Symbol, tf, 0, bars_count, rates) <= 0)
{
Print(__FUNCTION__, ": Failed to copy rates data (bars=", bars_count, ") err=", GetLastError());
return false;
}
// 必要なデータを配列に格納
for(int i = 0; i < ArraySize(rates); i++)
{
ArrayResize(open_prices, i + 1);
ArrayResize(high_prices, i + 1);
ArrayResize(low_prices, i + 1);
ArrayResize(close_prices, i + 1);
open_prices[i] = rates[i].open;
high_prices[i] = rates[i].high;
low_prices[i] = rates[i].low;
close_prices[i] = rates[i].close;
}
return true;
}
//+------------------------------------------------------------------+
//| "ohlc.csv"を出力する関数
//+------------------------------------------------------------------+
void RecordOHLC(const string filename, const datetime &times[], const double &open_prices[], const double &high_prices[], const double &low_prices[], const double &close_prices[])
{
// 出力先ファイル名
// string filename = "ohlc.csv";
// ファイルを "書き込みモード" でオープン (テキスト/ANSI)
int fileHandle = FileOpen(filename, FILE_WRITE | FILE_TXT | FILE_ANSI);
// ファイルが開けたかチェック
if(fileHandle == INVALID_HANDLE)
{
Print(__FUNCTION__, " : Failed to open file: ", GetLastError());
return;
}
// 配列サイズを取得 (times, open, high, low, close の要素数は同じ前提)
int size = ArraySize(times);
// ヘッダー行を追加
FileWrite(fileHandle, "Time,Open,High,Low,Close");
// 小数点の桁数を取得
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
// フォーマット文字列を動的に生成
string formatString = StringFormat("%%s,%%.%df,%%.%df,%%.%df,%%.%df", digits, digits, digits, digits);
// 1行ずつ "時刻, Open, High, Low, Close" の形式で書き込み
for(int i = 0; i < size; i++)
{
// 時間をフォーマット
string timeStr = TimeToString(times[i], TIME_DATE | TIME_MINUTES);
// 1行分の文字列を生成 (小数点以下の桁数を `digits` に調整)
string line = StringFormat(formatString,
timeStr, open_prices[i], high_prices[i], low_prices[i], close_prices[i]);
// ファイルに書き込み (改行付き)
FileWrite(fileHandle, line);
}
// 書き込み終了後、ファイルを閉じる
FileClose(fileHandle);
}
//+------------------------------------------------------------------+
//| バッチファイル(Pythonスクリプト)を実行する関数
//+------------------------------------------------------------------+
void ExecuteBatchTrend()
{
if(CheckDoneFile(done_trend_file))
{
DeleteDoneFile(done_trend_file);
ShellExecuteW(0, "open", get_trend_reply_bat, "", "", 1);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ExecuteBatchEntry()
{
if(CheckDoneFile(done_entry_file))
{
DeleteDoneFile(done_entry_file);
ShellExecuteW(0, "open", get_entry_reply_bat, "", "", 1);
}
}
//+------------------------------------------------------------------+
//| "ohlc.csv"を出力後、バッチファイルの実行する関数
//+------------------------------------------------------------------+
bool RecordOHLCAndExecuteBatch_Trend(EAState &state)
{
datetime times[];
double open_prices[], high_prices[], low_prices[], close_prices[];
if(!GetLatestOHLC(times, open_prices, high_prices, low_prices, close_prices,
PERIOD_H4, HISTORY_BARS))
{ Print("GetLatestOHLC(H4) failed."); return false; }
RecordOHLC("ohlc_H4.csv", times, open_prices, high_prices, low_prices, close_prices);
ExecuteBatchTrend();
state.load_trend_flg = true;
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool RecordOHLCAndExecuteBatch_Entry(EAState &state)
{
datetime times[];
double open_prices[], high_prices[], low_prices[], close_prices[];
if(!GetLatestOHLC(times, open_prices, high_prices, low_prices, close_prices,
PERIOD_H1, HISTORY_BARS))
{ Print("GetLatestOHLC(H1) failed."); return false; }
RecordOHLC("ohlc_H1.csv", times, open_prices, high_prices, low_prices, close_prices);
ExecuteBatchEntry();
state.load_target_flg = true;
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool GetTrendState(EAState &state)
{
bool ProcessDone = CheckDoneFile(done_trend_file);
if(ProcessDone && g_ea.load_trend_flg)
{
int trend_state;
LoadTrendState(trend_state);
state.trend_state = trend_state;
Print("trend_state: ", state.trend_state);
g_ea.load_trend_flg = false;
g_ea.last_trend_update = TimeLocal();
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void LoadTrendState(int &trend_state)
{
string filename = "trend_state.txt";
if(FileIsExist(filename))
{
int filehandle = FileOpen(filename, FILE_READ | FILE_TXT);
if(filehandle != INVALID_HANDLE)
{
string line = FileReadString(filehandle);
trend_state = (int)StringToInteger(line);
FileClose(filehandle);
}
else
{ Print("Failed to open trend_state.txt"); trend_state = 0; }
}
else
{ Print("trend_state.txt not found"); trend_state = 0; }
}
//+------------------------------------------------------------------+
//| ターゲット価格を取得する関数
//+------------------------------------------------------------------+
bool GetTargetPrices(EAState &state)
{
bool ProcessDone = CheckDoneFile(done_entry_file); // ←変更
if(ProcessDone && g_ea.load_target_flg) // ←変更
{
double target_prices[];
LoadTargetPrices(target_prices);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
// 先頭は res_chk
state.res_chk = (int)target_prices[0];
// ★ 1..4 の (en,tp,sl) を読む:合計12個
for(int t=1; t<=4; t++)
{
int base = 1 + (t-1)*3; // 1,4,7,10
state.en_price[t] = NormalizeDouble(target_prices[base + 0], digits);
state.tp_price[t] = NormalizeDouble(target_prices[base + 1], digits);
state.sl_price[t] = NormalizeDouble(target_prices[base + 2], digits);
}
// ログ(任意)
Print("target_prices: res=", state.res_chk,
" | T1 en=", state.en_price[1], " tp=", state.tp_price[1], " sl=", state.sl_price[1],
" | T2 en=", state.en_price[2], " tp=", state.tp_price[2], " sl=", state.sl_price[2],
" | T3 en=", state.en_price[3], " tp=", state.tp_price[3], " sl=", state.sl_price[3],
" | T4 en=", state.en_price[4], " tp=", state.tp_price[4], " sl=", state.sl_price[4]);
g_ea.load_target_flg = false; // ←変更
g_ea.last_target_update = TimeLocal(); // ←変更
}
return true;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| "target_prices.txt"を読み込む関数
//+------------------------------------------------------------------+
void LoadTargetPrices(double &target_prices[])
{
ArrayResize(target_prices, TARGET_SIZE);
string filename = "target_prices.txt";
if(FileIsExist(filename))
{
int filehandle = FileOpen(filename, FILE_READ | FILE_TXT);
if(filehandle != INVALID_HANDLE)
{
int i = 0;
while(!FileIsEnding(filehandle) && i < TARGET_SIZE)
{
string line = FileReadString(filehandle);
target_prices[i] = StringToDouble(line);
i++;
}
FileClose(filehandle);
}
else
{
Print("Failed to open file. Error code: ", GetLastError());
for(int i = 0; i < TARGET_SIZE; i++)
{
target_prices[i] = DEFAULT_TARGET_PRICE;
}
}
}
else
{
Print("File does not exist: ", filename);
for(int i = 0; i < TARGET_SIZE; i++)
{
target_prices[i] = DEFAULT_TARGET_PRICE;
}
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| エントリー注文を送信する関数 (1:buy-stop, 2:buy-limit, 3:sell-stop, 4:sell-limit)
//+------------------------------------------------------------------+
bool SendOrder(int orderType, double price, double tp, double sl)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.magic = magic_number;
request.symbol = _Symbol;
request.volume = lot_size;
request.deviation = slippage;
request.type_filling = GetOrderFillingPolicy(_Symbol);
request.price = NormalizeDouble(price, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
request.tp = NormalizeDouble(tp, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
request.sl = NormalizeDouble(sl, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
request.comment = RequestComment(orderType);
string orderTypeStr = "";
// 指値または逆指値注文
request.action = TRADE_ACTION_PENDING;
switch(orderType)
{
case 1: // 順張り買い (buy-stop)
request.type = ORDER_TYPE_BUY_STOP;
orderTypeStr = "buy-stop";
break;
case 2: // 逆張り買い (buy-limit)
request.type = ORDER_TYPE_BUY_LIMIT;
orderTypeStr = "buy-limit";
break;
case 3: // 順張り売り (sell-stop)
request.type = ORDER_TYPE_SELL_STOP;
orderTypeStr = "sell-stop";
break;
case 4: // 逆張り売り (sell-limit)
request.type = ORDER_TYPE_SELL_LIMIT;
orderTypeStr = "sell-limit";
break;
default: // 不正なorderType
Print(__FUNCTION__, ": invalid orderType=", orderType);
return false;
}
// 注文送信
if(!OrderSend(request, result))
{
Print(__FUNCTION__, ": OrderSend failed. err=", GetLastError());
return false;
}
// 注文成功確認
if(result.retcode == TRADE_RETCODE_PLACED ||
result.retcode == TRADE_RETCODE_DONE ||
result.retcode == TRADE_RETCODE_DONE_PARTIAL)
{
Print(request.comment + " - " + orderTypeStr + " order success (ticket=", result.order, ")");
return true;
}
else
{
Print(__FUNCTION__, ": OrderSend retcode=", result.retcode);
return false;
}
}
//+------------------------------------------------------------------+
//| 注文の執行ポリシーを取得する関数
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING GetOrderFillingPolicy(string symbol)
{
long fill_mode = SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
if((fill_mode & SYMBOL_FILLING_IOC) != 0)
return ORDER_FILLING_IOC;
if((fill_mode & SYMBOL_FILLING_FOK) != 0)
return ORDER_FILLING_FOK;
return ORDER_FILLING_RETURN;
}
//+------------------------------------------------------------------+
//| 注文コメント(ローカル時刻)を生成する関数(先頭に orderType を付与)
//+------------------------------------------------------------------+
string RequestComment(int orderType)
{
datetime localTime = TimeLocal();
string timeStr = TimeToString(localTime, TIME_DATE|TIME_MINUTES);
// 先頭に 1,2,3,4 を付ける(例: "1 | PC Time: 2026.02.14 19:05"
return IntegerToString(orderType) + " | PC Time: " + timeStr;
}
//+------------------------------------------------------------------+
//| 時間が経過したエントリー注文をキャンセルする関数
//+------------------------------------------------------------------+
bool CancelExpiredOrders()
{
bool result = false;
// 未決済注文の数を取得
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(OrderSelect(ticket)) // 注文を選択
{
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
if(OrderGetInteger(ORDER_MAGIC) != magic_number)
continue;
datetime open_time = (datetime)OrderGetInteger(ORDER_TIME_SETUP);
int open_shift = GetBarShiftByTime(open_time, PERIOD_H1, -1); // H1
if(open_shift < 0)
continue; // 時刻解決できないなら触らない
if(open_shift >= ENTRY_H1_LIMIT) // H1本数(=1本 ≒ 1時間)
{
MqlTradeRequest request = {};
MqlTradeResult trade_result = {};
request.action=TRADE_ACTION_REMOVE; // 取引操作タイプ
request.order = ticket; // 注文チケット
if(!OrderSend(request, trade_result)) // 削除リクエスト送信
{
Print("Failed to delete order. Ticket: ", ticket, " Error: ", GetLastError());
}
else
{
if(trade_result.retcode == TRADE_RETCODE_DONE)
{
Print("Order canceled successfully due to a time limit. Ticket: ", ticket);
result = true; // 削除成功フラグ
}
else
{
Print("Order cancellation failed. Ticket: ", ticket, " Retcode: ", trade_result.retcode);
}
}
}
}
}
return result;
}
//+------------------------------------------------------------------+
//| 時間が経過したポジションをクローズする関数
//+------------------------------------------------------------------+
bool CloseExpiredPositions()
{
bool result = false;
// ポジションを逆順でループ
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
string position_symbol = PositionGetSymbol(i);
if(position_symbol!="")
{
// Magic Number とシンボルでフィルタリング
if(position_symbol != _Symbol)
continue;
if(PositionGetInteger(POSITION_MAGIC) == magic_number)
{
// ポジションのエントリー時刻を取得
datetime entry_time = (datetime)PositionGetInteger(POSITION_TIME);
int entry_shift = GetBarShiftByTime(entry_time, PERIOD_H1, -1);
if(entry_shift < 0)
continue;
if(entry_shift >= CLOSE_H1_LIMIT) // H1本数(=12本 ≒ 12時間)
{
MqlTradeRequest request = {};
MqlTradeResult trade_result = {};
// ポジションタイプに応じてリクエストを設定
int position_type = (int)PositionGetInteger(POSITION_TYPE);
request.action = TRADE_ACTION_DEAL;
request.position = PositionGetInteger(POSITION_TICKET); // ポジションのチケット番号
request.symbol = position_symbol; // シンボル
request.volume = PositionGetDouble(POSITION_VOLUME); // ポジションサイズ
request.price = (position_type == POSITION_TYPE_BUY)
? SymbolInfoDouble(position_symbol, SYMBOL_BID) // BUYの場合はBIDでクローズ
: SymbolInfoDouble(position_symbol, SYMBOL_ASK); // SELLの場合はASKでクローズ
request.deviation = slippage;
request.type = (position_type == POSITION_TYPE_BUY)
? ORDER_TYPE_SELL // BUYポジションをSELLでクローズ
: ORDER_TYPE_BUY; // SELLポジションをBUYでクローズ
request.type_filling = GetOrderFillingPolicy(position_symbol); // 注文執行ポリシー
request.magic = magic_number;
// 注文送信
if(!OrderSend(request, trade_result))
{
Print("Failed to close position. Ticket: ", request.position, " Error: ", GetLastError());
continue;
}
// 結果の確認
if(trade_result.retcode == TRADE_RETCODE_DONE || trade_result.retcode == TRADE_RETCODE_DONE_PARTIAL)
{
Print("Position closed successfully due to a time limit. Ticket: ", request.position);
result = true; // 少なくとも1つ成功した場合
}
else
{
Print("Failed to close position. Ticket: ", request.position, " Retcode: ", trade_result.retcode);
}
}
}
}
else
{
Print("Failed to select position at index ", i, ". Error: ", GetLastError());
}
}
return result;
}
//+------------------------------------------------------------------+
//| 指定された時間に最も近いバーのインデックスを返す関数
//+------------------------------------------------------------------+
int GetBarShiftByTime(datetime time, ENUM_TIMEFRAMES tf, int default_shift = -1)
{
if(time <= 0)
return default_shift;
int shift = iBarShift(_Symbol, tf, time, false); // 近傍バーで解決する
if(shift < 0)
return default_shift;
return shift;
}
//+------------------------------------------------------------------+
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-441
View File
@@ -1,441 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
string trend_state_file = "trend_state.txt";
string target_prices_file = "target_prices.txt";
// Python実行中を判定するための管理ファイル
string running_trend_file = "process_running_trend.txt";
string running_entry_file = "process_running_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
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 = 45; // H1候補価格を新規発注に使う最大経過分数
input bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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 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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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]; // ロスカット基準価格
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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,440 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
string trend_state_file = "trend_state.txt";
string target_prices_file = "target_prices.txt";
// Python実行中を判定するための管理ファイル
string running_trend_file = "process_running_trend.txt";
string running_entry_file = "process_running_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
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 bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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 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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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]; // ロスカット基準価格
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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,441 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
string trend_state_file = "trend_state.txt";
string target_prices_file = "target_prices.txt";
// Python実行中を判定するための管理ファイル
string running_trend_file = "process_running_trend.txt";
string running_entry_file = "process_running_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
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 = 45; // H1候補価格を新規発注に使う最大経過分数
input bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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 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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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]; // ロスカット基準価格
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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,441 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
string trend_state_file = "trend_state.txt";
string target_prices_file = "target_prices.txt";
// Python実行中を判定するための管理ファイル
string running_trend_file = "process_running_trend.txt";
string running_entry_file = "process_running_entry.txt";
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| インプットの設定
//+------------------------------------------------------------------+
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
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 = 45; // H1候補価格を新規発注に使う最大経過分数
input bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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 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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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]; // ロスカット基準価格
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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,462 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
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 magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
input int input_position_limit = 10; // 自EAの未約定注文+ポジション上限
input bool use_split_entry_zone = false; // H1予測ゾーンで分割エントリーする
input int split_entry_count = 3; // 分割エントリー本数(1..10)
input SplitLotMode split_lot_mode = SPLIT_LOT_TOTAL; // 分割ロット配分モード
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 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 = 45; // H1候補価格を新規発注に使う最大経過分数
input bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 1 // H1本数(=1時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,462 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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
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
// バッチファイルのパス
string get_trend_reply_bat = "C:\\ea_py\\bat\\get_trend_reply.bat"; // H4: trend
string get_entry_reply_bat = "C:\\ea_py\\bat\\get_entry_reply.bat"; // H1: entry
// プロセス完了ファイルの設定
string done_trend_file = "process_done_trend.txt";
string done_entry_file = "process_done_entry.txt";
// Python出力ファイル
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 magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
input int input_position_limit = 10; // 自EAの未約定注文+ポジション上限
input bool use_split_entry_zone = false; // H1予測ゾーンで分割エントリーする
input int split_entry_count = 3; // 分割エントリー本数(1..10)
input SplitLotMode split_lot_mode = SPLIT_LOT_TOTAL; // 分割ロット配分モード
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 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 bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 2 // H1本数(=2時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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 last_chk;
};
EAState g_ea; // EA全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
/**
* @brief EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,609 +0,0 @@
//+------------------------------------------------------------------+
//| HIT_EA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
//+------------------------------------------------------------------+
// 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 magic_number = 10001; // マジックナンバー
input int initial_order = 0; // 起動時の注文(0:なし, 1:あり)
input int input_position_limit = 10; // 自EAの未約定注文+ポジション上限
input bool use_split_entry_zone = false; // H1予測ゾーンで分割エントリーする
input int split_entry_count = 3; // 分割エントリー本数(1..10)
input SplitLotMode split_lot_mode = SPLIT_LOT_TOTAL; // 分割ロット配分モード
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 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 bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
//--- 主要な定数・設定
#define POSITION_LIMIT 48
#define ENTRY_H1_LIMIT 2 // H1本数(=2時間)
#define CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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全体の状態をここで管理
// initial_order 用の初回処理フラグ
// H4トレンド処理とH1エントリー処理で別々に管理し、初回起動時の再実行ループを防ぐ。
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;
// ティックごとの価格情報
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;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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 EA起動時の初期化処理を行います。
*
* Python連携用のdone/runningファイル状態を確認し、`initial_order` が有効な場合は
* H4トレンド判定とH1エントリー価格生成をそれぞれ初回実行対象にします。
* H4/H1の初回フラグを分けることで、起動直後にH4処理だけが繰り返される状態を防ぎます。
*/
int OnInit()
// プロセス完了ファイルと実行中ファイルの状態を初期化
{
ConfigurePythonGatewayPaths();
PrepareDoneFileOnInit(done_trend_file, running_trend_file, "trend");
PrepareDoneFileOnInit(done_entry_file, running_entry_file, "entry");
// initial_order=1 の場合でも、H4/H1を別々に1回だけ初回実行する。
g_init_trend_pending = (initial_order == 1);
g_init_entry_pending = (initial_order == 1);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 終了時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// 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;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
// H4新バーまたは初回起動時に、トレンド判定用Pythonを起動する。
ProcessTrendUpdate(g_ea);
// トレンド判定が完了していない場合、新規注文側の処理だけをスキップする。
if(!IsTrendResultReady())
return;
RefreshTrendState(g_ea);
// H1新バーまたは初回起動時に、エントリー価格生成用Pythonを起動する。
ProcessEntryUpdate(g_ea);
// M15確定足ごとに、H1候補価格を発注してよいタイミングか再判定する。
ProcessM15EntryTimingUpdate();
UpdateStatusComment(ctx);
// ここから下は新規注文に関する処理。
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// 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>
@@ -1,14 +1,11 @@
//+------------------------------------------------------------------+
//| HIT_EA.mq5 |
//| 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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
#property version "1.11"
//+------------------------------------------------------------------+
//| バッチファイル実行用の設定
@@ -99,7 +96,8 @@ enum SplitLotMode
//--- 入力パラメータ
input double lot_size = 0.01; // ロット数
input double spread_limit = 60; // 許容スプレッド(point)
input int magic_number = 10001; // マジックナンバー
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 = false; // H1予測ゾーンで分割エントリーする
@@ -117,29 +115,25 @@ 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 bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
ulong slippage = 10; // スリッページ
input double input_tp_multiplier = 1.25; // 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 CLOSE_H1_LIMIT 12 // H1本数(=12時間)
#define HISTORY_BARS 72
#define OHLC_START_SHIFT 1 // 1: 確定足のみをPythonへ渡す
#define M15_CONFIRM_BARS 30
@@ -162,6 +156,10 @@ ulong slippage = 10; // スリッページ
#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
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -209,6 +207,10 @@ bool g_bars_M15_check = false;
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
{
@@ -233,12 +235,6 @@ struct ExternalProcessState
ExternalProcessState g_trend_process;
ExternalProcessState g_entry_process;
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -378,6 +374,17 @@ void ConfigurePythonGatewayPaths()
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);
}
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
@@ -391,6 +398,16 @@ void ConfigurePythonGatewayPaths()
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");
@@ -406,9 +423,6 @@ int OnInit()
ArrayResize(g_cancel_retry_tickets, 0);
ArrayResize(g_cancel_retry_at, 0);
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
@@ -420,9 +434,6 @@ void OnDeinit(const int reason)
ResetExternalProcessState(g_trend_process);
ResetExternalProcessState(g_entry_process);
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
// Clear chart status text left by Comment() when the EA is removed.
Comment("");
ChartRedraw(0);
@@ -444,11 +455,11 @@ void OnTick()
if(!GetTickContext(ctx))
return;
// 期限切れ注文・ポジションは、Python待ちやスプレッド制限に関係なく先に処理する。
ManageExpiredTrades();
// Expired pending orders are managed by the entry EA before any Python wait state.
ManageExpiredEntryOrders();
// Existing-position protection stays active even while Python results are pending.
ManageSLTPPositions();
// Delete and pause adverse limit orders before breakout spikes can fill them.
ManageRangeBreakoutLimitGuard(ctx);
// 外部Pythonがdoneを返さない場合に、一定時間で待機状態を解除して再実行対象にする。
RecoverTimedOutPythonProcesses();
@@ -465,6 +476,11 @@ void OnTick()
// 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();
@@ -474,144 +490,23 @@ void OnTick()
if(!IsSpreadAllowed(ctx))
return;
if(!IsEntryResultReady())
if(!entry_result_ready)
return;
RefreshTargetPrices(g_ea);
ProcessEntryDecisionIfNeeded(g_ea, ctx);
}
//+------------------------------------------------------------------+
//| SLTP manager panel and settings bridge
//| Entry order lifecycle
//+------------------------------------------------------------------+
string SLTPBoolText(const bool value)
void ManageExpiredEntryOrders()
{
return value ? "ON" : "OFF";
CleanupPendingOrderCancelAttempts();
if(OrdersTotal() > 0)
CancelExpiredOrders();
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_settings_valid = false;
g_sltp_manager.SetMagicNumber((ulong)magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(slippage);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(!g_sltp_manager.ValidateSettings())
{
Print("HIT SLTP settings rejected. Manager is disabled until valid settings are applied.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit));
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT SLTP panel is unavailable. EA continues with input-based SLTP settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
void ManageSLTPPositions()
{
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT SLTP panel apply failed. Please review panel values.");
}
}
// Function implementations are kept in project headers so the EA entry points stay compact.
#include <MyLib/Common/HITRuntimeController.mqh>
#include <MyLib/Signals/HITEntrySignal.mqh>
+231
View File
@@ -0,0 +1,231 @@
//+------------------------------------------------------------------+
//| HITPositionManagerEA.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.01"
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Trading/HITPositionLifecycleManager.mqh>
#include <MyLib/Panel/SLTPManagerPanel.mqh>
// This EA never opens new positions. It manages hedging-account tickets created by HITEntryEA.
input int input_managed_magic_number = 10001; // 管理対象のマジックナンバー
input ulong input_slippage_points = 10; // 決済/SL変更の許容偏差(point)
input int input_position_close_after_h1_bars = 12; // 保有時間決済(H1本数、0で無効)
input bool input_sltp_manager_enabled = false; // UI連動SLTP管理を有効化
input bool input_sltp_show_panel = true; // SLTP操作パネルを表示
input bool input_sltp_use_breakeven = false; // 通常ブレークイーブンを有効化
input double input_sltp_breakeven_trigger_pips = 30.0; // 通常BE開始pips
input double input_sltp_breakeven_buffer_pips = 3.0; // 通常BE固定バッファpips
input bool input_sltp_use_elapsed_breakeven = false; // 保有時間BEを有効化
input double input_sltp_elapsed_breakeven_hours = 4.0; // 保有時間BE開始時間
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; // 保有時間BE固定バッファpips
input bool input_sltp_use_active_trailing = false; // アクティブトレーリングを有効化
input double input_sltp_active_breakeven_pips = 30.0; // アクティブ開始pips
input double input_sltp_active_stop_loss_offset_pips = 5.0; // 初期固定pips
input double input_sltp_active_step_trigger_pips = 10.0; // ステップ更新間隔pips
input double input_sltp_active_step_move_pips = 5.0; // 1ステップ固定追加pips
input bool input_sltp_use_tp_progress_stop = false; // TP進捗SLを有効化
input double input_sltp_tp_progress_trigger_percent = 70.0; // TP進捗SL開始率
input double input_sltp_tp_progress_sl_lock_percent = 30.0; // TP距離固定率
input bool input_sltp_use_high_volatility_limit = false; // 急変時SL引き締めを有効化
CSLTPManager g_sltp_manager;
CSLTPManagerPanel g_sltp_panel;
SLTPManagerPanelSettings g_sltp_settings;
CHITPositionLifecycleManager g_position_lifecycle;
bool g_sltp_settings_valid = false;
bool g_sltp_panel_created = false;
int EffectiveCloseAfterSeconds()
{
int close_bars = input_position_close_after_h1_bars;
if(close_bars < 0)
close_bars = 0;
if(close_bars > 240)
close_bars = 240;
const int h1_seconds = PeriodSeconds(PERIOD_H1);
if(close_bars <= 0 || h1_seconds <= 0)
return 0;
return close_bars * h1_seconds;
}
bool IsHedgingAccount()
{
return (AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
}
string SLTPBoolText(const bool value)
{
return value ? "ON" : "OFF";
}
void LoadSLTPInputSettings(SLTPManagerPanelSettings &settings)
{
settings.manager_enabled = input_sltp_manager_enabled;
settings.use_breakeven = input_sltp_use_breakeven;
settings.breakeven_trigger_pips = input_sltp_breakeven_trigger_pips;
settings.breakeven_buffer_pips = input_sltp_breakeven_buffer_pips;
settings.use_elapsed_breakeven = input_sltp_use_elapsed_breakeven;
settings.elapsed_breakeven_hours = input_sltp_elapsed_breakeven_hours;
settings.elapsed_breakeven_buffer_pips = input_sltp_elapsed_breakeven_buffer_pips;
settings.use_active_trailing = input_sltp_use_active_trailing;
settings.active_breakeven_pips = input_sltp_active_breakeven_pips;
settings.active_stop_loss_offset_pips = input_sltp_active_stop_loss_offset_pips;
settings.active_step_trigger_pips = input_sltp_active_step_trigger_pips;
settings.active_step_move_pips = input_sltp_active_step_move_pips;
settings.use_tp_progress_stop = input_sltp_use_tp_progress_stop;
settings.tp_progress_trigger_percent = input_sltp_tp_progress_trigger_percent;
settings.tp_progress_sl_lock_percent = input_sltp_tp_progress_sl_lock_percent;
settings.use_high_volatility_limit = input_sltp_use_high_volatility_limit;
}
bool ApplySLTPSettings(const SLTPManagerPanelSettings &settings,
const bool print_summary)
{
g_sltp_manager.SetMagicNumber((ulong)input_managed_magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(input_slippage_points);
g_sltp_manager.SetBreakevenSettings(settings.use_breakeven,
settings.breakeven_trigger_pips,
settings.breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(settings.use_elapsed_breakeven,
settings.elapsed_breakeven_hours,
settings.elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(settings.use_active_trailing,
settings.active_breakeven_pips,
settings.active_stop_loss_offset_pips,
settings.active_step_trigger_pips,
settings.active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(settings.use_tp_progress_stop,
settings.tp_progress_trigger_percent,
settings.tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(settings.use_high_volatility_limit);
if(settings.manager_enabled && !g_sltp_manager.ValidateSettings())
{
Print("HIT position manager SLTP settings rejected. Previous valid settings remain active.");
return false;
}
g_sltp_settings = settings;
g_sltp_settings_valid = true;
if(print_summary)
{
Print("HIT position manager SLTP settings: Manager=", SLTPBoolText(settings.manager_enabled),
" BE=", SLTPBoolText(settings.use_breakeven),
" ElapsedBE=", SLTPBoolText(settings.use_elapsed_breakeven),
" ActiveTrail=", SLTPBoolText(settings.use_active_trailing),
" TPProgress=", SLTPBoolText(settings.use_tp_progress_stop),
" HighVol=", SLTPBoolText(settings.use_high_volatility_limit),
" magic=", input_managed_magic_number);
}
return true;
}
bool InitializeSLTPManager()
{
SLTPManagerPanelSettings initial_settings;
LoadSLTPInputSettings(initial_settings);
if(!ApplySLTPSettings(initial_settings, true))
return false;
g_sltp_panel.Init(g_sltp_settings);
if(input_sltp_show_panel)
{
if(!g_sltp_panel.CreatePanel())
{
Print("HIT position manager SLTP panel is unavailable. EA continues with input-based settings.");
return true;
}
g_sltp_panel.SetInitialValues();
g_sltp_panel_created = true;
}
return true;
}
bool InitializePositionLifecycleManager()
{
return g_position_lifecycle.Init(_Symbol,
(ulong)input_managed_magic_number,
input_slippage_points,
EffectiveCloseAfterSeconds());
}
void ManageTargetPositions()
{
g_position_lifecycle.SetCloseAfterSeconds(EffectiveCloseAfterSeconds());
g_position_lifecycle.CloseExpiredPositions();
if(!g_sltp_settings_valid)
return;
if(!g_sltp_settings.manager_enabled)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
int OnInit()
{
if(!IsHedgingAccount())
{
Print("HITPositionManagerEA requires a retail hedging account. account_margin_mode=",
AccountInfoInteger(ACCOUNT_MARGIN_MODE));
return INIT_FAILED;
}
if(!InitializePositionLifecycleManager())
return INIT_FAILED;
if(!InitializeSLTPManager())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_sltp_panel_created)
g_sltp_panel.Destroy(reason);
Comment("");
ChartRedraw(0);
}
void OnTick()
{
ManageTargetPositions();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(!g_sltp_panel_created)
return;
SLTPManagerPanelSettings candidate_settings = g_sltp_settings;
if(g_sltp_panel.HandleChartEvent(id, lparam, dparam, sparam, candidate_settings))
{
if(!ApplySLTPSettings(candidate_settings, true))
Print("HIT position manager panel apply failed. Please review panel values.");
}
}
@@ -0,0 +1,243 @@
//+------------------------------------------------------------------+
//| HITPositionManagerTestHarness.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.00"
#include <Trade/Trade.mqh>
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Trading/HITPositionLifecycleManager.mqh>
// Strategy Tester harness only. It seeds positions, then calls the production managers.
input int input_managed_magic_number = 10001; // 管理対象magic
input int input_control_magic_number = 20002; // 非対象確認用magic
input ulong input_slippage_points = 10; // 許容偏差(point)
input double input_test_lot_size = 0.01; // 検証用ロット
input bool input_seed_managed_buy = true; // 管理対象BUYを作成
input bool input_seed_managed_sell = true; // 管理対象SELLを作成
input bool input_seed_control_position = true; // magic違いポジションを作成
input int input_position_close_after_h1_bars = 12; // 保有時間決済(H1本数、0で無効)
input bool input_sltp_manager_enabled = true; // SLTP管理を有効化
input bool input_sltp_use_breakeven = true; // 通常BEを有効化
input double input_sltp_breakeven_trigger_pips = 30.0;
input double input_sltp_breakeven_buffer_pips = 3.0;
input bool input_sltp_use_elapsed_breakeven = false;
input double input_sltp_elapsed_breakeven_hours = 4.0;
input double input_sltp_elapsed_breakeven_buffer_pips = 3.0;
input bool input_sltp_use_active_trailing = false;
input double input_sltp_active_breakeven_pips = 30.0;
input double input_sltp_active_stop_loss_offset_pips = 5.0;
input double input_sltp_active_step_trigger_pips = 10.0;
input double input_sltp_active_step_move_pips = 5.0;
input bool input_sltp_use_tp_progress_stop = false;
input double input_sltp_tp_progress_trigger_percent = 70.0;
input double input_sltp_tp_progress_sl_lock_percent = 30.0;
input bool input_sltp_use_high_volatility_limit = false;
CTrade g_test_trade;
CSLTPManager g_sltp_manager;
CHITPositionLifecycleManager g_position_lifecycle;
bool g_seed_done = false;
bool g_sltp_settings_valid = false;
int EffectiveCloseAfterSeconds()
{
int close_bars = input_position_close_after_h1_bars;
if(close_bars < 0)
close_bars = 0;
if(close_bars > 240)
close_bars = 240;
const int h1_seconds = PeriodSeconds(PERIOD_H1);
if(close_bars <= 0 || h1_seconds <= 0)
return 0;
return close_bars * h1_seconds;
}
bool IsHedgingAccount()
{
return (AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
}
double NormalizedVolume(const double volume)
{
const double min_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
const double max_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
const double step_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(min_volume <= 0.0 || max_volume <= 0.0 || step_volume <= 0.0)
return volume;
double normalized = volume;
if(normalized < min_volume)
normalized = min_volume;
if(normalized > max_volume)
normalized = max_volume;
const double steps = MathFloor((normalized - min_volume) / step_volume + 0.5);
normalized = min_volume + steps * step_volume;
int volume_digits = 0;
double step = step_volume;
while(step < 1.0 && volume_digits < 8)
{
step *= 10.0;
volume_digits++;
}
return NormalizeDouble(normalized, volume_digits);
}
bool ConfigureManagers()
{
if(!g_position_lifecycle.Init(_Symbol,
(ulong)input_managed_magic_number,
input_slippage_points,
EffectiveCloseAfterSeconds()))
return false;
g_sltp_manager.SetMagicNumber((ulong)input_managed_magic_number);
g_sltp_manager.SetSymbol(_Symbol);
g_sltp_manager.SetDeviationInPoints(input_slippage_points);
g_sltp_manager.SetBreakevenSettings(input_sltp_use_breakeven,
input_sltp_breakeven_trigger_pips,
input_sltp_breakeven_buffer_pips);
g_sltp_manager.SetElapsedBreakevenSettings(input_sltp_use_elapsed_breakeven,
input_sltp_elapsed_breakeven_hours,
input_sltp_elapsed_breakeven_buffer_pips);
g_sltp_manager.SetActiveTrailingSettings(input_sltp_use_active_trailing,
input_sltp_active_breakeven_pips,
input_sltp_active_stop_loss_offset_pips,
input_sltp_active_step_trigger_pips,
input_sltp_active_step_move_pips);
g_sltp_manager.SetTpProgressStopSettings(input_sltp_use_tp_progress_stop,
input_sltp_tp_progress_trigger_percent,
input_sltp_tp_progress_sl_lock_percent);
g_sltp_manager.SetHighVolatilityLimitSettings(input_sltp_use_high_volatility_limit);
g_sltp_settings_valid = g_sltp_manager.ValidateSettings();
return g_sltp_settings_valid;
}
bool SeedBuyPosition(const ulong magic,
const string comment)
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
{
Print("Harness tick read failed: err=", GetLastError());
return false;
}
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
const double sl = NormalizeDouble(tick.bid - 5000.0 * point, digits);
const double tp = NormalizeDouble(tick.bid + 10000.0 * point, digits);
g_test_trade.SetExpertMagicNumber(magic);
ResetLastError();
if(!g_test_trade.Buy(NormalizedVolume(input_test_lot_size), _Symbol, 0.0, sl, tp, comment))
{
Print("Harness BUY seed failed: magic=", magic,
" retcode=", g_test_trade.ResultRetcode(),
" err=", GetLastError());
return false;
}
Print("Harness BUY seed succeeded: magic=", magic,
" ticket=", g_test_trade.ResultOrder(),
" comment=", comment);
return true;
}
bool SeedSellPosition(const ulong magic,
const string comment)
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
{
Print("Harness tick read failed: err=", GetLastError());
return false;
}
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
const double sl = NormalizeDouble(tick.ask + 5000.0 * point, digits);
const double tp = NormalizeDouble(tick.ask - 10000.0 * point, digits);
g_test_trade.SetExpertMagicNumber(magic);
ResetLastError();
if(!g_test_trade.Sell(NormalizedVolume(input_test_lot_size), _Symbol, 0.0, sl, tp, comment))
{
Print("Harness SELL seed failed: magic=", magic,
" retcode=", g_test_trade.ResultRetcode(),
" err=", GetLastError());
return false;
}
Print("Harness SELL seed succeeded: magic=", magic,
" ticket=", g_test_trade.ResultOrder(),
" comment=", comment);
return true;
}
void SeedTestPositions()
{
if(g_seed_done)
return;
g_test_trade.SetDeviationInPoints((int)input_slippage_points);
g_test_trade.SetTypeFillingBySymbol(_Symbol);
if(input_seed_managed_buy)
SeedBuyPosition((ulong)input_managed_magic_number, "HIT PM TEST managed BUY");
if(input_seed_managed_sell)
SeedSellPosition((ulong)input_managed_magic_number, "HIT PM TEST managed SELL");
if(input_seed_control_position)
SeedBuyPosition((ulong)input_control_magic_number, "HIT PM TEST control magic");
g_seed_done = true;
}
void ManageTargetPositions()
{
g_position_lifecycle.SetCloseAfterSeconds(EffectiveCloseAfterSeconds());
g_position_lifecycle.CloseExpiredPositions();
if(!input_sltp_manager_enabled || !g_sltp_settings_valid)
return;
g_sltp_manager.ManagePositions();
g_sltp_manager.HighVolatilityLimit();
}
int OnInit()
{
if(!MQLInfoInteger(MQL_TESTER))
{
Print("HITPositionManagerTestHarness is for Strategy Tester only.");
return INIT_FAILED;
}
if(!IsHedgingAccount())
{
Print("HITPositionManagerTestHarness requires a retail hedging account. account_margin_mode=",
AccountInfoInteger(ACCOUNT_MARGIN_MODE));
return INIT_FAILED;
}
if(!ConfigureManagers())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
void OnTick()
{
SeedTestPositions();
ManageTargetPositions();
}
-395
View File
@@ -1,395 +0,0 @@
//+------------------------------------------------------------------+
//| TradingPanel EA entry point
//+------------------------------------------------------------------+
#include <MyLib/Trading/TradingPanelTradingManagers.mqh>
#include <MyLib/Panel/TradingPanelPanel.mqh>
#define POSITION_TYPE_ALL ((ENUM_POSITION_TYPE) - 1) // フィルタ無し
// トータルブレークイーブン
#define BE_BUY "BreakEvenBuy"
#define BE_SELL "BreakEvenSell"
//--- カテゴリ1:取引設定
input group "■ 取引設定";
input int exit_time_interval = 4; // 時間制限クローズまでの保有時間(時間)
//--- カテゴリ2:基本設定
input group "■ 基本設定";
input ulong slippage = 10; // スリッページ
input int magic_number = 10001; // マジックナンバー
input group "■ SLTP設定: Common";
input double take_profit_pips = 150.0; // TP設定値
input double default_sl = 100.0; // SL初期値
input double stop_offset_pips = 10.0; // ストップロスオフセット(pips)
input group "■ SLTP設定: TRAIL";
input bool enable_breakeven = true; // ブレークイーブンを有効化
input double breakeven_pips = 30.0; // ブレークイーブン判定利幅
input double stop_loss_offset_pips = 5.0; // 建値からのマージン
input double step_trigger_pips = 10.0; // トレール更新トリガーpips
input double step_move_pips = 8.0; // トレール更新pips
input double tp_edit_pips = 10.0; // TP更新初期pips
//--- カテゴリ4:フィルター設定
input group "■ レンジフィルター設定";
input double range_pips = 80.0; // レンジ幅pips1
//--- カテゴリ5:時間設定
input group "■ 取引時間設定";
input bool isTradingTimeEnabled = true; // 取引時間を制限
input int TradeStartHour = 4; // 開始時間GMT(時間)
input int TradeStartMin = 0; // 開始時間GMT(分)
input int TradeEndHour = 23; // 終了時間GMT(時間)
input int TradeEndMin = 0; // 終了時間GMT(分)
int TimerPeriod_sec = 1; // n 秒ごとに OnTimer
bool wasInTradingTime = false;
bool beforeTradingInitDone = false;
bool afterTradingInitDone = false;
bool timelimit_exit = true; // 時間制限クローズ 初期状態
bool input_lock = true; // 時間制限クローズ 初期状態
// --------------------------------------------------------------
// EAとの共有用グローバル変数
// --------------------------------------------------------------
int gl_exitTimeIntervalInSeconds = exit_time_interval * 3600;
double gl_breakeven_pips = breakeven_pips;
double gl_stop_loss_offset_pips = stop_loss_offset_pips;
double gl_step_trigger_pips = step_trigger_pips;
double gl_step_move_pips = step_move_pips;
double gl_default_sl_pips = default_sl;
double gl_take_profit_pips = take_profit_pips;
double gl_stop_offset_pips = stop_offset_pips;
int buy_position = 0; // CountPositions() で随時更新
int sell_position = 0;
datetime last_buy_time = 0;
datetime last_sell_time = 0;
bool buy_total_be_flg = false;
bool sell_total_be_flg = false;
//+------------------------------------------------------------------+
//| トータルのブレークイーブン用変数
//+------------------------------------------------------------------+
bool InpShowBuyLine = true; // 買いのブレークイーブンライン
bool InpShowSellLine = true; // 売りのブレークイーブンライン
double BufferPips = 2.0; // BE バッファ
int LabelShiftBars = 60; // 何本前のバーにラベルを置くか
double LabelOffsetPips = 4.0; // ライン ⇔ ラベルの距離
CTradingPanelStopManager g_stop_manager;
CTradingPanelPositionManager g_position_manager;
CTradingPanelBreakEvenLineManager g_break_even_manager;
CTradingPanelPanel g_panel;
//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
int OnInit()
{
g_stop_manager.Init(_Symbol, (ulong)magic_number, slippage);
g_position_manager.Init(_Symbol, (ulong)magic_number, slippage);
g_break_even_manager.Init(_Symbol, (ulong)magic_number);
g_panel.Init(exit_time_interval,
range_pips,
breakeven_pips,
stop_loss_offset_pips,
step_trigger_pips,
step_move_pips,
default_sl,
take_profit_pips,
tp_edit_pips,
stop_offset_pips);
// EA設定変更用のパネルを表示
if (!g_panel.CreatePanel(input_lock))
return INIT_FAILED;
g_panel.SetInitialValues();
// Backfill missing initial stops for existing matching positions.
g_position_manager.ApplyInitialStops(gl_default_sl_pips, gl_take_profit_pips);
// タイマーをセット
EventSetTimer(TimerPeriod_sec);
return (INIT_SUCCEEDED); // 成功コードを明示しておく
}
//+------------------------------------------------------------------+
//| 削除時の処理
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
// EA適用後のプロパティで変更すると、EAを再読み込みする。
g_panel.Destroy(reason);
// トータルブレークイーブンラインを削除
if (InpShowBuyLine)
g_break_even_manager.DeleteLine(BE_BUY);
if (InpShowSellLine)
g_break_even_manager.DeleteLine(BE_SELL);
// EAが削除された際にコメントを消す
Comment("");
}
//+------------------------------------------------------------------+
//| タイマー処理
//+------------------------------------------------------------------+
void OnTimer()
{
// 時間状態の変化を検知
HandleTradingTimeTransition();
// ティックが少ない時間帯でも時間制限クローズを監視する。
g_position_manager.CloseTimedPositions(POSITION_TYPE_BUY, ORDER_TYPE_SELL,
timelimit_exit, gl_exitTimeIntervalInSeconds, slippage);
g_position_manager.CloseTimedPositions(POSITION_TYPE_SELL, ORDER_TYPE_BUY,
timelimit_exit, gl_exitTimeIntervalInSeconds, slippage);
// 取引時間が指定時間内か確認
if (!IsTradingTime())
return;
}
//+------------------------------------------------------------------+
//| ティック毎の処理
//+------------------------------------------------------------------+
void OnTick()
{
// 時間状態の変化を検知
HandleTradingTimeTransition();
// EAの設定を画面上に表示
DisplayEAValues();
// 時間制限クローズは取引時間フィルターとは独立して実行する。
g_position_manager.CloseTimedPositions(POSITION_TYPE_BUY, ORDER_TYPE_SELL,
timelimit_exit, gl_exitTimeIntervalInSeconds, slippage);
g_position_manager.CloseTimedPositions(POSITION_TYPE_SELL, ORDER_TYPE_BUY,
timelimit_exit, gl_exitTimeIntervalInSeconds, slippage);
// 取引時間が指定時間内か確認
if (!IsTradingTime())
return;
// レート急伸SL
g_stop_manager.HighVolatilityLimit();
//------------------------------------
// ブレークイーブン / トレールストップ監視
//------------------------------------
g_stop_manager.ManageStops(enable_breakeven,
gl_breakeven_pips,
gl_stop_loss_offset_pips,
gl_step_trigger_pips,
gl_step_move_pips);
g_break_even_manager.ManageBuy(buy_total_be_flg && InpShowBuyLine,
buy_total_be_flg,
BufferPips,
LabelShiftBars,
LabelOffsetPips,
gl_default_sl_pips,
g_stop_manager);
g_break_even_manager.ManageSell(sell_total_be_flg && InpShowSellLine,
sell_total_be_flg,
BufferPips,
LabelShiftBars,
LabelOffsetPips,
gl_default_sl_pips,
g_stop_manager);
// 保有ポジションの確認
buy_position = g_position_manager.CountPositions(POSITION_TYPE_BUY, magic_number);
sell_position = g_position_manager.CountPositions(POSITION_TYPE_SELL, magic_number);
}
//+------------------------------------------------------------------+
//| 取引時間を指定する関数
//+------------------------------------------------------------------+
bool IsTradingTime()
{
// 取引時間が有効化されていない場合、常にtrueを返す
if (!isTradingTimeEnabled)
{
return true;
}
MqlDateTime structTime;
TimeCurrent(structTime);
structTime.sec = 0;
structTime.hour = TradeStartHour;
structTime.min = TradeStartMin;
datetime timeStart = StructToTime(structTime);
structTime.hour = TradeEndHour;
structTime.min = TradeEndMin;
datetime timeEnd = StructToTime(structTime);
// エラーチェック
if (TradeStartHour >= TradeEndHour && TradeStartMin >= TradeEndMin)
{
Print("Error: Invalid Time input");
return false;
}
// 現在の時間を取得
datetime now = TimeCurrent();
// 時間内かどうかをチェック
return (now >= timeStart && now < timeEnd);
}
//+------------------------------------------------------------------+
//| 取引時間内外に状態が変化した時の初期化処理
//+------------------------------------------------------------------+
void HandleTradingTimeTransition()
{
bool nowInTradingTime = IsTradingTime();
// --- 時間内に入った瞬間 ---
if (nowInTradingTime && !wasInTradingTime)
{
if (!beforeTradingInitDone)
{
Print("取引時間内 初期化処理");
beforeTradingInitDone = true;
}
afterTradingInitDone = false;
}
// --- 時間外に出た瞬間 ---
if (!nowInTradingTime && wasInTradingTime)
{
if (!afterTradingInitDone)
{
Print("取引時間外 初期化処理");
afterTradingInitDone = true;
}
beforeTradingInitDone = false;
}
wasInTradingTime = nowInTradingTime;
}
//+------------------------------------------------------------------+
//| pipsを価格に換算する関数
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
// 出力処理を関数にまとめる
//+------------------------------------------------------------------+
void DisplayEAValues()
{
Comment("\n",
"ブレークイーブン: ", (int)gl_breakeven_pips, "pips\n",
"時間制限クローズ: ", timelimit_exit ? "ON" : "OFF",
" | 保有時間: ", gl_exitTimeIntervalInSeconds / 3600, "時間\n",
"------------------------------------------------------------------------------------\n");
}
// +------------------------------------------------------------------+
// | チャートイベントのハンドラ
// +------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
ENUM_POSITION_TYPE tp_side = POSITION_TYPE_ALL;
double tp_adjust_pips = 0.0;
if (g_panel.HandleChartEvent(id,
lparam,
dparam,
sparam,
exit_time_interval,
range_pips,
breakeven_pips,
stop_loss_offset_pips,
step_trigger_pips,
step_move_pips,
default_sl,
take_profit_pips,
tp_edit_pips,
stop_offset_pips,
timelimit_exit,
input_lock,
buy_total_be_flg,
sell_total_be_flg,
gl_exitTimeIntervalInSeconds,
gl_breakeven_pips,
gl_stop_loss_offset_pips,
gl_step_trigger_pips,
gl_step_move_pips,
gl_default_sl_pips,
gl_take_profit_pips,
gl_stop_offset_pips,
tp_side,
tp_adjust_pips))
{
g_position_manager.AdjustTakeProfit(tp_adjust_pips, tp_side);
}
}
//------------------------------------------------------------------
// 取引イベントコールバック(再エントリーを防止)
//------------------------------------------------------------------
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
bool recalc_needed = false; // ポジション再集計フラグ
// 1) Deal が追加された(約定)
if (trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
recalc_needed = true;
//---- Deal 詳細を取得 --------------------------------------
if (HistoryDealSelect(trans.deal)) // Deal を選択
{
ENUM_DEAL_REASON deal_reason = (ENUM_DEAL_REASON)
HistoryDealGetInteger(trans.deal, DEAL_REASON);
ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)
HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
//==== ★ クローズ条件(手動 or SL/TP ===================
bool is_close =
(deal_entry == DEAL_ENTRY_OUT) &&
(deal_reason == DEAL_REASON_CLIENT || // 手動決済
deal_reason == DEAL_REASON_MOBILE || // スマホアプリ決済
deal_reason == DEAL_REASON_WEB || // WEB版決済
deal_reason == DEAL_REASON_EXPERT || // EAのタイマー決済
deal_reason == DEAL_REASON_SL || // SL ヒット(部分/全量とも)
deal_reason == DEAL_REASON_TP); // TP ヒット(部分/全量とも)
if (is_close)
{
Print("Position closed (", EnumToString(deal_reason), ") — EA waits for next prediction.");
}
}
}
// 2) サーバ側でポジション内容が変更 (数量減少・SL変更等)
else if (trans.type == TRADE_TRANSACTION_POSITION)
{
recalc_needed = true;
}
// 3) 必要に応じてポジション再集計
if (recalc_needed)
{
g_position_manager.CheckPositions(buy_position, sell_position, last_buy_time, last_sell_time);
g_position_manager.ApplyInitialStops(gl_default_sl_pips, gl_take_profit_pips);
}
}
+34 -2
View File
@@ -512,14 +512,46 @@ bool IsProcessResultReady(const string done_file, const string running_file, con
if(!FileIsExist(result_file))
{
Print("[", label, "] done exists but result file is missing: ", result_file);
return false;
Print("[", label, "] done exists but result file is missing: ", result_file);
DeleteDoneFile(done_file);
DeleteRunningFile(running_file);
ResetExternalProcessState(process);
MarkProcessRetryPending(label);
return false;
}
DeleteRunningFile(running_file);
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/**
* @brief Mark a missing-result Python run for immediate retry on the next tick.
*/
void MarkProcessRetryPending(const string label)
{
if(label == "trend")
{
g_ea.trend_state = MARKET_TECHNICAL_ERROR_STOP;
g_ea.load_trend_flg = false;
g_init_trend_pending = true;
return;
}
if(label == "entry")
{
g_ea.res_chk = 0;
g_ea.zone_res_chk = 0;
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開始可能状態を返す関数
//+------------------------------------------------------------------+
@@ -25,26 +25,6 @@ bool GetTickContext(TickContext &ctx)
return true;
}
//+------------------------------------------------------------------+
//| 期限切れ注文・期限切れポジションを処理する関数
//+------------------------------------------------------------------+
/**
* @brief 期限切れの未約定注文と保有ポジションを処理します。
*
* 未約定注文はENTRY_H1_LIMIT時間、保有ポジションはCLOSE_H1_LIMIT時間を基準に判定します。
* 新規注文条件とは独立して、OnTickの早い段階で実行される想定です。
*/
void ManageExpiredTrades()
{
CleanupPendingOrderCancelAttempts();
if(OrdersTotal() > 0)
CancelExpiredOrders();
if(PositionsTotal() > 0)
CloseExpiredPositions();
}
//+------------------------------------------------------------------+
//| H4更新検知とトレンド判定Python起動を処理する関数
//+------------------------------------------------------------------+
+180 -15
View File
@@ -1,6 +1,131 @@
#ifndef HIT_ENTRY_SIGNAL_MQH
#define HIT_ENTRY_SIGNAL_MQH
//+------------------------------------------------------------------+
//| TP expansion settings
//+------------------------------------------------------------------+
double EffectiveTpMultiplier()
{
double multiplier = input_tp_multiplier;
if(multiplier < 1.0)
multiplier = 1.0;
if(multiplier > 100.0)
multiplier = 100.0;
return multiplier;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double EffectiveMinTpPoints()
{
if(input_min_tp_points <= 0)
return 0.0;
return (double)input_min_tp_points;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double EffectiveMaxTpPoints()
{
if(input_max_tp_points <= 0)
return 0.0;
return (double)input_max_tp_points;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsBuyTargetOrderType(const int orderType)
{
return (orderType == 1 || orderType == 2);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsSellTargetOrderType(const int orderType)
{
return (orderType == 3 || orderType == 4);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsTakeProfitDirectionValid(const int orderType, const double en, const double tp)
{
if(IsBuyTargetOrderType(orderType))
return (tp > en);
if(IsSellTargetOrderType(orderType))
return (tp < en);
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double ExpandedTakeProfitPrice(const int orderType, const double en, const double api_tp)
{
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double normalized_api_tp = NormalizeDouble(api_tp, digits);
if(en <= 0.0 || api_tp <= 0.0)
return normalized_api_tp;
if(!IsTakeProfitDirectionValid(orderType, en, api_tp))
{
Print("[TP Adjust Skip] invalid API TP direction. orderType=", orderType,
" en=", en, " api_tp=", api_tp);
return normalized_api_tp;
}
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0.0)
point = Point();
if(point <= 0.0)
return normalized_api_tp;
double base_points = MathAbs(api_tp - en) / point;
if(base_points <= 0.0)
return normalized_api_tp;
double final_points = base_points * EffectiveTpMultiplier();
double min_points = EffectiveMinTpPoints();
double max_points = EffectiveMaxTpPoints();
if(min_points > 0.0 && final_points < min_points)
final_points = min_points;
if(max_points > 0.0 && final_points > max_points)
final_points = max_points;
// Never make the final target closer than the API-provided TP.
if(final_points < base_points)
final_points = base_points;
double final_tp = 0.0;
if(IsBuyTargetOrderType(orderType))
final_tp = en + final_points * point;
else
final_tp = en - final_points * point;
final_tp = NormalizeDouble(final_tp, digits);
if(MathAbs(final_tp - normalized_api_tp) >= point * 0.5)
Print("[TP Adjust] orderType=", orderType,
" en=", en,
" api_tp=", normalized_api_tp,
" final_tp=", final_tp,
" base_points=", DoubleToString(base_points, 1),
" final_points=", DoubleToString(final_points, 1),
" multiplier=", DoubleToString(EffectiveTpMultiplier(), 2));
return final_tp;
}
//| エントリー判定が必要な場合のみ注文処理を実行する関数
//+------------------------------------------------------------------+
/**
@@ -302,6 +427,19 @@ double SplitEntryPrice(const double zone_low, const double zone_high, const int
return NormalizeDouble(zone_low + (zone_high - zone_low) * ratio, digits);
}
//+------------------------------------------------------------------+
//| Split-zone TP reference price
//+------------------------------------------------------------------+
double SplitTakeProfitReferenceEntry(const int orderType, const double zone_low, const double zone_high)
{
if(IsBuyTargetOrderType(orderType))
return zone_high;
if(IsSellTargetOrderType(orderType))
return zone_low;
return (zone_low + zone_high) / 2.0;
}
//+------------------------------------------------------------------+
//| 注文ロットがブローカー制約を満たすか判定する関数
//+------------------------------------------------------------------+
@@ -453,15 +591,26 @@ int TrySendSplitEntryOrders(const int orderType, EAState &state, TickContext &ct
{
string entry_type = EntryTypeName(orderType);
double cur_price = CurrentPriceForOrderType(orderType, ctx);
string guard_reason = "";
if(IsLimitOrderBlockedByRangeBreakoutGuard(orderType, guard_reason))
{
Print("[Split Skip] ", entry_type, " blocked by range breakout guard. ", guard_reason);
return 0;
}
double zone_low = state.zone_low[orderType];
double zone_high = state.zone_high[orderType];
double tp = state.zone_tp[orderType];
double raw_tp = state.zone_tp[orderType];
double tp = ExpandedTakeProfitPrice(orderType,
SplitTakeProfitReferenceEntry(orderType, zone_low, zone_high),
raw_tp);
double sl = state.zone_sl[orderType];
if(!HasValidZonePrices(zone_low, zone_high, tp, sl))
{
Print("[Split Skip] invalid target zone. orderType=", orderType,
" zone=", zone_low, "-", zone_high, " tp=", tp, " sl=", sl,
" zone=", zone_low, "-", zone_high,
" api_tp=", raw_tp, " tp=", tp, " sl=", sl,
" candidate_id=", state.zone_candidate_id);
return 0;
}
@@ -507,29 +656,34 @@ int TrySendSplitEntryOrders(const int orderType, EAState &state, TickContext &ct
}
double en = SplitEntryPrice(zone_low, zone_high, slot, split_count);
if(!IsTargetPriceOrderConditionMatched(orderType, ctx, en, tp, sl))
double slot_tp = ExpandedTakeProfitPrice(orderType, en, raw_tp);
if(!IsTargetPriceOrderConditionMatched(orderType, ctx, en, slot_tp, sl))
{
Print("[Split Skip] slot price no longer valid. key=", slot_key,
" cur=", cur_price, " en=", en, " tp=", tp, " sl=", sl);
" cur=", cur_price, " en=", en,
" api_tp=", raw_tp, " tp=", slot_tp, " sl=", sl);
continue;
}
if(!MeetsTradeDistanceRules(orderType, ctx, en, tp, sl))
if(!MeetsTradeDistanceRules(orderType, ctx, en, slot_tp, sl))
continue;
Print("[Split ", entry_type, " Order Try] key=", slot_key,
" en=", en, " tp=", tp, " sl=", sl, " volume=", volume);
" en=", en, " api_tp=", raw_tp, " tp=", slot_tp,
" sl=", sl, " volume=", volume);
if(SendOrder(orderType, en, tp, sl, volume, slot_key, TargetCandidateReferenceTime(state)))
if(SendOrder(orderType, en, slot_tp, sl, volume, slot_key, TargetCandidateReferenceTime(state)))
{
Print("[Split ", entry_type, " Order Sent] key=", slot_key,
" en=", en, " tp=", tp, " sl=", sl, " volume=", volume);
" en=", en, " api_tp=", raw_tp, " tp=", slot_tp,
" sl=", sl, " volume=", volume);
sent_success++;
}
else
{
Print("[Split ", entry_type, " Order Failed] key=", slot_key,
" en=", en, " tp=", tp, " sl=", sl, " volume=", volume);
" en=", en, " api_tp=", raw_tp, " tp=", slot_tp,
" sl=", sl, " volume=", volume);
}
}
@@ -551,15 +705,22 @@ bool TrySendEntryOrder(const int orderType, EAState &state, TickContext &ctx)
{
string entry_type = EntryTypeName(orderType);
double cur_price = CurrentPriceForOrderType(orderType, ctx);
string guard_reason = "";
if(IsLimitOrderBlockedByRangeBreakoutGuard(orderType, guard_reason))
{
Print("[Skip] ", entry_type, " blocked by range breakout guard. ", guard_reason);
return false;
}
double en = state.en_price[orderType];
double tp = state.tp_price[orderType];
double raw_tp = state.tp_price[orderType];
double tp = ExpandedTakeProfitPrice(orderType, en, raw_tp);
double sl = state.sl_price[orderType];
if(!HasValidTargetPrices(en, tp, sl))
{
Print("[Skip] invalid target prices. orderType=", orderType,
" en=", en, " tp=", tp, " sl=", sl,
" en=", en, " api_tp=", raw_tp, " tp=", tp, " sl=", sl,
" candidate_age=", TargetCandidateAgeText(state));
return false;
}
@@ -567,7 +728,8 @@ bool TrySendEntryOrder(const int orderType, EAState &state, TickContext &ctx)
bool ok = IsTargetPriceOrderConditionMatched(orderType, ctx, en, tp, sl);
if(!ok)
{
Print("[No ", entry_type, "] cur=", cur_price, " en=", en, " tp=", tp, " sl=", sl,
Print("[No ", entry_type, "] cur=", cur_price, " en=", en,
" api_tp=", raw_tp, " tp=", tp, " sl=", sl,
" candidate_age=", TargetCandidateAgeText(state));
return false;
}
@@ -583,16 +745,19 @@ bool TrySendEntryOrder(const int orderType, EAState &state, TickContext &ctx)
if(!MeetsTradeDistanceRules(orderType, ctx, en, tp, sl))
return false;
Print("[", entry_type, " Order Try at ", cur_price, "] en=", en, " tp=", tp, " sl=", sl);
Print("[", entry_type, " Order Try at ", cur_price, "] en=", en,
" api_tp=", raw_tp, " tp=", tp, " sl=", sl);
if(SendOrder(orderType, en, tp, sl, lot_size,
TargetCandidateCommentSuffix(state), TargetCandidateReferenceTime(state)))
{
Print("[", entry_type, " Order Sent] ticket ok. en=", en, " tp=", tp, " sl=", sl);
Print("[", entry_type, " Order Sent] ticket ok. en=", en,
" api_tp=", raw_tp, " tp=", tp, " sl=", sl);
return true;
}
Print("[", entry_type, " Order Failed] en=", en, " tp=", tp, " sl=", sl);
Print("[", entry_type, " Order Failed] en=", en,
" api_tp=", raw_tp, " tp=", tp, " sl=", sl);
return false;
}
@@ -0,0 +1,172 @@
#ifndef HIT_POSITION_LIFECYCLE_MANAGER_MQH
#define HIT_POSITION_LIFECYCLE_MANAGER_MQH
#include <Trade/Trade.mqh>
// Ticket-based position lifecycle manager for hedging accounts.
class CHITPositionLifecycleManager
{
private:
string m_symbol;
ulong m_magic;
ulong m_deviation_points;
int m_close_after_seconds;
CTrade m_trade;
public:
CHITPositionLifecycleManager()
{
m_symbol = _Symbol;
m_magic = 0;
m_deviation_points = 10;
m_close_after_seconds = 0;
}
bool Init(const string symbol,
const ulong magic,
const ulong deviation_points,
const int close_after_seconds)
{
m_symbol = symbol;
m_magic = magic;
m_deviation_points = deviation_points;
m_close_after_seconds = close_after_seconds;
if(m_symbol == "")
{
Print("HITPositionLifecycleManager init failed: symbol is empty.");
return false;
}
ResetLastError();
if(!SymbolSelect(m_symbol, true))
{
Print("HITPositionLifecycleManager init failed: symbol=", m_symbol,
" err=", GetLastError());
return false;
}
m_trade.SetExpertMagicNumber(m_magic);
m_trade.SetDeviationInPoints((int)m_deviation_points);
m_trade.SetTypeFillingBySymbol(m_symbol);
return true;
}
void SetCloseAfterSeconds(const int close_after_seconds)
{
m_close_after_seconds = close_after_seconds;
}
int CountTargetPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(!IsTargetSelectedPosition())
continue;
count++;
}
return count;
}
bool CloseExpiredPositions()
{
if(m_close_after_seconds <= 0)
return false;
bool result = false;
const datetime current_time = TimeCurrent();
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
ResetLastError();
if(!PositionSelectByTicket(ticket))
{
Print("HITPositionLifecycleManager select failed: ticket=", ticket,
" err=", GetLastError());
continue;
}
if(!IsTargetSelectedPosition())
continue;
const datetime entry_time = (datetime)PositionGetInteger(POSITION_TIME);
if(entry_time <= 0)
continue;
if(current_time - entry_time < m_close_after_seconds)
continue;
if(CloseSelectedPosition(ticket, "time limit"))
result = true;
}
return result;
}
private:
bool IsTargetSelectedPosition()
{
if(PositionGetString(POSITION_SYMBOL) != m_symbol)
return false;
if((ulong)PositionGetInteger(POSITION_MAGIC) != m_magic)
return false;
return true;
}
string PositionTypeText(const ENUM_POSITION_TYPE type)
{
if(type == POSITION_TYPE_BUY)
return "BUY";
if(type == POSITION_TYPE_SELL)
return "SELL";
return "UNKNOWN";
}
bool CloseSelectedPosition(const ulong ticket,
const string reason)
{
const ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double volume = PositionGetDouble(POSITION_VOLUME);
ResetLastError();
if(!m_trade.PositionClose(ticket, m_deviation_points))
{
Print("HITPositionLifecycleManager close failed: ticket=", ticket,
" symbol=", m_symbol,
" magic=", m_magic,
" type=", PositionTypeText(type),
" volume=", DoubleToString(volume, 2),
" reason=", reason,
" retcode=", m_trade.ResultRetcode(),
" err=", GetLastError());
return false;
}
Print("HITPositionLifecycleManager closed position: ticket=", ticket,
" symbol=", m_symbol,
" magic=", m_magic,
" type=", PositionTypeText(type),
" volume=", DoubleToString(volume, 2),
" reason=", reason,
" retcode=", m_trade.ResultRetcode());
return true;
}
};
#endif
+334 -82
View File
@@ -599,6 +599,340 @@ bool CancelPendingOrdersNotAllowedByTrend(const int trend_state)
return result;
}
//+------------------------------------------------------------------+
//| レンジブレイクガード用の入力値を安全な範囲へ丸める関数群
//+------------------------------------------------------------------+
int EffectiveRangeBreakoutLookbackBars()
{
int bars = input_range_breakout_lookback_bars;
if(bars < 5)
bars = 5;
if(bars > 200)
bars = 200;
return bars;
}
double EffectiveRangeBreakoutMaxWidthMultiplier()
{
double multiplier = input_range_breakout_max_width_multiplier;
if(multiplier < 0.0)
multiplier = 0.0;
if(multiplier > 100.0)
multiplier = 100.0;
return multiplier;
}
double EffectiveRangeBreakoutBufferMultiplier()
{
double multiplier = input_range_breakout_buffer_multiplier;
if(multiplier <= 0.0)
multiplier = 0.20;
if(multiplier > 10.0)
multiplier = 10.0;
return multiplier;
}
double EffectiveRangeBreakoutNearMultiplier()
{
double multiplier = input_range_breakout_near_multiplier;
if(multiplier <= 0.0)
multiplier = 0.50;
if(multiplier > 20.0)
multiplier = 20.0;
return multiplier;
}
double EffectiveRangeBreakoutBodyMultiplier()
{
double multiplier = input_range_breakout_body_multiplier;
if(multiplier <= 0.0)
multiplier = 1.50;
if(multiplier > 20.0)
multiplier = 20.0;
return multiplier;
}
int EffectiveRangeBreakoutCooldownBars()
{
int bars = input_range_breakout_cooldown_bars;
if(bars < 1)
bars = 1;
if(bars > 96)
bars = 96;
return bars;
}
//+------------------------------------------------------------------+
//| レンジブレイク方向の表示名
//+------------------------------------------------------------------+
string RangeBreakoutDirectionName(const int direction)
{
if(direction == RANGE_BREAKOUT_UP)
return "UP";
if(direction == RANGE_BREAKOUT_DOWN)
return "DOWN";
return "NONE";
}
//+------------------------------------------------------------------+
//| 価格を現在シンボルの桁数で表示する関数
//+------------------------------------------------------------------+
string RangeBreakoutPriceText(const double price)
{
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
return DoubleToString(price, digits);
}
//+------------------------------------------------------------------+
//| 期限切れのレンジブレイクガード状態を解除する関数
//+------------------------------------------------------------------+
void ExpireRangeBreakoutLimitGuard()
{
if(g_range_breakout_guard_direction == RANGE_BREAKOUT_NONE)
return;
if(g_range_breakout_guard_until <= 0)
return;
if(TimeCurrent() < g_range_breakout_guard_until)
return;
Print("[Range Breakout Guard] cooldown expired. direction=",
RangeBreakoutDirectionName(g_range_breakout_guard_direction));
g_range_breakout_guard_direction = RANGE_BREAKOUT_NONE;
g_range_breakout_guard_until = 0;
}
//+------------------------------------------------------------------+
//| レンジブレイク状態を有効化する関数
//+------------------------------------------------------------------+
void ActivateRangeBreakoutLimitGuard(const int direction, const string reason)
{
if(direction == RANGE_BREAKOUT_NONE)
return;
int timeframe_seconds = PeriodSeconds(input_range_breakout_guard_timeframe);
if(timeframe_seconds <= 0)
timeframe_seconds = PeriodSeconds(_Period);
if(timeframe_seconds <= 0)
timeframe_seconds = 60;
datetime new_until = TimeCurrent() + timeframe_seconds * EffectiveRangeBreakoutCooldownBars();
if(g_range_breakout_guard_direction == direction &&
g_range_breakout_guard_until > TimeCurrent())
return;
g_range_breakout_guard_direction = direction;
g_range_breakout_guard_until = new_until;
Print("[Range Breakout Guard] activated. direction=", RangeBreakoutDirectionName(direction),
" until=", TimeToString(g_range_breakout_guard_until, TIME_DATE | TIME_SECONDS),
" reason=", reason);
}
//+------------------------------------------------------------------+
//| レンジブレイクを分析し、逆張りLimitを止める方向を返す関数
//+------------------------------------------------------------------+
bool AnalyzeRangeBreakoutLimitGuard(TickContext &ctx, int &direction, string &reason)
{
direction = RANGE_BREAKOUT_NONE;
reason = "";
if(!input_range_breakout_guard_enabled)
return false;
int lookback = EffectiveRangeBreakoutLookbackBars();
MqlRates rates[];
int copied = CopyRates(_Symbol, input_range_breakout_guard_timeframe, 1, lookback + 1, rates);
if(copied < lookback + 1)
return false;
int last_index = copied - 1;
int history_count = copied - 1;
if(history_count < lookback || last_index <= 0)
return false;
bool initialized = false;
double range_high = 0.0;
double range_low = 0.0;
double total_range = 0.0;
double total_body = 0.0;
// Build the reference range from closed bars before the latest closed bar.
for(int i = 0; i < history_count; i++)
{
if(!initialized)
{
range_high = rates[i].high;
range_low = rates[i].low;
initialized = true;
}
else
{
if(rates[i].high > range_high)
range_high = rates[i].high;
if(rates[i].low < range_low)
range_low = rates[i].low;
}
total_range += rates[i].high - rates[i].low;
total_body += MathAbs(rates[i].close - rates[i].open);
}
if(!initialized)
return false;
double avg_range = total_range / history_count;
double avg_body = total_body / history_count;
if(avg_range <= 0.0)
avg_range = Point();
if(avg_body <= 0.0)
avg_body = Point();
double range_width = range_high - range_low;
if(range_width <= 0.0)
return false;
double max_width_multiplier = EffectiveRangeBreakoutMaxWidthMultiplier();
if(max_width_multiplier > 0.0 && range_width > avg_range * max_width_multiplier)
return false;
MqlRates last_bar = rates[last_index];
double buffer = MathMax(avg_range * EffectiveRangeBreakoutBufferMultiplier(), ctx.spread);
double near_buffer = MathMax(avg_range * EffectiveRangeBreakoutNearMultiplier(), ctx.spread);
double body_threshold = avg_body * EffectiveRangeBreakoutBodyMultiplier();
double last_body = MathAbs(last_bar.close - last_bar.open);
bool last_bullish = (last_bar.close > last_bar.open);
bool last_bearish = (last_bar.close < last_bar.open);
bool last_large_body = (last_body >= body_threshold);
bool confirmed_up = (last_bar.close > range_high + buffer);
bool confirmed_down = (last_bar.close < range_low - buffer);
bool warning_up = (ctx.bid >= range_high - near_buffer &&
(confirmed_up || ctx.bid > range_high + buffer ||
(last_bullish && (last_large_body || last_bar.close > range_high))));
bool warning_down = (ctx.ask <= range_low + near_buffer &&
(confirmed_down || ctx.ask < range_low - buffer ||
(last_bearish && (last_large_body || last_bar.close < range_low))));
if(confirmed_up || warning_up)
{
direction = RANGE_BREAKOUT_UP;
reason = "upward " + string(confirmed_up ? "confirmed" : "warning") +
" tf=" + EnumToString(input_range_breakout_guard_timeframe) +
" range=[" + RangeBreakoutPriceText(range_low) + "," +
RangeBreakoutPriceText(range_high) + "] bid=" +
RangeBreakoutPriceText(ctx.bid) + " last_close=" +
RangeBreakoutPriceText(last_bar.close);
return true;
}
if(confirmed_down || warning_down)
{
direction = RANGE_BREAKOUT_DOWN;
reason = "downward " + string(confirmed_down ? "confirmed" : "warning") +
" tf=" + EnumToString(input_range_breakout_guard_timeframe) +
" range=[" + RangeBreakoutPriceText(range_low) + "," +
RangeBreakoutPriceText(range_high) + "] ask=" +
RangeBreakoutPriceText(ctx.ask) + " last_close=" +
RangeBreakoutPriceText(last_bar.close);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| レンジブレイク方向と逆向きのLimit pendingを取消する関数
//+------------------------------------------------------------------+
bool CancelRangeBreakoutOppositeLimitOrders(const int direction, const string reason)
{
if(direction == RANGE_BREAKOUT_NONE)
return false;
bool result = false;
CleanupPendingOrderCancelAttempts();
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket == 0 || !OrderSelect(ticket))
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
if((int)OrderGetInteger(ORDER_MAGIC) != magic_number)
continue;
ENUM_ORDER_TYPE order_type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
bool should_cancel = ((direction == RANGE_BREAKOUT_UP && order_type == ORDER_TYPE_SELL_LIMIT) ||
(direction == RANGE_BREAKOUT_DOWN && order_type == ORDER_TYPE_BUY_LIMIT));
if(!should_cancel)
continue;
if(TryCancelPendingOrder(ticket, "range breakout guard: " + reason))
result = true;
}
return result;
}
//+------------------------------------------------------------------+
//| レンジブレイクガードを監視し、逆方向Limitを退避する関数
//+------------------------------------------------------------------+
void ManageRangeBreakoutLimitGuard(TickContext &ctx)
{
if(!input_range_breakout_guard_enabled)
return;
ExpireRangeBreakoutLimitGuard();
int direction = RANGE_BREAKOUT_NONE;
string reason = "";
if(AnalyzeRangeBreakoutLimitGuard(ctx, direction, reason))
ActivateRangeBreakoutLimitGuard(direction, reason);
if(g_range_breakout_guard_direction == RANGE_BREAKOUT_NONE ||
g_range_breakout_guard_until <= TimeCurrent())
return;
string active_reason = "active " +
RangeBreakoutDirectionName(g_range_breakout_guard_direction) +
" cooldown until " +
TimeToString(g_range_breakout_guard_until, TIME_DATE | TIME_SECONDS);
CancelRangeBreakoutOppositeLimitOrders(g_range_breakout_guard_direction, active_reason);
}
//+------------------------------------------------------------------+
//| 新規Limit注文がレンジブレイクガードで停止中か判定する関数
//+------------------------------------------------------------------+
bool IsLimitOrderBlockedByRangeBreakoutGuard(const int orderType, string &reason)
{
reason = "";
if(!input_range_breakout_guard_enabled)
return false;
ExpireRangeBreakoutLimitGuard();
if(g_range_breakout_guard_direction == RANGE_BREAKOUT_NONE ||
g_range_breakout_guard_until <= TimeCurrent())
return false;
bool blocked = ((g_range_breakout_guard_direction == RANGE_BREAKOUT_UP && orderType == 4) ||
(g_range_breakout_guard_direction == RANGE_BREAKOUT_DOWN && orderType == 2));
if(!blocked)
return false;
reason = "direction=" + RangeBreakoutDirectionName(g_range_breakout_guard_direction) +
" until=" + TimeToString(g_range_breakout_guard_until, TIME_DATE | TIME_SECONDS);
return true;
}
//+------------------------------------------------------------------+
//| pendingコメントからH1候補時刻を復元する関数
//+------------------------------------------------------------------+
@@ -734,86 +1068,4 @@ bool CancelExpiredOrders()
}
return result;
}
//+------------------------------------------------------------------+
//| 時間が経過したポジションをクローズする関数
//+------------------------------------------------------------------+
/**
* @brief 実時間でCLOSE_H1_LIMIT時間を超えた保有ポジションをクローズします。
*
* @return 1件以上クローズに成功した場合はtrue。
*
* 対象は`_Symbol` と `magic_number` が一致するポジションのみです。
*/
bool CloseExpiredPositions()
{
bool result = false;
// ポジションを逆順でループ
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
string position_symbol = PositionGetSymbol(i);
if(position_symbol!="")
{
// Magic Number とシンボルでフィルタリング
if(position_symbol != _Symbol)
continue;
if(PositionGetInteger(POSITION_MAGIC) == magic_number)
{
// ポジションのエントリー時刻を取得
datetime entry_time = (datetime)PositionGetInteger(POSITION_TIME);
int expiration_seconds = CLOSE_H1_LIMIT * PeriodSeconds(PERIOD_H1);
if(entry_time <= 0 || expiration_seconds <= 0)
continue;
if(TimeCurrent() - entry_time >= expiration_seconds)
{
MqlTradeRequest request = {};
MqlTradeResult trade_result = {};
// ポジションタイプに応じてリクエストを設定
int position_type = (int)PositionGetInteger(POSITION_TYPE);
request.action = TRADE_ACTION_DEAL;
request.position = PositionGetInteger(POSITION_TICKET); // ポジションのチケット番号
request.symbol = position_symbol; // シンボル
request.volume = PositionGetDouble(POSITION_VOLUME); // ポジションサイズ
request.price = (position_type == POSITION_TYPE_BUY)
? SymbolInfoDouble(position_symbol, SYMBOL_BID) // BUYの場合はBIDでクローズ
: SymbolInfoDouble(position_symbol, SYMBOL_ASK); // SELLの場合はASKでクローズ
request.deviation = slippage;
request.type = (position_type == POSITION_TYPE_BUY)
? ORDER_TYPE_SELL // BUYポジションをSELLでクローズ
: ORDER_TYPE_BUY; // SELLポジションをBUYでクローズ
request.type_filling = GetOrderFillingPolicy(position_symbol); // 注文執行ポリシー
request.magic = magic_number;
// 注文送信
if(!OrderSend(request, trade_result))
{
Print("Failed to close position. Ticket: ", request.position, " Error: ", GetLastError());
continue;
}
// 結果の確認
if(trade_result.retcode == TRADE_RETCODE_DONE || trade_result.retcode == TRADE_RETCODE_DONE_PARTIAL)
{
Print("Position closed successfully due to a time limit. Ticket: ", request.position);
result = true; // 少なくとも1つ成功した場合
}
else
{
Print("Failed to close position. Ticket: ", request.position, " Retcode: ", trade_result.retcode);
}
}
}
}
else
{
Print("Failed to select position at index ", i, ". Error: ", GetLastError());
}
}
return result;
}
#endif
+31 -5
View File
@@ -335,12 +335,14 @@ public:
continue;
double candidate_sl = 0.0;
const double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
const double evaluation_price = (type == POSITION_TYPE_BUY) ? tick.bid : tick.ask;
if (!CalcHighVolatilitySL(type,
evaluation_price,
open_m1,
open_m3,
open_m5,
evaluation_price,
position_open_price,
open_m1,
open_m3,
open_m5,
open_m10,
open_m15,
pip_size,
@@ -590,6 +592,7 @@ private:
/// @return いずれかの時間足でしきい値を超え、SL候補を出力した場合は true。
bool CalcHighVolatilitySL(const ENUM_POSITION_TYPE type,
const double current_price,
const double position_open_price,
const double open_m1,
const double open_m3,
const double open_m5,
@@ -612,13 +615,36 @@ private:
if (!has_candidate)
return false;
if (!IsProfitProtectingStopLoss(type, candidate_sl, position_open_price))
return false;
sl_value = NormalizeDouble(candidate_sl, digits);
return true;
}
/// @brief 急変時SL候補が建値より不利にならないことを確認する。
bool IsProfitProtectingStopLoss(const ENUM_POSITION_TYPE type,
const double candidate_sl,
const double open_price)
{
if (open_price <= 0.0)
return false;
const double point = SymbolInfoDouble(m_symbol, SYMBOL_POINT);
const double tolerance = point * 0.1;
if (type == POSITION_TYPE_BUY)
return (candidate_sl >= open_price - tolerance);
if (type == POSITION_TYPE_SELL)
return (candidate_sl <= open_price + tolerance);
return false;
}
/// @brief 1本の時間足始値から急変幅を評価し、有効なSL候補なら最良候補へ反映する。
void EvaluateHighVolatilityOpen(const ENUM_POSITION_TYPE type,
const double current_price,
const double current_price,
const double open_price,
const double limit_pips,
const double rate_ratio,
+38 -1
View File
@@ -37,6 +37,17 @@
- `m15_imbalance_min_avg_body_points = 1.0`: Minimum M15 average body in points.
- `use_m15_imbalance_debug_log = false`: Enables detailed M15 impulse-confirmation logs.
- `input_entry_max_candidate_age_minutes = 120`: Maximum age, in minutes, for using an H1 candidate for new execution. This blocks delayed entries when M15 confirmation appears too late.
- `input_tp_multiplier = 1.25`: Expands the API TP distance from the entry price. Values below `1.0` are clamped to `1.0`, and values above `100.0` are clamped to `100.0`.
- `input_min_tp_points = 0`: Minimum final TP distance in points. `0` disables the floor.
- `input_max_tp_points = 0`: Maximum expanded TP distance in points. `0` disables the cap. The EA never makes the final TP closer than the API-provided TP distance.
- `input_range_breakout_guard_enabled = true`: Cancels adverse reversal Limit orders during range breakouts and temporarily blocks new ones.
- `input_range_breakout_guard_timeframe = PERIOD_M15`: Timeframe used by the range-breakout monitor.
- `input_range_breakout_lookback_bars = 20`: Closed-bar count used for range high/low and average range. The range starts from the bar before the latest closed bar.
- `input_range_breakout_max_width_multiplier = 6.0`: Maximum allowed range width versus average range. `0` disables the width filter.
- `input_range_breakout_buffer_multiplier = 0.20`: Average-range multiplier used as the confirmed-breakout buffer.
- `input_range_breakout_near_multiplier = 0.50`: Average-range multiplier used as the pre-breakout proximity band.
- `input_range_breakout_body_multiplier = 1.50`: Required latest closed-bar body multiple for momentum warning.
- `input_range_breakout_cooldown_bars = 4`: Number of monitor-timeframe bars to block adverse Limit entries after detection.
- `input_sltp_manager_enabled = false`: Enables UI-driven existing-position SL management through `CSLTPManager`.
- `input_sltp_show_panel = true`: Shows the SLTP control panel on the chart.
- `input_sltp_use_breakeven = false`: Enables normal break-even.
@@ -82,7 +93,7 @@
- `Include/MyLib/Signals/HITEntrySignal.mqh`: Entry preconditions, order-type price validation, M15 filter, and retry-state updates.
- `Include/MyLib/Common/HITExternalProcess.mqh`: done/running files, external process handles, PID recovery, exit-code checks, and timeout recovery.
- `Include/MyLib/Signals/HITPythonSignalGateway.mqh`: OHLC CSV export, Python batch launch, and `trend_state.txt` / `target_prices.txt` / `target_zones.txt` loading.
- `Include/MyLib/Trading/HITTradeManager.mqh`: Order sending, filling policy, pending-order expiration, order/position counting, expired order cancellation, and expired position closing.
- `Include/MyLib/Trading/HITTradeManager.mqh`: Order sending, filling policy, pending-order expiration, order/position counting, expired order cancellation, expired position closing, and adverse Limit cancellation during range breakouts.
- `Include/MyLib/Trading/SLTPManager.mqh`: Existing-position break-even, elapsed-time break-even, active trailing, TP-progress SL, and high-volatility SL management.
- `Include/MyLib/Panel/SLTPManagerPanel.mqh`: Chart panel that toggles `CSLTPManager` features, validates numeric inputs, and applies settings to the EA.
@@ -108,6 +119,8 @@ When no prefix is provided, such as manual Python execution, Python keeps using
- Running marker: `<prefix>_process_running_entry.txt`
- Batch file: `<TerminalDataPath>\MQL5\python_for_ea\bat\get_entry_reply.bat`
H1 candidate generation uses the OpenAI Responses API. The default model is `gpt-5.5`, the default `reasoning.effort` is `low`, and the default `text.verbosity` is `low`. `temperature` is not set. Runtime overrides are available through `OPENAI_MODEL`, `OPENAI_REASONING_EFFORT`, and `OPENAI_TEXT_VERBOSITY`. Output is validated with `text.format` Structured Outputs JSON Schema; incomplete responses, schema mismatches, price-rule failures, and insufficient reward/risk fall back to safe-stop values.
During `OnInit()`, the EA resolves `MQL5\python_for_ea` from `TerminalInfoString(TERMINAL_DATA_PATH)` and builds absolute batch-file paths. Each batch file resolves the Python project root from its own location, so runtime execution no longer depends on `C:\ea_py`.
`target_prices.txt` must contain 13 lines:
@@ -146,8 +159,11 @@ New entries are allowed only when all conditions below are satisfied:
- The H1 candidate set has not exceeded `input_entry_max_candidate_age_minutes`, measured from the closed H1 bar time that produced the candidate.
- The selected order type has valid `entry/tp/sl` values greater than `0.0`.
- The order type is allowed by the H4 `market_state`.
- Before price-consistency and broker-distance checks, TP is recalculated from the distance between entry and the API TP, multiplied by `input_tp_multiplier`. Buy strategies place the final TP above entry, and Sell strategies place it below entry.
- When split entries are enabled, the final TP is recalculated separately for each slot from that slot's entry price.
- The order-type price relationship and broker minimum distance rules are satisfied.
- When `use_m15_entry_filter = true`, T2/T4 satisfy the M15 direction, rejection, and proximity checks. T1/T3 satisfy M15 impulse confirmation when `use_m15_imbalance_confirmation = true`.
- When `input_range_breakout_guard_enabled = true`, T4 Sell Limit is blocked during an upward range-breakout cooldown and T2 Buy Limit is blocked during a downward range-breakout cooldown.
- This EA's pending orders plus positions are below `input_position_limit`.
- When split entries are enabled, `target_zones.txt` has `res_chk = 1`, the predicted zone prices are valid, split lots fit `SYMBOL_VOLUME_MIN/MAX/STEP`, and no duplicate order or position exists for the same `candidate_id` + order type + slot.
@@ -163,6 +179,14 @@ New entries are allowed only when all conditions below are satisfied:
After an H4 `market_state` reload, the EA cancels existing pending orders whose order types are not allowed by the refreshed state. For `6 MARKET_TECHNICAL_ERROR_STOP`, it cancels all pending orders managed by this EA.
### Range Breakout Guard
- The guard uses closed bars from `input_range_breakout_guard_timeframe`; the reference range is built from `input_range_breakout_lookback_bars` bars before the latest closed bar.
- It treats the market as a range only when the range width is within `input_range_breakout_max_width_multiplier` times the average range. A value of `0` disables this width check.
- If the latest closed bar closes above `rangeHigh + buffer`, or current Bid approaches the range high with upward momentum on the latest closed bar, the guard treats it as an upward warning/confirmation. It cancels existing T4 Sell Limit orders and blocks new T4 entries for `input_range_breakout_cooldown_bars` monitor bars.
- If the latest closed bar closes below `rangeLow - buffer`, or current Ask approaches the range low with downward momentum on the latest closed bar, the guard treats it as a downward warning/confirmation. It cancels existing T2 Buy Limit orders and blocks new T2 entries for `input_range_breakout_cooldown_bars` monitor bars.
- `buffer` and the proximity band use the larger of the average-range multiple and current spread to reduce over-triggering during live spread expansion.
### Order-Type Price Rules
- T1 Buy Stop: `Ask < entry`, `tp > entry`, `sl < entry`
@@ -199,7 +223,9 @@ After an H4 `market_state` reload, the EA cancels existing pending orders whose
- Single and split order comments keep a valid `candidate_id` for candidate-origin expiration handling.
- Split-entry order comments also include order type and slot number to prevent duplicate execution of the same strategy slot.
- When `cancel_old_split_pending_on_new_zone = true`, a new valid `candidate_id` cancels older split pending orders, and an invalid or stopped `target_zones.txt` cancels all existing split pending orders for this EA/symbol/magic.
- If the API TP direction contradicts the Buy/Sell price rules, the EA does not repair it; the existing price-consistency checks skip the order.
- After an H4 state update, existing pending orders that contradict the refreshed `market_state` are cancelled.
- The range-breakout guard only targets pending orders matching `_Symbol` and `magic_number`; upward breakouts cancel T4 Sell Limit only, and downward breakouts cancel T2 Buy Limit only.
- A failed pending-order cancellation is not retried for the same ticket until `input_cancel_retry_cooldown_seconds` has elapsed, preventing excessive per-tick trade-server requests.
- SLTP management only targets existing positions matching `_Symbol` and `magic_number`, and it preserves each position's current TP.
- SL changes are sent only when the candidate improves protection relative to the current SL and satisfies broker stop-level and freeze-level constraints.
@@ -217,6 +243,17 @@ After an H4 `market_state` reload, the EA cancels existing pending orders whose
## 10. Changelog
### 2026-06-13
- Added a range-breakout guard that retreats adverse reversal Limit orders. Upward breakouts cancel existing T4 Sell Limit orders and temporarily block new T4 entries; downward breakouts cancel existing T2 Buy Limit orders and temporarily block new T2 entries.
- Added guard inputs for monitor timeframe, lookback bars, range-width multiplier, breakout buffer, proximity band, momentum body multiple, and cooldown bars.
- Added `input_tp_multiplier`, `input_min_tp_points`, and `input_max_tp_points` to expand API-derived TP distances for both standard orders and per-slot split entries.
### 2026-06-06
- Updated the Python-side OpenAI default model to the official `gpt-5.5` slug and made Responses API calls send explicit `reasoning.effort` and `text.verbosity`. For GPT-5.5, the output contract is now documented as primarily enforced by Structured Outputs rather than duplicated prompt schema text.
- Added compatibility declarations to `HIT-EA_refactor_ver5.mq5` so it compiles against the current shared headers, covering split-entry zones, Python linkage filenames, and pending-cancel retry tracking. Since `use_split_entry_zone = false` by default, the normal ver5 entry behavior remains unchanged.
### 2026-06-01
- Added `debug_entry.txt` safe-stop diagnostics with the stop stage and reason so normal candidate skips can be distinguished from guarded failure fallbacks.
+38 -1
View File
@@ -29,6 +29,17 @@
- `m15_imbalance_min_avg_body_points = 1.0`: M15平均実体の最小値(point)。
- `use_m15_imbalance_debug_log = false`: M15初動確認の詳細ログを出すか。
- `input_entry_max_candidate_age_minutes = 120`: H1候補価格を新規発注に使用できる最大経過分数。古い候補でM15条件が後から整った場合の遅延エントリーを抑止する。
- `input_tp_multiplier = 1.25`: APIから取得したTP距離を、エントリー価格からの距離ベースで拡大する倍率。`1.0` 未満は `1.0``100.0` 超は `100.0` に丸める。
- `input_min_tp_points = 0`: 拡大後TP距離の最小値(point)。`0` の場合は無効化する。
- `input_max_tp_points = 0`: 拡大後TP距離の最大値(point)。`0` の場合は無効化する。API由来TPより近くなる場合は縮小せず、API距離を維持する。
- `input_range_breakout_guard_enabled = true`: レンジ上抜け/下抜け時に逆方向の逆張りLimit注文を退避し、新規発注も一時停止するか。
- `input_range_breakout_guard_timeframe = PERIOD_M15`: レンジブレイクを監視する時間足。
- `input_range_breakout_lookback_bars = 20`: レンジ高値・安値と平均レンジを計算する確定足本数。直近確定足の1本前から参照する。
- `input_range_breakout_max_width_multiplier = 6.0`: 平均レンジに対して許容する最大レンジ幅。`0` の場合はレンジ幅フィルタを無効化する。
- `input_range_breakout_buffer_multiplier = 0.20`: 確定ブレイク判定に使う平均レンジ倍率。
- `input_range_breakout_near_multiplier = 0.50`: ブレイク警戒判定に使うレンジ境界への接近幅倍率。
- `input_range_breakout_body_multiplier = 1.50`: ブレイク警戒に必要な直近確定足実体の平均実体倍率。
- `input_range_breakout_cooldown_bars = 4`: ブレイク検知後に逆方向Limitの新規発注を停止する足数。
- `input_position_limit = 10`: 自EAの未約定注文 + ポジション数の実運用上限。内部上限 `POSITION_LIMIT` を超える指定は丸める。
- `use_split_entry_zone = false`: H1予測ゾーンを使った分割エントリーを有効化するか。
- `split_entry_count = 3`: 分割エントリー本数。1〜10に丸めて使用する。
@@ -82,7 +93,7 @@
- `Include/MyLib/Signals/HITEntrySignal.mqh`: エントリー前提条件、注文タイプ別価格判定、M15フィルタ、リトライ状態更新。
- `Include/MyLib/Common/HITExternalProcess.mqh`: done/runningファイル、外部プロセスハンドル、PID復元、終了コード確認、タイムアウト復旧。
- `Include/MyLib/Signals/HITPythonSignalGateway.mqh`: OHLC CSV出力、Pythonバッチ起動、`trend_state.txt` / `target_prices.txt` / `target_zones.txt` 読込。
- `Include/MyLib/Trading/HITTradeManager.mqh`: 注文送信、執行ポリシー、有効期限設定、注文/ポジション数集計、期限切れ取消/クローズ。
- `Include/MyLib/Trading/HITTradeManager.mqh`: 注文送信、執行ポリシー、有効期限設定、注文/ポジション数集計、期限切れ取消/クローズ、レンジブレイク時の逆方向Limit取消
- `Include/MyLib/Trading/SLTPManager.mqh`: 既存ポジションに対するブレークイーブン、時間経過BE、アクティブトレーリング、TP進捗SL、急変時SL引き締めを管理する。
- `Include/MyLib/Panel/SLTPManagerPanel.mqh`: `CSLTPManager` の各設定をチャート上で切り替え、数値入力を検証してEAへ反映する操作パネル。
@@ -108,6 +119,8 @@ EAは `OnInit()` で `_Symbol` と `magic_number` から `HIT_<sanitized symbol>
- 実行中フラグ: `<prefix>_process_running_entry.txt`
- 起動バッチ: `<TerminalDataPath>\MQL5\python_for_ea\bat\get_entry_reply.bat`
H1候補生成はOpenAI Responses APIを使用する。既定モデルは `gpt-5.5`、既定 `reasoning.effort``low`、既定 `text.verbosity``low` とする。`temperature` は指定せず、`OPENAI_MODEL``OPENAI_REASONING_EFFORT``OPENAI_TEXT_VERBOSITY` で実行時上書きできる。出力は `text.format` のStructured Outputs JSON Schemaで検証し、未完了応答、schema不一致、価格条件違反、reward/risk不足は安全側停止値へ倒す。
EAは `OnInit()``TerminalInfoString(TERMINAL_DATA_PATH)` から `MQL5\python_for_ea` を解決し、各batファイルの絶対パスを組み立てる。batファイルは自身の位置からPythonプロジェクトルートを解決するため、`C:\ea_py` には依存しない。
`target_prices.txt` は13行構成とする。
@@ -146,8 +159,11 @@ Pythonは `target_prices.txt` と `target_zones.txt` の両方を書き終えて
- H1候補価格が、元になったH1確定足時刻から `input_entry_max_candidate_age_minutes` 分を超えていない。
- 対象注文タイプの `entry/tp/sl` がすべて `0.0` より大きい。
- H4 `market_state` に対して注文タイプが許可されている。
- TPは価格整合条件とブローカー距離制約の判定前に、APIのTP価格そのものではなく「エントリー価格からAPI TPまでの距離」を `input_tp_multiplier` で拡大して最終TPへ変換する。Buy系はエントリーより上、Sell系はエントリーより下へ配置する。
- 分割エントリー有効時は、slotごとのエントリー価格を基準に各slotの最終TPを個別に計算する。
- 注文タイプごとの価格整合条件とブローカー最小距離制約を満たす。
- `use_m15_entry_filter = true` の場合、T2/T4はM15確定足の方向・反発・接近条件を満たす。T1/T3は `use_m15_imbalance_confirmation = true` の場合にM15初動確認を満たす。
- `input_range_breakout_guard_enabled = true` の場合、レンジ上抜け後のT4 Sell Limit、レンジ下抜け後のT2 Buy Limitはクールダウン終了まで新規発注しない。
- 自EAの注文 + ポジション数が `input_position_limit` 未満。
- 分割エントリー有効時は `target_zones.txt``res_chk = 1`、予測ゾーンの価格整合、分割ロットの `SYMBOL_VOLUME_MIN/MAX/STEP` 適合、同一 `candidate_id` + 注文タイプ + slot の重複注文/ポジション不存在を満たす。
@@ -163,6 +179,14 @@ Pythonは `target_prices.txt` と `target_zones.txt` の両方を書き終えて
H4 `market_state` の再読込後は、更新後の状態で許可されない注文タイプの既存pending注文を取消する。`6 MARKET_TECHNICAL_ERROR_STOP` の場合は対象EAのpending注文をすべて取消する。
### レンジブレイクガード
- `input_range_breakout_guard_timeframe` の確定足を使い、直近確定足の1本前から `input_range_breakout_lookback_bars` 本の高値・安値をレンジ境界として計算する。
- レンジ幅が平均レンジの `input_range_breakout_max_width_multiplier` 倍以内の場合のみ、レンジ相場としてブレイク監視を有効にする。倍率が `0` の場合はこの幅判定を行わない。
- 直近確定足終値が `rangeHigh + buffer` を上回る場合、または現在Bidがレンジ上限に接近し直近確定足に上方向の勢いがある場合は上方向ブレイク警戒/確定とする。この時、既存のT4 Sell Limitを取消し、T4新規発注を `input_range_breakout_cooldown_bars` 本分停止する。
- 直近確定足終値が `rangeLow - buffer` を下回る場合、または現在Askがレンジ下限に接近し直近確定足に下方向の勢いがある場合は下方向ブレイク警戒/確定とする。この時、既存のT2 Buy Limitを取消し、T2新規発注を `input_range_breakout_cooldown_bars` 本分停止する。
- `buffer` と接近幅はM15平均レンジ倍率と現在スプレッドの大きい方を使い、ライブ時のスプレッド拡大で過剰検知しにくくする。
### 注文タイプごとの価格整合条件
- T1 Buy Stop: `Ask < entry`, `tp > entry`, `sl < entry`
@@ -199,7 +223,9 @@ H4 `market_state` の再読込後は、更新後の状態で許可されない
- 通常注文と分割注文のコメントには有効な `candidate_id` を入れ、候補時刻起点の期限管理に使用する。
- 分割エントリーの注文コメントには注文タイプとslot番号も入れ、同一戦略slotの二重発注を抑止する。
- `cancel_old_split_pending_on_new_zone = true` の場合、新しい有効 `candidate_id` を読んだ時は旧candidateの分割pending注文を取消し、`target_zones.txt` が無効または停止値の場合は既存の分割pending注文をすべて取消する。
- API由来TPの方向がBuy/Sell条件と逆の場合、EAは方向を補正せず既存の価格整合チェックで発注を見送る。
- H4状態更新後は、更新後の `market_state` と矛盾する既存pending注文を取消する。
- レンジブレイクガードは `_Symbol``magic_number` が一致するpending注文だけを対象にし、上方向ブレイクではT4 Sell Limit、下方向ブレイクではT2 Buy Limitのみを取消する。
- pending取消に失敗したticketは `input_cancel_retry_cooldown_seconds` の間再送せず、毎tickの過剰な取引サーバー要求を防止する。
- SLTP管理は `_Symbol``magic_number` が一致する既存ポジションのみを対象とし、TPは既存値を維持する。
- SL変更は既存SLより利益保護方向へ改善する場合だけ実行し、ブローカーのstop level / freeze levelを満たさない候補は送信しない。
@@ -217,6 +243,17 @@ H4 `market_state` の再読込後は、更新後の状態で許可されない
## 10. 変更履歴
### 2026-06-13
- レンジ上抜け/下抜け時に逆方向の逆張りLimitを退避するレンジブレイクガードを追加した。上方向ブレイクでは既存T4 Sell Limitを取消してT4新規発注を一時停止し、下方向ブレイクでは既存T2 Buy Limitを取消してT2新規発注を一時停止する。
- ガード用inputとして、監視時間足、参照本数、レンジ幅倍率、ブレイクバッファ、接近幅、勢い判定倍率、クールダウン本数を追加した。
- API由来TPをエントリー価格からの距離ベースで拡大する `input_tp_multiplier`, `input_min_tp_points`, `input_max_tp_points` を追加し、通常注文と分割slotごとの最終TP計算に反映した。
### 2026-06-06
- Python側のOpenAI既定モデルを公式slugの `gpt-5.5` へ更新し、Responses API呼び出しで `reasoning.effort``text.verbosity` を明示する仕様にした。GPT-5.5向けに、出力形式はプロンプト重複指定ではなくStructured Outputsを主契約として扱う。
- `HIT-EA_refactor_ver5.mq5` が現行の共有ヘッダーを使用してもコンパイルできるよう、split-entry zone、Python連携ファイル名、pending取消リトライ管理に必要な互換宣言を追加した。既定では `use_split_entry_zone = false` のため、ver5の通常エントリー挙動は維持する。
### 2026-06-01
- H1候補生成が安全側停止値へフォールバックした場合も `debug_entry.txt` へ停止段階と理由を追記し、通常の候補見送りと障害停止を判別できるようにした。
+72
View File
@@ -0,0 +1,72 @@
# HITEntryEA Specification
## 1. Overview
`HITEntryEA.mq5` is the entry-only Expert Advisor. It runs the H4/H1 Python signal workflow, applies the M15 confirmation rules, and submits pending orders. It does not manage filled positions. Stop management, trailing, and time-based position exits are delegated to `HITPositionManagerEA.mq5`.
The two EAs coordinate through matching `_Symbol` and `input_entry_magic_number` / `input_managed_magic_number`. The operating assumption is a retail hedging account, and initialization fails on non-hedging accounts.
## 2. Indicators
- No standard or custom indicators are used.
- Confirmed H4/H1 OHLC data is exported to Python. The EA reads `trend_state.txt`, `target_prices.txt`, and `target_zones.txt`.
- M15 confirmation uses `CopyRates(_Symbol, PERIOD_M15, OHLC_START_SHIFT, M15_CONFIRM_BARS, rates)`.
## 3. Parameters
- `lot_size = 0.01`: Standard order volume.
- `spread_limit = 60`: Maximum allowed spread in points.
- `input_entry_magic_number = 10001`: Magic number assigned to pending orders.
- `input_slippage_points = 10`: Allowed order deviation in points.
- `input_position_limit = 10`: Limit for same-symbol/same-magic pending orders plus positions.
- `use_split_entry_zone = false`: Enables split entries from the H1 prediction zone.
- `split_entry_count = 3`: Number of split entry orders.
- `split_lot_mode = SPLIT_LOT_TOTAL`: Total-volume split or fixed-volume split.
- `input_cancel_retry_cooldown_seconds = 60`: Ticket-level retry cooldown after pending cancellation failures.
- `use_m15_entry_filter = true`: Enables M15 closed-bar confirmation.
- `input_entry_max_candidate_age_minutes = 120`: Maximum age for using an H1 candidate for new execution.
- `input_tp_multiplier = 1.25`: Expands the API TP distance from the entry price. Values below `1.0` are clamped to `1.0`, and values above `100.0` are clamped to `100.0`.
- `input_min_tp_points = 0`: Minimum final TP distance in points. `0` disables the floor.
- `input_max_tp_points = 0`: Maximum expanded TP distance in points. `0` disables the cap. The EA never makes the final TP closer than the API-provided TP distance.
- `input_range_breakout_guard_enabled = true`: Cancels adverse reversal Limit orders during range breakouts and temporarily blocks new ones.
- `input_range_breakout_guard_timeframe = PERIOD_M15`: Timeframe used by the range-breakout monitor.
- `input_range_breakout_lookback_bars = 20`: Closed-bar count used for range high/low and average range.
- `input_range_breakout_max_width_multiplier = 6.0`: Maximum allowed range width versus average range. `0` disables the width filter.
- `input_range_breakout_buffer_multiplier = 0.20`: Average-range multiplier used as the confirmed-breakout buffer.
- `input_range_breakout_near_multiplier = 0.50`: Average-range multiplier used as the pre-breakout proximity band.
- `input_range_breakout_body_multiplier = 1.50`: Required latest closed-bar body multiple for momentum warning.
- `input_range_breakout_cooldown_bars = 4`: Number of monitor-timeframe bars to block adverse Limit entries after detection.
## 4. Entry And Exit Conditions
- New pending orders are sent only after spread, H4 state, H1 candidate, M15 timing, price consistency, broker distance, volume, and same-symbol/same-magic limit checks pass.
- Before order submission, TP is recalculated from the distance between entry and the API TP, multiplied by `input_tp_multiplier`. Buy strategies place the final TP above entry, and Sell strategies place it below entry.
- Split entries recalculate the final TP separately for each slot, using that slot's entry price.
- Pending orders receive `input_entry_magic_number`.
- The entry EA cancels expired pending orders, pending orders that conflict with the refreshed H4 state, and stale split-entry pending orders.
- When the range-breakout guard is enabled, upward breakout warning/confirmation cancels existing T4 Sell Limit orders and blocks new T4 entries until cooldown expiry. Downward breakout warning/confirmation cancels existing T2 Buy Limit orders and blocks new T2 entries.
- H1 Python results, candidate timestamps, and stale split-pending cleanup are processed before the spread gate; excessive spread only blocks new order submission.
- Filled-position SL updates, trailing, and time-based exits are not performed by this EA.
## 5. Risk Management
- The order limit counts only pending orders and positions matching `_Symbol` and `input_entry_magic_number`.
- Python process markers, process IDs, done files, and timeouts are monitored to prevent duplicate launches and stale-result reuse.
- If a done file exists but the expected result file is missing, the EA clears the done/running state and marks the same workflow for retry on the next tick.
- If the API TP direction contradicts the Buy/Sell price rules, the EA does not repair it; the existing price-consistency checks skip the order.
- The range-breakout guard only touches pending orders matching `_Symbol` and `input_entry_magic_number`; upward breakouts cancel T4 Sell Limit only, and downward breakouts cancel T2 Buy Limit only.
- `OrderSend` return values and `MqlTradeResult.retcode` are checked and logged.
- Because the design assumes hedging accounts, per-ticket position management is handled by `HITPositionManagerEA.mq5`.
## 6. Unit Test Scope
- In the Strategy Tester, this EA is tested for pending-order creation, expired pending cancellation, and H4-state mismatch cancellation.
- Position management behavior is outside this EA's unit-test scope.
- Python linkage filenames include `_Symbol` and `input_entry_magic_number` to preserve reproducibility.
## 7. Changelog
- 2026-06-14: Added initialization failure on non-hedging accounts. Moved H1 result loading before spread gating, added retry recovery for done-without-result Python states, and removed the unused filled-position time-exit path from the entry EA side.
- 2026-06-13: Connected the range-breakout guard from `HIT-EA_refactor_ver6.mq5` to the entry EA, including T4 Sell Limit retreat on upward breakouts, T2 Buy Limit retreat on downward breakouts, and cooldown-based new-entry blocking.
- 2026-06-13: Added `input_tp_multiplier`, `input_min_tp_points`, and `input_max_tp_points` to expand API-derived TP distances for both standard orders and per-slot split entries.
- 2026-06-07: Split entry responsibilities from `HIT-EA_refactor_ver6.mq5`; moved SLTP panel and filled-position management responsibilities to `HITPositionManagerEA.mq5`.
+72
View File
@@ -0,0 +1,72 @@
# HITEntryEA 仕様書(日本語)
## 1. 概要
`HITEntryEA.mq5` は、H4/H1の外部Python判定とM15確認を使って新規pending注文までを実行する発注専用EAです。約定後ポジションのSL移動、トレーリング、時間決済は行わず、同じマジックナンバーを監視する `HITPositionManagerEA.mq5` に委譲します。
2つのEAは `_Symbol``input_entry_magic_number` / `input_managed_magic_number` の一致で連携します。運用口座はhedging口座を前提とし、非hedging口座では初期化に失敗します。
## 2. 使用インジケータ
- 標準・カスタムインジケータは使用しません。
- H4/H1の確定足OHLCをPythonへ渡し、`trend_state.txt`, `target_prices.txt`, `target_zones.txt` を読み込みます。
- M15確認は `CopyRates(_Symbol, PERIOD_M15, OHLC_START_SHIFT, M15_CONFIRM_BARS, rates)` を使用します。
## 3. パラメータ設定
- `lot_size = 0.01`: 通常注文ロット。
- `spread_limit = 60`: 許容スプレッド(point)。
- `input_entry_magic_number = 10001`: 発注EAがpending注文へ付与するマジックナンバー。
- `input_slippage_points = 10`: 注文送信時の許容偏差。
- `input_position_limit = 10`: 同一symbol/magicのpending注文 + ポジション上限。
- `use_split_entry_zone = false`: H1予測ゾーンによる分割エントリーを有効化するか。
- `split_entry_count = 3`: 分割本数。
- `split_lot_mode = SPLIT_LOT_TOTAL`: 総量分割または固定ロット分割。
- `input_cancel_retry_cooldown_seconds = 60`: pending取消失敗後のticket単位再試行間隔。
- `use_m15_entry_filter = true`: M15確定足確認を使うか。
- `input_entry_max_candidate_age_minutes = 120`: H1候補を新規発注に使える最大経過分数。
- `input_tp_multiplier = 1.25`: APIから取得したTP距離を、エントリー価格からの距離ベースで拡大する倍率。`1.0` 未満は `1.0``100.0` 超は `100.0` に丸めます。
- `input_min_tp_points = 0`: 拡大後TP距離の最小値(point)。`0` の場合は無効です。
- `input_max_tp_points = 0`: 拡大後TP距離の最大値(point)。`0` の場合は無効です。ただしAPI由来TPより近くなる場合は縮小せず、API距離を維持します。
- `input_range_breakout_guard_enabled = true`: レンジ上抜け/下抜け時に逆方向の逆張りLimit注文を退避し、新規発注も一時停止するか。
- `input_range_breakout_guard_timeframe = PERIOD_M15`: レンジブレイクを監視する時間足。
- `input_range_breakout_lookback_bars = 20`: レンジ高値・安値と平均レンジを計算する確定足本数。
- `input_range_breakout_max_width_multiplier = 6.0`: 平均レンジに対して許容する最大レンジ幅。`0` の場合はレンジ幅フィルタを無効化します。
- `input_range_breakout_buffer_multiplier = 0.20`: 確定ブレイク判定に使う平均レンジ倍率。
- `input_range_breakout_near_multiplier = 0.50`: ブレイク警戒判定に使うレンジ境界への接近幅倍率。
- `input_range_breakout_body_multiplier = 1.50`: ブレイク警戒に必要な直近確定足実体の平均実体倍率。
- `input_range_breakout_cooldown_bars = 4`: ブレイク検知後に逆方向Limitの新規発注を停止する足数。
## 4. エントリー/エグジット条件
- 新規注文はスプレッド、H4状態、H1候補、M15確認、価格整合、ブローカー距離、ロット制約、同一symbol/magicの上限を満たす場合のみ送信します。
- TPは注文送信前に、APIのTP価格そのものではなく「エントリー価格からAPI TPまでの距離」を `input_tp_multiplier` で拡大して最終TPを計算します。Buy系はエントリーより上、Sell系はエントリーより下へ再配置します。
- 分割エントリーではslotごとのエントリー価格を基準に、各slotの最終TPを個別に計算します。
- pending注文には `input_entry_magic_number` を付与します。
- H4状態と矛盾するpending注文、期限切れpending注文、古い分割pending注文は発注EAが取消します。
- レンジブレイクガードがONの場合、M15監視足で上方向ブレイク警戒/確定を検出すると既存T4 Sell Limitを取消し、T4新規発注をクールダウン終了まで停止します。下方向ブレイクでは既存T2 Buy Limitを取消し、T2新規発注を停止します。
- H1 Python結果の読み込み、候補時刻更新、古い分割pending取消はスプレッド判定より前に実行し、スプレッド超過は新規発注だけを停止します。
- 約定後ポジションのSL変更、時間決済、トレーリングは発注EAでは行いません。
## 5. リスク管理
- 発注上限は口座全体ではなく `_Symbol``input_entry_magic_number` が一致するpending注文 + ポジションで判定します。
- Python連携は起動中/完了ファイルとプロセスIDを監視し、二重起動と古い結果の再利用を抑止します。
- doneファイルが存在しても結果ファイルが欠落している場合は、done/running状態を破棄し、次tickで同じ処理を再実行できる状態へ戻します。
- API由来TPの方向がBuy/Sell条件と逆の場合、EAは方向を補正せず既存の価格整合チェックで発注を見送ります。
- レンジブレイクガードは `_Symbol``input_entry_magic_number` が一致するpending注文だけを対象にし、上方向ではT4 Sell Limit、下方向ではT2 Buy Limitのみを取消します。
- `OrderSend` の戻り値と `MqlTradeResult.retcode` を確認し、失敗時はエラーまたはretcodeをログに出します。
- hedging口座前提のため、約定後の複数ticket管理は `HITPositionManagerEA.mq5` が担当します。
## 6. 単体テスト
- Strategy Testerでは発注EA単体で、pending注文の作成、期限切れ取消、H4状態不一致取消を確認します。
- 管理EA機能はこのテスト対象に含めません。
- Python連携ファイル名は `_Symbol``input_entry_magic_number` を含むprefixで分離し、再現性を保ちます。
## 7. 変更履歴
- 2026-06-14: 非hedging口座での初期化停止を追加。H1結果読み込みをスプレッド判定前へ移動し、doneのみ残るPython不整合時の再実行復旧を追加。発注EA側に残っていた未使用の約定後時間決済経路を削除。
- 2026-06-13: `HIT-EA_refactor_ver6.mq5` と同等のレンジブレイクガードを発注EAへ接続。レンジ上抜け時のT4 Sell Limit退避、レンジ下抜け時のT2 Buy Limit退避、およびクールダウン中の新規発注停止を反映。
- 2026-06-13: API由来TPをエントリー価格からの距離ベースで拡大する `input_tp_multiplier`, `input_min_tp_points`, `input_max_tp_points` を追加し、通常注文と分割slotごとの最終TP計算に反映。
- 2026-06-07: `HIT-EA_refactor_ver6.mq5` から発注責務を分離し、SLTPパネルとポジション管理を `HITPositionManagerEA.mq5` へ移管する仕様を追加。
@@ -0,0 +1,65 @@
# HITPositionManagerEA Specification
## 1. Overview
`HITPositionManagerEA.mq5` is the filled-position manager for positions created by `HITEntryEA.mq5`. It never creates new orders or pending orders. It is a retail hedging account EA and fails initialization on non-hedging accounts.
The managed set is limited to positions whose `POSITION_SYMBOL` equals `_Symbol` and whose `POSITION_MAGIC` equals `input_managed_magic_number`. Multiple tickets, split entries, and simultaneous buy/sell positions are managed independently.
## 2. Indicators
- No standard or custom indicators are used.
- SLTP management uses the current tick, each position's open price, SL, TP, open time, and symbol stop/freeze levels.
- High-volatility stop tightening uses M1/M3/M5/M10/M15 open prices and the current market price.
## 3. Parameters
- `input_managed_magic_number = 10001`: Magic number of positions to manage. It should match the entry EA's `input_entry_magic_number`.
- `input_slippage_points = 10`: Allowed deviation for closes and SL modifications.
- `input_position_close_after_h1_bars = 12`: Time-based close threshold in H1 bars. `0` disables it.
- `input_sltp_manager_enabled = false`: Master switch for SLTP management.
- `input_sltp_show_panel = true`: Shows the SLTP control panel.
- `input_sltp_use_breakeven = false`: Standard breakeven.
- `input_sltp_breakeven_trigger_pips = 30.0`: Profit threshold for standard breakeven.
- `input_sltp_breakeven_buffer_pips = 3.0`: Profit-side entry buffer used by standard breakeven.
- `input_sltp_use_elapsed_breakeven = false`: Elapsed-time breakeven.
- `input_sltp_elapsed_breakeven_hours = 4.0`: Holding time required before elapsed-time breakeven is allowed.
- `input_sltp_elapsed_breakeven_buffer_pips = 3.0`: Profit-side entry buffer used by elapsed-time breakeven.
- `input_sltp_use_active_trailing = false`: Active trailing.
- `input_sltp_active_breakeven_pips = 30.0`: Profit threshold that starts active trailing.
- `input_sltp_active_stop_loss_offset_pips = 5.0`: Initial locked profit offset from entry.
- `input_sltp_active_step_trigger_pips = 10.0`: Additional profit interval required for each SL step.
- `input_sltp_active_step_move_pips = 5.0`: SL movement added per completed step.
- `input_sltp_use_tp_progress_stop = false`: TP-progress stop.
- `input_sltp_tp_progress_trigger_percent = 70.0`: TP progress required before the SL lock is applied.
- `input_sltp_tp_progress_sl_lock_percent = 30.0`: Entry-to-TP distance percentage locked by SL.
- `input_sltp_use_high_volatility_limit = false`: High-volatility stop tightening.
## 4. Entry And Exit Conditions
- There are no entry conditions because this EA does not create orders.
- On every tick, the EA first evaluates ticket-based time exits and then applies SLTP management.
- Time exits close target tickets when `POSITION_TIME` is older than `input_position_close_after_h1_bars * PeriodSeconds(PERIOD_H1)` by using `CTrade::PositionClose(ticket)`.
- SLTP management applies only candidates that improve the current SL in the profit-protection direction, preserving the current TP with `CTrade::PositionModify(ticket, new_sl, current_tp)`.
- High-volatility stop tightening only accepts candidates that are at or beyond breakeven: at or above entry for BUY positions and at or below entry for SELL positions.
## 5. Risk Management
- All position processing uses `PositionGetTicket(i)` followed by `PositionSelectByTicket(ticket)`.
- Only positions matching `_Symbol` and `input_managed_magic_number` are touched.
- Different magic numbers, different symbols, and manual-position equivalents are ignored.
- Logs include ticket, symbol, magic, position type, trade retcode, and `GetLastError()`.
- Standard breakeven and active trailing cannot be enabled together; settings validation rejects that conflict.
- When `input_sltp_manager_enabled=false`, contradictory detailed SLTP settings do not stop the EA; time exits continue to run. Detailed settings are validated when SLTP management is enabled.
## 6. Unit Test Scope
- The production manager EA contains no test-only order creation.
- In the Strategy Tester, `HITPositionManagerTestHarness.mq5` seeds test BUY/SELL positions and a different-magic control position, then calls the same production manager classes.
- Test coverage targets same-symbol/same-magic multi-ticket handling, simultaneous buy/sell positions, magic mismatch exclusion, time exits, and SL updates.
## 7. Changelog
- 2026-06-14: Allowed the EA to continue time-exit management when SLTP master is off even if detailed SLTP settings conflict. Added breakeven protection to high-volatility stop tightening so loss-side SL candidates are discarded.
- 2026-06-13: Synchronized SLTP input defaults with `HIT-EA_refactor_ver6.mq5` and documented each default value.
- 2026-06-07: Split position-management responsibilities from `HIT-EA_refactor_ver6.mq5` and defined a hedging-account, ticket-based manager EA.
@@ -0,0 +1,65 @@
# HITPositionManagerEA 仕様書(日本語)
## 1. 概要
`HITPositionManagerEA.mq5` は、`HITEntryEA.mq5` が作成した約定済みポジションを管理する専用EAです。新規注文やpending注文は作成しません。hedging口座専用とし、起動時に `ACCOUNT_MARGIN_MODE_RETAIL_HEDGING` でない場合は `INIT_FAILED` とします。
管理対象は `_Symbol``input_managed_magic_number` が一致するポジションのみです。複数ポジション、両建て、分割エントリーをticket単位で独立管理します。
## 2. 使用インジケータ
- 標準・カスタムインジケータは使用しません。
- SLTP管理は現在tick、ポジションの建値/SL/TP/保有時間、シンボルのstop level / freeze levelを使用します。
- 急変時SL引き締めは M1/M3/M5/M10/M15 の始値と現在価格を使用します。
## 3. パラメータ設定
- `input_managed_magic_number = 10001`: 管理対象ポジションのマジックナンバー。発注EAの `input_entry_magic_number` と一致させます。
- `input_slippage_points = 10`: 決済とSL変更の許容偏差。
- `input_position_close_after_h1_bars = 12`: 保有時間決済のH1本数。0で無効。
- `input_sltp_manager_enabled = false`: SLTP管理全体のON/OFF。
- `input_sltp_show_panel = true`: SLTP操作パネルを表示するか。
- `input_sltp_use_breakeven = false`: 通常ブレークイーブン。
- `input_sltp_breakeven_trigger_pips = 30.0`: 通常ブレークイーブン開始pips。
- `input_sltp_breakeven_buffer_pips = 3.0`: 通常ブレークイーブン時の利益方向バッファpips。
- `input_sltp_use_elapsed_breakeven = false`: 保有時間ベースのブレークイーブン。
- `input_sltp_elapsed_breakeven_hours = 4.0`: 保有時間ベースのブレークイーブン開始時間。
- `input_sltp_elapsed_breakeven_buffer_pips = 3.0`: 保有時間ベースのブレークイーブン時の利益方向バッファpips。
- `input_sltp_use_active_trailing = false`: アクティブトレーリング。
- `input_sltp_active_breakeven_pips = 30.0`: アクティブトレーリング開始pips。
- `input_sltp_active_stop_loss_offset_pips = 5.0`: 開始時に建値から固定する利益pips。
- `input_sltp_active_step_trigger_pips = 10.0`: SLを1段進めるために必要な追加利益pips。
- `input_sltp_active_step_move_pips = 5.0`: 1段ごとにSLを利益方向へ動かすpips。
- `input_sltp_use_tp_progress_stop = false`: TP進捗率SL。
- `input_sltp_tp_progress_trigger_percent = 70.0`: TP進捗率SL開始率。
- `input_sltp_tp_progress_sl_lock_percent = 30.0`: 建値からTPまでの距離のうちSLで固定する割合。
- `input_sltp_use_high_volatility_limit = false`: 急変時SL引き締め。
## 4. エントリー/エグジット条件
- 新規エントリー条件はありません。このEAは注文作成を行いません。
- `OnTick()` では、まずticket単位で保有時間決済を評価し、その後SLTP管理を実行します。
- 時間決済は `POSITION_TIME` から `input_position_close_after_h1_bars * PeriodSeconds(PERIOD_H1)` 以上経過した対象ticketを `CTrade::PositionClose(ticket)` で閉じます。
- SLTP管理は、対象ticketの現在SLより利益保護方向へ改善する候補だけを `CTrade::PositionModify(ticket, new_sl, current_tp)` で反映します。
- 急変時SL引き締めは、BUYでは建値以上、SELLでは建値以下となる候補だけを採用し、損失側へのSL候補は破棄します。
## 5. リスク管理
- すべてのポジション選択は `PositionGetTicket(i)``PositionSelectByTicket(ticket)` を使います。
- `POSITION_SYMBOL == _Symbol` かつ `POSITION_MAGIC == input_managed_magic_number` のみ処理します。
- magic違い、symbol違い、手動ポジション相当には触れません。
- ログにはticket、symbol、magic、position type、retcode、`GetLastError()` を含めます。
- 通常ブレークイーブンとアクティブトレーリングの同時ONは設定検証で拒否します。
- `input_sltp_manager_enabled=false` の場合、SLTP詳細設定に矛盾があってもEA全体は停止せず、時間決済を継続します。SLTP管理をONにする時点で詳細設定を検証します。
## 6. 単体テスト
- 管理EA本体は本番安全性のため新規注文を持ちません。
- Strategy Testerでは `HITPositionManagerTestHarness.mq5` を使い、検証用のBUY/SELLおよびmagic違いポジションを作成してから同じ管理クラスを呼び出します。
- テスト観点は、同一symbol/magicの複数ticket管理、BUY/SELL両建て、magic違い非対象、時間決済、SL更新です。
## 7. 変更履歴
- 2026-06-14: SLTPマスターOFF時は詳細設定の矛盾でEA全体を停止しないよう変更。急変時SL引き締めに建値保護条件を追加し、損失側SL候補を採用しないようにした。
- 2026-06-13: SLTP関連inputの既定値を `HIT-EA_refactor_ver6.mq5` と同じ初期状態に同期し、仕様書へ各既定値を明記。
- 2026-06-07: `HIT-EA_refactor_ver6.mq5` からポジション管理責務を分離し、hedging口座専用のticket単位管理EAとして新規定義。
@@ -0,0 +1,45 @@
# HITPositionManagerTestHarness Specification
## 1. Overview
`HITPositionManagerTestHarness.mq5` is a Strategy Tester-only EA for unit-testing the management logic used by `HITPositionManagerEA.mq5`. It is not intended for live trading. It fails initialization outside `MQL_TESTER` and on non-hedging accounts.
The harness seeds test positions, then calls the same production `CSLTPManager` and `CHITPositionLifecycleManager` classes used by the manager EA.
## 2. Indicators
- No standard or custom indicators are used.
- The management logic uses the current tick, selected position data, and symbol trading constraints.
## 3. Parameters
- `input_managed_magic_number = 10001`: Magic number for managed test positions.
- `input_control_magic_number = 20002`: Magic number for a different-magic control position.
- `input_slippage_points = 10`: Allowed deviation for seed orders, closes, and SL modifications.
- `input_test_lot_size = 0.01`: Test position volume.
- `input_seed_managed_buy = true`: Seeds a managed BUY position.
- `input_seed_managed_sell = true`: Seeds a managed SELL position.
- `input_seed_control_position = true`: Seeds a different-magic control position.
- `input_position_close_after_h1_bars = 12`: Time-exit threshold in H1 bars.
- `input_sltp_*`: Same SLTP settings as the production manager EA.
## 4. Entry And Exit Conditions
- On the first tick, the harness creates the configured managed BUY, managed SELL, and different-magic BUY positions.
- After seeding, it runs the same management sequence as production: time exits, SLTP management, and high-volatility stop tightening.
- The different-magic control position is expected to remain unmanaged.
## 5. Risk Management
- This EA is tester-only and does not run in live mode.
- Test volume is normalized to `SYMBOL_VOLUME_MIN/MAX/STEP`.
- The target filter is the same as production: `_Symbol + input_managed_magic_number`.
## 6. Unit Test Scope
- The harness keeps test-only order creation out of the production manager EA.
- It verifies buy/sell coexistence, multiple-ticket handling, magic mismatch exclusion, time exits, and SL updates.
## 7. Changelog
- 2026-06-07: Created as the Strategy Tester unit-test harness for `HITPositionManagerEA.mq5`.
@@ -0,0 +1,45 @@
# HITPositionManagerTestHarness 仕様書(日本語)
## 1. 概要
`HITPositionManagerTestHarness.mq5` は、`HITPositionManagerEA.mq5` の管理ロジックをStrategy Testerで単体検証するためのテスト専用EAです。本番運用には使用しません。`MQL_TESTER` 以外では `INIT_FAILED` とし、hedging口座以外でも起動しません。
テスター内で検証用ポジションを作成した後、本番管理EAと同じ `CSLTPManager``CHITPositionLifecycleManager` を呼び出します。
## 2. 使用インジケータ
- 標準・カスタムインジケータは使用しません。
- 管理ロジックは現在tick、ポジション情報、シンボル制約を使用します。
## 3. パラメータ設定
- `input_managed_magic_number = 10001`: 管理対象として作成する検証用ポジションのmagic。
- `input_control_magic_number = 20002`: magic違い非対象確認用ポジションのmagic。
- `input_slippage_points = 10`: 検証用注文、決済、SL変更の許容偏差。
- `input_test_lot_size = 0.01`: 検証用ロット。
- `input_seed_managed_buy = true`: 管理対象BUYを作成するか。
- `input_seed_managed_sell = true`: 管理対象SELLを作成するか。
- `input_seed_control_position = true`: magic違いポジションを作成するか。
- `input_position_close_after_h1_bars = 12`: 保有時間決済のH1本数。
- `input_sltp_*`: 本番管理EAと同じSLTP管理設定。
## 4. エントリー/エグジット条件
- 初回tickで、設定に従って管理対象BUY、管理対象SELL、magic違いBUYを作成します。
- 作成後は本番管理ロジックと同じ順序で、保有時間決済、SLTP管理、急変時SL引き締めを実行します。
- magic違いポジションは管理対象外として残ることを確認します。
## 5. リスク管理
- 本EAはテスター専用であり、ライブ環境では起動しません。
- 検証用ポジションは `_Symbol` 上に作成し、ロットは `SYMBOL_VOLUME_MIN/MAX/STEP` に合わせて正規化します。
- 管理対象判定は本番と同じ `_Symbol + input_managed_magic_number` です。
## 6. 単体テスト
- 管理EA本体に新規注文コードを入れないためのテストハーネスです。
- BUY/SELL両建て、複数ticket、magic違い除外、時間決済、SL更新の再現性を確認します。
## 7. 変更履歴
- 2026-06-07: `HITPositionManagerEA.mq5` のStrategy Tester単体検証用ハーネスとして新規作成。
+6 -3
View File
@@ -133,7 +133,9 @@ EA側で分割エントリーを有効にした場合は、この予測ゾーン
## OpenAI 設定
H1 エントリー候補生成では OpenAI API を使用します。
Responses API `text.format` にJSON Schemaを渡し、GPT出力を自然文やCSV風テキストではなく
既定モデルは `gpt-5.5` です。Responses API `reasoning.effort`
`text.verbosity` を明示して送り、`temperature` は使用しません。
`text.format` にはJSON Schemaを渡し、GPT出力を自然文やCSV風テキストではなく
`schema_version``strategies` を持つ構造化JSONに固定します。
JSONが不正、API応答が未完了、価格条件やreward/risk条件に合わない場合は停止値へ倒します。
@@ -146,13 +148,14 @@ $env:OPENAI_API_KEY = "..."
任意:
```powershell
$env:OPENAI_MODEL = "..."
$env:OPENAI_MODEL = "gpt-5.5"
$env:OPENAI_REASONING_EFFORT = "low"
$env:OPENAI_TEXT_VERBOSITY = "low"
$env:MT5_EA_FILE_PREFIX = "HIT_GOLD_10001"
$env:MT5_PRICE_DIGITS = "2"
```
`OPENAI_MODEL` 未設定時は `src/ea_py/constants.py``DEFAULT_GPT_MODEL` を使います。`OPENAI_REASONING_EFFORT``none`, `low`, `medium`, `high`, `xhigh` のいずれかです。`MT5_EA_FILE_PREFIX``MT5_PRICE_DIGITS` は手動実行時だけ指定すればよく、EA/bat経由では自動設定されます。
`OPENAI_MODEL` 未設定時は `src/ea_py/constants.py``DEFAULT_GPT_MODEL` を使います。`OPENAI_REASONING_EFFORT``none`, `low`, `medium`, `high`, `xhigh` のいずれかです。H1候補生成の既定は低遅延と再現性確認のため `low` ですが、品質優先の検証では `medium` も評価してください。`OPENAI_TEXT_VERBOSITY``low`, `medium`, `high` のいずれかで、既定はJSON専用応答に合わせて `low` です。`MT5_EA_FILE_PREFIX``MT5_PRICE_DIGITS` は手動実行時だけ指定すればよく、EA/bat経由では自動設定されます。
## 主なモジュール
@@ -4,7 +4,7 @@
このドキュメントは、MT5データフォルダ配下の `MQL5\python_for_ea` のフォルダ構成に関する正本である。
現在のプロジェクトは、MT5 HIT-EAから出力されたOHLC CSVをPythonで解析し、OpenAI APIを使ってGOLD/XAUUSD向けの相場環境とエントリー候補価格を返す補助アプリケーションである。
現在のプロジェクトは、MT5 HIT-EAから出力されたOHLC CSVをPythonで解析し、GOLD/XAUUSD向けのH4相場環境とH1エントリー候補価格を返す補助アプリケーションである。H4相場環境はルールベースで判定し、H1エントリー候補価格のみOpenAI APIを使う。
MT5 EAから `bat/` 経由でルート直下のPythonスクリプトが呼ばれているため、現時点ではルート直下の `get_trend_reply.py``get_entry_reply.py` を実行入口として維持する。
@@ -197,7 +197,7 @@ EAファイルはこのPythonプロジェクトの外部連携先として扱う
| モジュール | 責務 |
|---|---|
| `src/ea_py/config.py` | 環境変数、モデル名、デバッグ設定などの設定読み込み。 |
| `src/ea_py/config.py` | 環境変数、GPT-5.5モデル名、reasoning effort、text verbosity、デバッグ設定などの設定読み込み。 |
| `src/ea_py/paths.py` | MT5データフォルダ、入力CSV、出力ファイルパスの組み立て。 |
| `src/ea_py/constants.py` | `market_state`、ATR期間、出力サイズなどの定数。 |
| `src/ea_py/types.py` | OHLCバー、OHLC要約、13行出力などの型定義。 |
@@ -212,7 +212,7 @@ EAファイルはこのPythonプロジェクトの外部連携先として扱う
| `src/ea_py/market/imbalance.py` | H1インバランス初動判定。 |
| `src/ea_py/prompts/trend_prompt.py` | H4相場環境判定プロンプトの生成。 |
| `src/ea_py/prompts/entry_prompt.py` | H1候補価格生成プロンプトの生成。 |
| `src/ea_py/openai_client.py` | OpenAI API呼び出しの薄いラッパー。 |
| `src/ea_py/openai_client.py` | GPT-5.5向けのResponses API呼び出し、reasoning/text設定、Structured Outputs指定をまとめる薄いラッパー。 |
| `src/ea_py/pipelines/trend_pipeline.py` | H4判定処理全体のオーケストレーション。 |
| `src/ea_py/pipelines/entry_pipeline.py` | H1候補生成処理全体のオーケストレーション。 |
+20 -3
View File
@@ -5,7 +5,14 @@ from __future__ import annotations
from dataclasses import dataclass
import os
from ea_py.constants import DEBUG_PRINT, DEFAULT_GPT_MODEL, DEFAULT_REASONING_EFFORT, VALID_REASONING_EFFORTS
from ea_py.constants import (
DEBUG_PRINT,
DEFAULT_GPT_MODEL,
DEFAULT_REASONING_EFFORT,
DEFAULT_TEXT_VERBOSITY,
VALID_REASONING_EFFORTS,
VALID_TEXT_VERBOSITIES,
)
@dataclass(frozen=True)
@@ -15,6 +22,7 @@ class RuntimeConfig:
api_key: str
model: str
reasoning_effort: str
text_verbosity: str
debug_print: bool
@@ -22,6 +30,7 @@ def load_runtime_config(
*,
model: str | None = None,
reasoning_effort: str | None = None,
text_verbosity: str | None = None,
debug_print: bool = DEBUG_PRINT,
) -> RuntimeConfig:
"""OpenAI呼び出しに必要な実行時設定を読み込む。
@@ -30,9 +39,9 @@ def load_runtime_config(
`OPENAI_API_KEY` 環境変数からのみ取得する。
未設定の場合は `RuntimeError` を送出し、上位パイプラインで停止値を出力する。
`model` / `reasoning_effort` / `debug_print` は呼び出し側から上書き可能だが、
`model` / `reasoning_effort` / `text_verbosity` / `debug_print` は呼び出し側から上書き可能だが、
通常は `constants.py` のデフォルト値を使う。
`OPENAI_MODEL` `OPENAI_REASONING_EFFORT` が設定されている場合は、
`OPENAI_MODEL` / `OPENAI_REASONING_EFFORT` / `OPENAI_TEXT_VERBOSITY` が設定されている場合は、
引数未指定時の実行時上書きとして扱う。
"""
api_key = os.getenv("OPENAI_API_KEY")
@@ -41,16 +50,24 @@ def load_runtime_config(
selected_model = model or os.getenv("OPENAI_MODEL") or DEFAULT_GPT_MODEL
selected_reasoning_effort = reasoning_effort or os.getenv("OPENAI_REASONING_EFFORT") or DEFAULT_REASONING_EFFORT
selected_text_verbosity = text_verbosity or os.getenv("OPENAI_TEXT_VERBOSITY") or DEFAULT_TEXT_VERBOSITY
if selected_reasoning_effort not in VALID_REASONING_EFFORTS:
allowed = ", ".join(sorted(VALID_REASONING_EFFORTS))
raise RuntimeError(
f"OPENAI_REASONING_EFFORT が不正です: {selected_reasoning_effort!r}. "
f"allowed={allowed}"
)
if selected_text_verbosity not in VALID_TEXT_VERBOSITIES:
allowed = ", ".join(sorted(VALID_TEXT_VERBOSITIES))
raise RuntimeError(
f"OPENAI_TEXT_VERBOSITY が不正です: {selected_text_verbosity!r}. "
f"allowed={allowed}"
)
return RuntimeConfig(
api_key=api_key,
model=selected_model,
reasoning_effort=selected_reasoning_effort,
text_verbosity=selected_text_verbosity,
debug_print=debug_print,
)
+4 -1
View File
@@ -4,9 +4,11 @@ from __future__ import annotations
MT_ENCODING = "utf-16 LE"
DEFAULT_GPT_MODEL = "gpt-5.5-2026-04-23"
DEFAULT_GPT_MODEL = "gpt-5.5"
DEFAULT_REASONING_EFFORT = "low"
VALID_REASONING_EFFORTS = frozenset({"none", "low", "medium", "high", "xhigh"})
DEFAULT_TEXT_VERBOSITY = "low"
VALID_TEXT_VERBOSITIES = frozenset({"low", "medium", "high"})
DEBUG_PRINT = True
ENTRY_RESPONSE_SCHEMA_VERSION = 1
@@ -36,6 +38,7 @@ TREND_TIMEFRAME = "H4"
ENTRY_TIMEFRAME = "H1"
TARGET_SIZE = 13
ENTRY_RESPONSE_SCHEMA_VERSION = 1
TECHNICAL_ERROR_STOP = 6
TREND_MAX_OUTPUT_TOKENS = 128
+2
View File
@@ -60,6 +60,7 @@ def append_debug_entry(
path: Path,
model: str,
reasoning_effort: str,
text_verbosity: str,
max_output_tokens: int,
api_diagnostics: str,
timeframe: str,
@@ -79,6 +80,7 @@ def append_debug_entry(
file.write(f"DEBUG TIME : {now_str()}\n")
file.write(f"MODEL : {model}\n")
file.write(f"REASONING : {reasoning_effort}\n")
file.write(f"VERBOSITY : {text_verbosity}\n")
file.write(f"MAX TOKENS : {max_output_tokens}\n")
file.write(f"TIMEFRAME : {timeframe}\n")
file.write(f"MARKET_STATE(H4) : {trend_state} ({MARKET_STATE_LABELS.get(trend_state, 'UNKNOWN')})\n")
+8 -1
View File
@@ -94,6 +94,7 @@ def call_responses_api(
image_data_urls: Sequence[str],
max_output_tokens: int,
response_text_format: Mapping[str, Any] | None = None,
text_verbosity: str | None = None,
) -> ResponsesApiResult:
"""Responses APIへテキストとチャート画像を送り、出力テキストを返す。
@@ -103,6 +104,7 @@ def call_responses_api(
`response_text_format` が指定された場合は Responses API の `text.format` へ渡し、
JSON Schemaなどの構造化出力をAPI側でも強制する。
`text_verbosity` はGPT-5.5の最終出力長を制御し、reasoning品質とは独立して扱う。
戻り値は `response.output_text` をstripした文字列とAPI診断情報。
API例外や空/不正な出力の安全側処理は、この薄いラッパーではなく
@@ -129,8 +131,13 @@ def call_responses_api(
"reasoning": {"effort": reasoning_effort},
"max_output_tokens": max_output_tokens,
}
text_config: dict[str, Any] = {}
if response_text_format is not None:
create_params["text"] = {"format": dict(response_text_format)}
text_config["format"] = dict(response_text_format)
if text_verbosity is not None:
text_config["verbosity"] = text_verbosity
if text_config:
create_params["text"] = text_config
response = client.responses.create(**create_params)
diagnostics = _extract_response_diagnostics(response)
@@ -323,6 +323,7 @@ def run_pipeline() -> None:
image_data_urls=images_data_urls,
max_output_tokens=max_tokens,
response_text_format=response_text_format,
text_verbosity=config.text_verbosity,
)
gpt_reply = gpt_result.text
api_diagnostics = gpt_result.diagnostics.to_log_text()
@@ -378,6 +379,7 @@ def run_pipeline() -> None:
path=paths.debug_reason,
model=config.model,
reasoning_effort=config.reasoning_effort,
text_verbosity=config.text_verbosity,
max_output_tokens=max_tokens,
api_diagnostics=api_diagnostics,
timeframe=ENTRY_TIMEFRAME,
@@ -320,13 +320,11 @@ def build_common_rules_block(
- エントリー後12時間以内に利確・損切に到達しなければ、その時点の価格でクローズ。
【出力ルール(最重要)】
- Responses APIのJSON Schemaに一致するJSONオブジェクトだけを出力してください。
- Responses APIのStructured Outputs(JSON Schema)で検証されるJSONオブジェクトだけを出力してください。
- schema_version は {ENTRY_RESPONSE_SCHEMA_VERSION}
- strategies には対象戦略だけを入れ、順序は {out_order}
- 各strategy要素は strategy, decision, entry, tp, sl, zone_low, zone_high, reason_code を必ず含めてください。
- decision は "use" または "skip" のみ
- reason_code は次のいずれかのみ: {", ".join(ENTRY_REASON_CODES)}
- 自然文、Markdown、余分なキー、空行は一切出力しないでください。
- decision は "use" または "skip"、reason_code は次のいずれかのみ: {", ".join(ENTRY_REASON_CODES)}
- 自然文、Markdown、空行は一切出力しないでください
""".strip()
+26
View File
@@ -7,17 +7,33 @@ import pytest
from ea_py.config import load_runtime_config
def test_load_runtime_config_uses_gpt55_defaults(monkeypatch: pytest.MonkeyPatch) -> None:
"""Default OpenAI settings follow the GPT-5.5 Responses API contract."""
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
monkeypatch.delenv("OPENAI_MODEL", raising=False)
monkeypatch.delenv("OPENAI_REASONING_EFFORT", raising=False)
monkeypatch.delenv("OPENAI_TEXT_VERBOSITY", raising=False)
actual = load_runtime_config()
assert actual.model == "gpt-5.5"
assert actual.reasoning_effort == "low"
assert actual.text_verbosity == "low"
def test_load_runtime_config_reads_model_and_reasoning_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
"""OpenAI model and reasoning effort can be overridden without code edits."""
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
monkeypatch.setenv("OPENAI_MODEL", "gpt-test")
monkeypatch.setenv("OPENAI_REASONING_EFFORT", "low")
monkeypatch.setenv("OPENAI_TEXT_VERBOSITY", "medium")
actual = load_runtime_config()
assert actual.api_key == "test-key"
assert actual.model == "gpt-test"
assert actual.reasoning_effort == "low"
assert actual.text_verbosity == "medium"
def test_load_runtime_config_rejects_invalid_reasoning_effort(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -27,3 +43,13 @@ def test_load_runtime_config_rejects_invalid_reasoning_effort(monkeypatch: pytes
with pytest.raises(RuntimeError, match="OPENAI_REASONING_EFFORT"):
load_runtime_config()
def test_load_runtime_config_rejects_invalid_text_verbosity(monkeypatch: pytest.MonkeyPatch) -> None:
"""Invalid text verbosity should fail before an API call is attempted."""
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
monkeypatch.delenv("OPENAI_REASONING_EFFORT", raising=False)
monkeypatch.setenv("OPENAI_TEXT_VERBOSITY", "tiny")
with pytest.raises(RuntimeError, match="OPENAI_TEXT_VERBOSITY"):
load_runtime_config()
+13 -7
View File
@@ -23,7 +23,7 @@ class FakeResponse:
def __init__(self, *, output_text: str, incomplete_details: object | None = None) -> None:
self.output_text = output_text
self.model = "gpt-5.5-2026-04-23"
self.model = "gpt-5.5"
self.status = "completed"
self.incomplete_details = incomplete_details
self.error = None
@@ -50,23 +50,25 @@ class FakeClient:
self.responses = FakeResponsesResource(response)
def test_call_responses_api_sends_reasoning_and_omits_temperature() -> None:
"""GPT-5.5 requests should use reasoning controls instead of temperature."""
def test_call_responses_api_sends_reasoning_text_controls_and_omits_temperature() -> None:
"""GPT-5.5 requests should use reasoning/text controls instead of temperature."""
fake_client = FakeClient(FakeResponse(output_text=" 0\n"))
actual = call_responses_api(
client=fake_client, # type: ignore[arg-type]
model="gpt-5.5-2026-04-23",
model="gpt-5.5",
reasoning_effort="none",
system_content="Return one number.",
user_text="Classify.",
image_data_urls=[],
max_output_tokens=128,
text_verbosity="low",
)
assert actual.text == "0"
assert fake_client.responses.create_params["reasoning"] == {"effort": "none"}
assert fake_client.responses.create_params["max_output_tokens"] == 128
assert fake_client.responses.create_params["text"] == {"verbosity": "low"}
assert "temperature" not in fake_client.responses.create_params
@@ -87,17 +89,21 @@ def test_call_responses_api_sends_structured_text_format() -> None:
actual = call_responses_api(
client=fake_client, # type: ignore[arg-type]
model="gpt-5.5-2026-04-23",
model="gpt-5.5",
reasoning_effort="low",
system_content="Return JSON.",
user_text="Classify.",
image_data_urls=[],
max_output_tokens=128,
response_text_format=response_text_format,
text_verbosity="low",
)
assert actual.text.startswith("{")
assert fake_client.responses.create_params["text"] == {"format": response_text_format}
assert fake_client.responses.create_params["text"] == {
"format": response_text_format,
"verbosity": "low",
}
def test_call_responses_api_returns_incomplete_diagnostics_for_empty_text() -> None:
@@ -111,7 +117,7 @@ def test_call_responses_api_returns_incomplete_diagnostics_for_empty_text() -> N
actual = call_responses_api(
client=fake_client, # type: ignore[arg-type]
model="gpt-5.5-2026-04-23",
model="gpt-5.5",
reasoning_effort="low",
system_content="Return one number.",
user_text="Classify.",
+8 -3
View File
@@ -22,7 +22,9 @@ YOUR_API_KEY = os.getenv("OPENAI_API_KEY")
if YOUR_API_KEY is None:
raise RuntimeError("OPENAI_API_KEY が環境変数に設定されていません。")
gpt_model = "gpt-5.4"
gpt_model = "gpt-5.5"
reasoning_effort = os.getenv("OPENAI_REASONING_EFFORT", "low")
text_verbosity = os.getenv("OPENAI_TEXT_VERBOSITY", "low")
# =========================
# 入出力ファイル
@@ -355,7 +357,8 @@ def call_gpt(client, model, system_content: str, user_text: str, images_data_url
{"role": "system", "content": system_content},
{"role": "user", "content": content_parts},
],
temperature=0.0,
reasoning={"effort": reasoning_effort},
text={"verbosity": text_verbosity},
max_output_tokens=max_output_tokens,
)
return resp.output_text or ""
@@ -440,6 +443,8 @@ def write_debug_entry(
f.write("=" * 60 + "\n")
f.write(f"DEBUG TIME : {_now_str()}\n")
f.write(f"MODEL : {gpt_model}\n")
f.write(f"REASONING : {reasoning_effort}\n")
f.write(f"VERBOSITY : {text_verbosity}\n")
f.write(f"TIMEFRAME : {timeframe}\n")
f.write(f"TREND_STATE(H4) : {trend_state} (0=RANGE,1=UP,2=DOWN)\n")
f.write(f"SELECTED_STRATEGY : {','.join(str(x) for x in selected_strategies)}\n")
@@ -627,4 +632,4 @@ def _write_outputs(numeric_list):
# 実行
# =========================
if __name__ == "__main__":
run_pipeline()
run_pipeline()
+8 -3
View File
@@ -22,7 +22,9 @@ YOUR_API_KEY = os.getenv("OPENAI_API_KEY")
if YOUR_API_KEY is None:
raise RuntimeError("OPENAI_API_KEY が環境変数に設定されていません。")
gpt_model = "gpt-5.4"
gpt_model = "gpt-5.5"
reasoning_effort = os.getenv("OPENAI_REASONING_EFFORT", "low")
text_verbosity = os.getenv("OPENAI_TEXT_VERBOSITY", "low")
# =========================
# 入出力ファイル
@@ -62,6 +64,8 @@ def write_debug_trend(path: str, current_price: float, numeric_summary: str, gpt
f.write("=" * 60 + "\n")
f.write(f"DEBUG TIME : {_now_str()}\n")
f.write(f"MODEL : {gpt_model}\n")
f.write(f"REASONING : {reasoning_effort}\n")
f.write(f"VERBOSITY : {text_verbosity}\n")
f.write(f"CURRENT PRICE : {current_price:.2f}\n")
f.write("=" * 60 + "\n\n")
f.write("---- NUMERIC SUMMARY START ----\n")
@@ -289,7 +293,8 @@ def call_gpt_trend(client, model, system_content: str, user_text: str, image_dat
{"role": "system", "content": system_content},
{"role": "user", "content": content_parts},
],
temperature=0.0,
reasoning={"effort": reasoning_effort},
text={"verbosity": text_verbosity},
max_output_tokens=max_output_tokens,
)
return (resp.output_text or "").strip()
@@ -485,4 +490,4 @@ def run_pipeline():
# 実行
# =========================
if __name__ == "__main__":
run_pipeline()
run_pipeline()