Files
Bell-PriceActionWithEma-EA/Experts/RSIForceStateEA.mq5
T
2026-05-09 23:16:50 +07:00

160 lines
5.2 KiB
Plaintext

//+------------------------------------------------------------------+
//| RSIForceStateEA - pullback by RSI/EMA9/WMA45 force, EMA200 trend |
//| State machine: NO_TRADE -> WATCHING -> PENDING_ORDER -> IN_TRADE |
//+------------------------------------------------------------------+
#property copyright "RSI Force State EA"
#property version "1.30"
#property strict
// ---- Layer 1: inputs + value types + indicator buffers/handles ----
#include "RSIForceStateEA/Config.mqh"
#include "RSIForceStateEA/State.mqh"
#include "RSIForceStateEA/Indicators.mqh"
// ---- Globals consumed by Layer 2/3 modules ----
EAState g_State = STATE_NO_TRADE;
PendingContext g_Pending;
TradeContext g_OpenTrade;
// ---- Layer 2: trade ops + state machine (depend on globals above) ----
#include "RSIForceStateEA/Trade.mqh"
#include "RSIForceStateEA/StateMachine.mqh"
// ---- Layer 3: visualization (depends on globals + state machine) ----
#include "RSIForceStateEA/Visualizer.mqh"
// Throttle stats panel refresh (deal history scan can be heavy).
datetime g_LastVisualRefresh = 0;
const int kVisualRefreshSeconds = 2;
// ------------------------------------------------------------------
// Input validation (fail fast on misconfiguration)
// ------------------------------------------------------------------
bool ValidateInputs()
{
if (InpRiskPercent <= 0.0 || InpRiskPercent > 50.0)
{ Print("[INIT] InpRiskPercent must be in (0, 50]"); return false; }
if (InpRiskRewardRatio <= 0.0)
{ Print("[INIT] InpRiskRewardRatio must be > 0"); return false; }
if (InpPartialCloseAtR <= 0.0 || InpPartialCloseAtR >= InpRiskRewardRatio)
{ Print("[INIT] InpPartialCloseAtR must be in (0, InpRiskRewardRatio)"); return false; }
if (InpPartialClosePercent <= 0.0 || InpPartialClosePercent >= 100.0)
{ Print("[INIT] InpPartialClosePercent must be in (0, 100)"); return false; }
if (InpPendingMaxAliveBars < 1)
{ Print("[INIT] InpPendingMaxAliveBars must be >= 1"); return false; }
if (InpWatchingMaxBars < 1)
{ Print("[INIT] InpWatchingMaxBars must be >= 1"); return false; }
if (InpSlopeLookbackBars < 2)
{ Print("[INIT] InpSlopeLookbackBars must be >= 2"); return false; }
if (InpMinBarsBetweenCrosses < 1)
{ Print("[INIT] InpMinBarsBetweenCrosses must be >= 1"); return false; }
if (InpSwingLookbackBars < 5)
{ Print("[INIT] InpSwingLookbackBars must be >= 5"); return false; }
if (InpSidewayRSILow >= InpSidewayRSIHigh)
{ Print("[INIT] InpSidewayRSILow must be < InpSidewayRSIHigh"); return false; }
if (InpRSI_EMA9Period >= InpRSI_WMA45Period)
{ Print("[INIT] InpRSI_EMA9Period must be < InpRSI_WMA45Period"); return false; }
if (InpSignalBarShift < 1)
{ Print("[INIT] InpSignalBarShift must be >= 1 (use closed bars)"); return false; }
return true;
}
// ------------------------------------------------------------------
// Lifecycle
// ------------------------------------------------------------------
int OnInit()
{
if (!ValidateInputs()) return INIT_PARAMETERS_INCORRECT;
ZeroMemory(g_Pending);
ZeroMemory(g_OpenTrade);
g_State = STATE_NO_TRADE;
g_HasTradedThisPullback = false;
g_BarsInWatching = 0;
g_LastTrend = TREND_NONE;
g_LastDirTrend = TREND_NONE;
g_LastVisualRefresh = 0;
if (!InitIndicators())
{
Print("[INIT] InitIndicators failed");
return INIT_FAILED;
}
if (!RefreshIndicatorData())
{
Print("[INIT] RefreshIndicatorData failed (need more history)");
return INIT_FAILED;
}
InitTradeOps();
// Seed both trend trackers to current value so we don't fire a false
// "direction flip" event on the first tick.
g_LastTrend = DetectTrend(InpSignalBarShift);
if (g_LastTrend != TREND_NONE) g_LastDirTrend = g_LastTrend;
PrintFormat("[INIT] RSIForceStateEA v1.30 ready | Symbol=%s | TF=%d | startTrend=%s",
_Symbol, (int)_Period, EnumToString(g_LastTrend));
AttachIndicatorsToChart();
DrawAllVisuals(g_LastTrend);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
RemoveAllVisuals();
ReleaseIndicators();
PrintFormat("[DEINIT] reason=%d", reason);
}
// ------------------------------------------------------------------
// Tick loop:
// - tick-level: refresh data, sync broker state, manage open trade
// - bar-level: evaluate state machine on every newly closed bar
// - visuals: refresh dashboard/levels (throttled)
// ------------------------------------------------------------------
void OnTick()
{
if (!RefreshIndicatorData()) return;
SyncStateWithBroker();
if (g_State == STATE_IN_TRADE)
ManagePartialAndBreakEven(g_OpenTrade);
const bool newBar = IsNewBar();
if (newBar) RunStateMachine();
// Throttle the dashboard refresh to avoid excessive history queries.
const datetime now = TimeCurrent();
if (newBar || (now - g_LastVisualRefresh) >= kVisualRefreshSeconds)
{
g_LastVisualRefresh = now;
DrawAllVisuals(DetectTrend(InpSignalBarShift));
}
}
// Trade events can change order/position state between ticks; just resync.
// Visuals will refresh on the next tick (throttled by kVisualRefreshSeconds).
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
SyncStateWithBroker();
}