Files
Bell-PriceActionWithEma-EA/Experts/EA_ICT_CL/StateMachine.mqh
T
2026-03-10 13:47:42 +07:00

204 lines
6.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef EA_ICT_CL__STATE_MACHINE_MQH
#define EA_ICT_CL__STATE_MACHINE_MQH
// Module: StateMachine
// State transitions + handlers + runner.
// Extracted from EA_ICT_CL.mq5 (Sections 4, 9, 10).
// NOTE: Uses EA globals + functions from Signals/Trade modules.
// Include AFTER Signals_BOS_FVG_OB.mqh and Trade.mqh.
//+------------------------------------------------------------------+
//| SECTION 4 STATE MACHINE HELPERS |
//+------------------------------------------------------------------+
inline void TransitionTo(EAState next)
{
if (InpDebugLog)
PrintFormat("[STATE] %s → %s", EnumToString(g_State), EnumToString(next));
g_State = next;
}
inline void ResetToIdle(string reason = "")
{
if (InpDebugLog && reason != "")
PrintFormat("[RESET→IDLE] %s", reason);
g_ActiveFVGIdx = -1;
g_TradeBarIndex = -1;
g_PendingTicket = 0;
ZeroMemory(g_OrderPlan);
TransitionTo(EA_IDLE);
}
//+------------------------------------------------------------------+
//| SECTION 9 STATE HANDLERS |
//+------------------------------------------------------------------+
inline void OnStateIdle()
{
int idx = GetBestActiveFVGIdx();
if (idx < 0) return;
g_ActiveFVGIdx = idx;
if (InpDebugLog)
PrintFormat("[ACTIVE FVG] #%d %s [%.5f%.5f] %s",
g_FVGPool[idx].id, EnumToString(g_FVGPool[idx].direction),
g_FVGPool[idx].low, g_FVGPool[idx].high, EnumToString(g_FVGPool[idx].status));
TransitionTo(g_FVGPool[idx].status == FVG_TOUCHED ? EA_WAIT_TRIGGER : EA_WAIT_TOUCH);
}
inline void OnStateWaitTouch()
{
if (g_ActiveFVGIdx < 0) { ResetToIdle("active lost"); return; }
int ai = g_ActiveFVGIdx;
if (g_FVGPool[ai].status == FVG_USED)
{ ResetToIdle(StringFormat("FVG #%d broken/expired", g_FVGPool[ai].id)); return; }
if (g_FVGPool[ai].status == FVG_TOUCHED)
{ TransitionTo(EA_WAIT_TRIGGER); return; }
int better = GetBestActiveFVGIdx();
if (better >= 0 && better != ai)
{
bool bTouch = (g_FVGPool[better].status == FVG_TOUCHED);
bool bNewer = (g_FVGPool[better].createdTime > g_FVGPool[ai].createdTime);
if (bTouch || bNewer)
{
if (InpDebugLog)
PrintFormat("[SWITCH] FVG #%d → #%d", g_FVGPool[ai].id, g_FVGPool[better].id);
g_ActiveFVGIdx = better;
if (bTouch) TransitionTo(EA_WAIT_TRIGGER);
}
}
}
inline void OnStateWaitTrigger()
{
if (g_ActiveFVGIdx < 0) { ResetToIdle("active lost"); return; }
int ai = g_ActiveFVGIdx;
if (g_FVGPool[ai].status == FVG_USED)
{
if (g_FVGPool[ai].usedCase == 2)
{
if (InpDebugLog)
PrintFormat("[ENTRY SIGNAL] FVG #%d %s [%.5f%.5f] | MSS entry=%.5f SL=%.5f",
g_FVGPool[ai].id, EnumToString(g_FVGPool[ai].direction),
g_FVGPool[ai].low, g_FVGPool[ai].high,
g_FVGPool[ai].mssEntry, g_FVGPool[ai].mssSL);
if (BuildOrderPlan(g_FVGPool[ai].id, g_FVGPool[ai].direction,
g_FVGPool[ai].mssEntry, g_FVGPool[ai].mssSL))
{
ulong ticket = ExecuteLimitOrder();
if (ticket > 0)
{
g_PendingTicket = ticket;
g_TradeBarIndex = Bars(_Symbol, InpTriggerTF);
TransitionTo(EA_IN_TRADE);
}
else
ResetToIdle(StringFormat("FVG #%d order failed", g_FVGPool[ai].id));
}
else
ResetToIdle(StringFormat("FVG #%d invalid plan", g_FVGPool[ai].id));
}
else
ResetToIdle(StringFormat("FVG #%d case%d", g_FVGPool[ai].id, g_FVGPool[ai].usedCase));
return;
}
if (g_FVGPool[ai].status == FVG_PENDING) { TransitionTo(EA_WAIT_TOUCH); return; }
}
inline void OnStateInTrade()
{
if (g_PendingTicket > 0)
{
bool foundPending = false;
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderGetTicket(i) == g_PendingTicket) { foundPending = true; break; }
}
if (!foundPending)
{
bool posFound = false;
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong pt = PositionGetTicket(i);
if (pt > 0 && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber
&& PositionGetString(POSITION_SYMBOL) == _Symbol)
{
posFound = true;
g_PendingTicket = 0;
if (InpDebugLog) PrintFormat("[TRADE] Filled → position #%llu", pt);
break;
}
}
if (!posFound)
{
HistorySelect(TimeCurrent() - 86400, TimeCurrent());
bool cancelled = false;
for (int i = HistoryOrdersTotal() - 1; i >= 0; i--)
{
ulong ht = HistoryOrderGetTicket(i);
if (ht == g_PendingTicket)
{
long st = HistoryOrderGetInteger(ht, ORDER_STATE);
if (st == ORDER_STATE_CANCELED || st == ORDER_STATE_EXPIRED || st == ORDER_STATE_REJECTED)
cancelled = true;
break;
}
}
if (cancelled) { ResetToIdle("order cancelled"); return; }
bool closed = false;
for (int i = HistoryDealsTotal() - 1; i >= 0; i--)
{
ulong dt = HistoryDealGetTicket(i);
if (HistoryDealGetInteger(dt, DEAL_MAGIC) == InpMagicNumber
&& HistoryDealGetString(dt, DEAL_SYMBOL) == _Symbol
&& HistoryDealGetInteger(dt, DEAL_ENTRY) == DEAL_ENTRY_OUT)
{
double profit = HistoryDealGetDouble(dt, DEAL_PROFIT);
if (InpDebugLog) PrintFormat("[TRADE] Closed | profit=%.2f", profit);
closed = true; break;
}
}
ResetToIdle(closed ? "trade closed" : "order lost");
return;
}
}
return;
}
bool hasPos = false;
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong pt = PositionGetTicket(i);
if (pt > 0 && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber
&& PositionGetString(POSITION_SYMBOL) == _Symbol)
{ hasPos = true; break; }
}
if (!hasPos) ResetToIdle("position closed");
}
//+------------------------------------------------------------------+
//| SECTION 10 STATE MACHINE RUNNER |
//+------------------------------------------------------------------+
inline void RunStateMachine()
{
UpdateFVGStatuses();
ScanAndRegisterFVGs();
switch (g_State)
{
case EA_IDLE: OnStateIdle(); break;
case EA_WAIT_TOUCH: OnStateWaitTouch(); break;
case EA_WAIT_TRIGGER: OnStateWaitTrigger(); break;
case EA_IN_TRADE: OnStateInTrade(); break;
}
}
#endif // EA_ICT_CL__STATE_MACHINE_MQH