Harden HIT entry and manager state handling
This commit is contained in:
@@ -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起動を処理する関数
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
@@ -1068,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user