# SPEC — Apex NAS100 Adaptive Scalper (MT5)

**Single source of truth.** Every subagent implements to these interfaces EXACTLY. No unilateral changes.

## 1. Product definition

A pure-MQL5 (no ML, no Python, no DLLs, no paid deps) scalping EA + companion indicator for NAS100 CFD
(symbol aliases: NAS100, US100, USTEC, NASDAQ.cash, etc.). Dual mode:

- `MODE_AUTO` — places & manages trades itself.
- `MODE_SIGNALS` — relays detailed JSON/CSV/push signals for manual copying to other accounts/platforms/prop firms.
- `MODE_BOTH` — default: trades AND relays.

Design target (engineering goal, not a promise): high monthly return with <10% overall drawdown, prop-firm
rule compliant (daily DD, max DD, consistency, news/rollover guards).

## 2. Repo / file layout

```
project/
  SPEC.md                       (this file, committed)
  Include/Apex/
    Apex_Config.mqh             (WRITTEN BY ORCHESTRATOR — committed at init; do NOT modify)
    Apex_DeltaFlow.mqh          (Agent A)
    Apex_MoneyFlow.mqh          (Agent A)
    Apex_VolumeProfile.mqh      (Agent B)
    Apex_VWAP.mqh               (Agent B)
    Apex_MarketStructure.mqh    (Agent C)
    Apex_AMD.mqh                (Agent C)
    Apex_OrderBook.mqh          (Agent D)
    Apex_Regime.mqh             (Agent D)
    Apex_RiskManager.mqh        (Agent E)
    Apex_TradeExecutor.mqh      (Agent E)
    Apex_SignalEngine.mqh       (Agent F)
    Apex_SignalRelay.mqh        (Agent F)
  Experts/
    Apex_NAS100_EA.mq5          (Agent G, after merge)
  Indicators/
    Apex_NAS100_Indicator.mq5   (Agent G, after merge)
```

## 3. Shared contract (already in Apex_Config.mqh)

Enums: `ENUM_APEX_MODE, ENUM_APEX_REGIME, ENUM_APEX_SESSION, ENUM_APEX_PHASE, ENUM_VWAP_ANCHOR,
ENUM_MS_TREND, ENUM_APEX_SIGNAL_DIR`. Structs: `SZone, SSignal`. Class: `CApexConfig` holding ALL inputs
+ helpers (`Pt(), NormalizeLot(), ResolveSymbol(), InNewsWindow(), InRolloverWindow()`).
Subagents must `#include "Apex_Config.mqh"` and read it before coding. Do not redefine these.

## 4. MQL5 correctness rules (MANDATORY — no compiler available, static correctness is on you)

1. `#property strict` in .mq5 files; include guards in every .mqh: `#ifndef __APEX_X_MQH__ / #define / #endif`.
2. Time series arrays: call `ArraySetAsSeries(arr,true)` immediately after CopyRates/CopyBuffer/CopyTickVolume.
3. Check return counts of every Copy* call; bail gracefully on `< needed`.
4. Functions CANNOT return arrays/strings-by-value pitfalls: strings are OK to return; arrays only via
   reference params. Structs returned by value must contain NO dynamic arrays (SSignal/SZone are POD).
5. No `extern`; use members. No global mutable state except module instances in main files.
6. Normalize all prices with `NormalizeDouble(p,_Digits)`; volumes via `cfg.NormalizeLot()`.
7. History access via `CopyRates` with `PERIOD_M5` (configurable) — do not rely on the chart timeframe only.
8. `iBars`, `iTime` etc. need symbol first arg in MQL5.
9. DOM: `MarketBookAdd(sym)` in Init, `MarketBookRelease(sym)` in Deinit, data from OnBookEvent or
   `MarketBookGet`. Must degrade gracefully when broker provides no book.
10. No web calls, no DLL, no external libs. Only standard MQL5 + this repo's includes.
11. Every file ends with a `// SELF-CHECK` comment: list each MQL5 API used + why the call is valid.
12. Division guards: never divide without checking denominator > 0.

## 5. Module contracts

### 5.1 CDeltaFlow (Apex_DeltaFlow.mqh) — Agent A
Buy/sell pressure per bar. Historical bars: estimate split of tick volume between buy/sell using the
bar's close-location-in-range: `buyShare = (C-L)/(H-L==0?0.5:...)` blended 50/50 with sign(C-O) rule.
Forming bar: accumulate real tick rule via CopyTicks (uptick=buy, downtick=sell, unchanged=carry last).
```cpp
class CDeltaFlow {
public:
  bool   Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void   Update();                        // call each tick; internally heavy work only on new bar
  double Delta(const int shift);          // buyVol - sellVol of bar shift (0=forming)
  double Pressure(const int shift);       // (buyVol-sellVol)/(buyVol+sellVol), -1..1
  double BuyVol(const int shift);
  double SellVol(const int shift);
  double CumDelta();                      // cumulative delta since session/day start
  double CumDeltaSlope(const int bars);   // CumDelta now minus value 'bars' ago
  bool   BullishDivergence();             // price LL but delta HL over cfg.DivergenceLookback
  bool   BearishDivergence();             // price HH but delta LH
  bool   DeltaFlipUp();                   // delta crossed >0 on last closed bar after <0 streak
  bool   DeltaFlipDown();
};
```

### 5.2 CMoneyFlow (Apex_MoneyFlow.mqh) — Agent A
Money-flow sentiment: raw money flow = typical price × tick volume. MFI(14-style, length from cfg),
positive/negative money-flow ratio, per-bar signed money pressure, and "high-traded nodes":
price bins with top-decile money volume over the rolling profile window.
```cpp
class CMoneyFlow {
public:
  bool   Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void   Update();
  double MFI();                           // 0..100 money flow index
  double MoneyRatio();                    // posMF/negMF over cfg.MFILength
  double Pressure(const int shift);       // signed money-flow pressure -1..1 per bar
  bool   IsHighTradedNode(const double price);  // price inside a top-decile money-volume bin
  double NodeStrength(const double price);// 0..1 rank of the bin containing price
  bool   MoneyFlowBullish();              // MFI rising && MFI>50 && ratio>1
  bool   MoneyFlowBearish();
};
```

### 5.3 CVolumeProfile (Apex_VolumeProfile.mqh) — Agent B
Rolling profile over cfg.ProfileBars M5 bars, cfg.ProfileRows price bins between window high/low.
Distribute each bar's tick volume across the bins its [L..H] covers (uniform split — documented,
deterministic). POC = max bin. Value area: expand from POC to neighbors until >= cfg.ValueAreaPct
(default 70%). HVN = bin vol >= 1.5× average; LVN = bin vol <= 0.5× average.
```cpp
class CVolumeProfile {
public:
  bool   Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void   Update();                        // rebuild on new bar, cheap otherwise
  double POC(); double VAH(); double VAL(); double Low(); double High();
  bool   IsHVN(const double price);
  bool   IsLVN(const double price);
  double NearestHVN(const double price, const bool above); // 0 if none
  double NearestLVN(const double price, const bool above);
  int    NodeCount();                     // = rows, for the indicator
  double NodePrice(const int i);
  double NodeVol(const int i);
  bool   Valid();
};
```

### 5.4 CVwapEngine (Apex_VWAP.mqh) — Agent B
Anchored VWAPs: DAILY (server day), WEEKLY (Mon 00:00 server), MONTHLY (1st), QUARTERLY (Jan/Apr/Jul/Oct 1st).
VWAP = Σ(TP×V)/ΣV over anchor window, TP=(H+L+C)/3, V=tick volume (real volume if feed has it — detect once).
Bands: volume-weighted variance σ, bands at ±1σ, ±2σ.
```cpp
class CVwapEngine {
public:
  bool   Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void   Update();
  double Value(const ENUM_VWAP_ANCHOR a);
  double Band(const ENUM_VWAP_ANCHOR a, const int sigma /*1|2*/, const int dir /*+1|-1*/);
  int    PriceSide(const ENUM_VWAP_ANCHOR a);      // +1 above, -1 below, 0 n/a
  double QuarterlyBias();                          // -1..1 composite: price vs QVWAP, vs monthly, Q progress
  bool   Valid();
};
```

### 5.5 CMarketStructure (Apex_MarketStructure.mqh) — Agent C
Swing points: fractal-style with cfg.SwingLen bars each side (swing) and 2 bars (internal).
BOS: close beyond last swing point in trend direction. CHoCH: close beyond counter-swing point.
Equal highs/lows: 2+ swing highs/lows within cfg.EqTolATR×ATR. FVG: 3-bar gap (bull: low[i] > high[i+2]),
min size cfg.FvgMinATR×ATR, track mitigation (price re-entering). Order blocks: last opposite-color bar
before a BOS impulse; volumetric = store OB volume vs avg volume (strength 0..2). Internal OB uses
internal structure; swing OB uses swing structure. Dealing range = last confirmed swing high+low;
premium > equilibrium, discount < equilibrium. D/W/M (current+previous) highs/lows.
```cpp
class CMarketStructure {
public:
  bool  Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void  Update();
  ENUM_MS_TREND SwingTrend();
  ENUM_MS_TREND InternalTrend();
  bool  BullBOS();  bool BearBOS();               // within cfg.SignalValidBars
  bool  BullCHoCH(); bool BearCHoCH();
  int   SwingHighCount(); int SwingLowCount();
  double SwingHigh(const int i); double SwingLow(const int i);  // i=0 most recent, 0 if none
  bool  HasEQH(double &level); bool HasEQL(double &level);
  int   FVGCount();  bool GetFVG(const int i, SZone &z);        // z.tag: "FVG_B"/"FVG_B"
  int   OBCount();   bool GetOB(const int i, SZone &z);         // z.tag: "OBI_B","OBI_S","OBS_B","OBS_S"
  bool  PriceInBullishOB(const double price); bool PriceInBearishOB(const double price);
  bool  PriceInBullishFVG(const double price); bool PriceInBearishFVG(const double price);
  double DayHigh();   double DayLow();   double PrevDayHigh();  double PrevDayLow();
  double WeekHigh();  double WeekLow();  double MonthHigh();    double MonthLow();
  double RangeHigh(); double RangeLow(); double Equilibrium();
  bool  InPremium(const double price);  bool InDiscount(const double price);
  bool  SweepUp();   bool SweepDown();  // recent raid of EQH/EQL or swing then reclaim close
  bool  Valid();
};
```

### 5.6 CAMDTracker (Apex_AMD.mqh) — Agent C
Accumulation: price rising over lookback AND volume rising (vol slope>0 & price slope>0), or tight range
at lows with rising cum delta. Distribution: price falling AND volume rising. Manipulation: liquidity
sweep (from CMarketStructure.SweepUp/Down) + delta flip against the sweep + close back inside range.
```cpp
class CAMDTracker {
public:
  bool  Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg,
             CDeltaFlow *df, CMarketStructure *ms);
  void  Update();
  ENUM_APEX_PHASE Phase();
  double Confidence();            // 0..1
  bool  Accumulating(); bool Distributing(); bool Manipulating();
  string Describe();              // short human text for dashboard/relay
};
```

### 5.7 COrderBookFeed (Apex_OrderBook.mqh) — Agent D
Free Level-2 equivalent: native MT5 DOM via MarketBookAdd/Get (broker-dependent). Compute depth
imbalance, best bid/ask, spread. When DOM unavailable: degrade to spread/tick-speed proxies only;
Available()=false for depth but spread still returned.
```cpp
class COrderBookFeed {
public:
  bool   Init(const string sym);
  void   Deinit();
  void   OnBookEvent();                     // call from EA OnBookEvent
  void   Update();                          // tick fallback if no book events
  bool   DepthAvailable();
  double Imbalance();                       // (bidVol-askVol)/(bidVol+askVol), -1..1, 0 if n/a
  double BidDepthLots(); double AskDepthLots();
  double SpreadPoints();
  double TickSpeed();                       // ticks per second, smoothed — tape proxy
};
```

### 5.8 CRegimeEngine (Apex_Regime.mqh) — Agent D
Sessions from server time + cfg.GMTOffset: Asia 00-07, London 07-12, Overlap 12-16, NewYork 12-21,
OffHours else (London/NY defined on GMT). Volatility: ATR(cfg.ATRLen) + its percentile over 20 days.
Trend: ADX-proxy via Wilder DM smoothing or efficiency ratio Kaufman over cfg.TrendLen.
Regime enum: TREND_UP/TREND_DOWN (ER>0.35 & slope), RANGE (ER<0.2), BREAKOUT (ATR spike>1.5× + range
expansion), QUIET (ATR pctl<0.2). Momentum: ROC normalized -1..1. Market quarter 1..4 + progress.
News windows from cfg.NewsWindowsCSV "HH:MM-HH:MM;..." server time. Rollover: 5 min around 00:00 server.
Friday cutoff hour. `VolFactor()` 0.5..1.5 scaling SL/TP/score.
```cpp
class CRegimeEngine {
public:
  bool   Init(const string sym, const ENUM_TIMEFRAMES tf, const CApexConfig &cfg);
  void   Update();
  ENUM_APEX_REGIME Regime();
  ENUM_APEX_SESSION Session();
  double ATR(); double ATRPercentile();     // 0..1
  double TrendStrength();                   // 0..100 ADX-proxy
  double EfficiencyRatio();                 // -1..1 signed
  double Momentum();                        // -1..1
  double VolFactor();                       // 0.5..1.5
  int    MarketQuarter(); double QuarterProgress();
  bool   TradingAllowedNow();               // session filter + news + rollover + friday cutoff
  string DenyReason();                      // why TradingAllowedNow()==false
};
```

### 5.9 CRiskManager (Apex_RiskManager.mqh) — Agent E
Prop-firm grade. Baseline balance: first-run balance (persisted) or cfg.PropBaselineBalance if >0.
Daily DD% = (dayStartEquity - equity)/dayStartEquity vs cfg.MaxDailyDDPct (halt at 90% of limit).
Overall DD% = (baseline - equity)/baseline vs cfg.MaxOverallDDPct (halt at 90%).
Consistency: block new entries if todayProfitPct > cfg.DailyProfitCapPct (prop mode).
Max trades/day, max concurrent positions, max spread. Persistence: GlobalVariables (prefix "APEX_")
+ file fallback MQL5\Files\Apex\risk_state.dat. Day rollover detection by server date.
```cpp
class CRiskManager {
public:
  bool   Init(const string sym, const CApexConfig &cfg);
  void   Update();                          // per tick: equity track, day rollover, halt flag
  bool   CanOpen(string &why);              // all gates incl. spread + counts + DD
  double CalcLot(const double entry, const double sl);  // risk% sizing, normalized, min/max clamp
  void   NotifyOpened();
  void   NotifyClosedDeal(const double profit);         // EA feeds from OnTradeTransaction
  double DailyDD(); double OverallDD();                 // current % values
  bool   Halted();  string HaltReason();
  void   Persist(); void Restore();
};
```

### 5.10 CTradeExecutor (Apex_TradeExecutor.mqh) — Agent E
Market execution with CTrade (MQL5 standard, allowed). SL from SSignal. Partial management:
TP1 → close cfg.TP1ClosePct, move SL to BE + cfg.BEBufferATR×ATR; TP2 → close pct, trail remainder
by ATR(cfg.ATRLen)×cfg.TrailATRMult from highest watermark; TP3 final. One position per direction,
magic+symbol filtered. Deviation cfg.MaxSlippagePoints.
```cpp
class CTradeExecutor {
public:
  bool Init(const string sym, const CApexConfig &cfg);
  bool Execute(const SSignal &sig, const double lots);
  void Manage(const double atr);            // per tick: partials, BE, trail
  void CloseAll(const string reason);
  int  OpenCount();
  bool HasOpenPosition();
};
```

### 5.11 CSignalEngine (Apex_SignalEngine.mqh) — Agent F
Weighted confluence score 0..100. Weights from cfg (each 0..100, normalized internally):
- VWAP stack (price vs daily/weekly + quarterly bias) 15
- Delta flow (pressure, cumdelta slope, divergence, flip) 15
- Money flow (MFI side, ratio, high-traded node confluence) 10
- Volume profile (POC/VAH/VAL reaction, LVN breakout, HVN rejection) 12
- Market structure (BOS/CHoCH direction, premium/discount, OB/FVG touch, EQH/EQL sweep) 20
- AMD phase alignment 10
- Regime (trend alignment, momentum, vol factor) 10
- Order book imbalance + spread quality 8
Long logic mirror of short. Entry: market. SL: beyond invalidating structure (OB far edge / swing)
clamped to [cfg.SLMinATR, cfg.SLMaxATR]×ATR. TP1/2/3 = RR 1/2/3 (scaled by VolFactor).
Adaptive MinScore: cfg.MinScoreBase adjusted ±10 by regime (RANGE→+10, BREAKOUT→-5, QUIET→no trade).
Signal validity: fresh (bar-based) triggers only; dedupe by bar time+direction.
```cpp
class CSignalEngine {
public:
  bool Init(const CApexConfig &cfg);
  void Bind(CDeltaFlow *df, CMoneyFlow *mf, CVolumeProfile *vp, CVwapEngine *vw,
            CMarketStructure *ms, CAMDTracker *amd, COrderBookFeed *ob, CRegimeEngine *rg);
  SSignal Evaluate();                       // fills SSignal; valid=false if no trade
  double LastScore();  string LastReasons(); double CurrentMinScore();
};
```

### 5.12 CSignalRelay (Apex_SignalRelay.mqh) — Agent F
Emit on every valid SSignal (all modes):
- JSON: `MQL5\Files\<cfg.RelayFolder>\signal_<yyyymmdd_hhmmss>_<BUY|SELL>.json`
- CSV append: `signals.csv` (header once)
- Push: `SendNotification` if cfg.SendPush (message <= 255 chars)
JSON schema (exact keys):
```json
{"ea":"ApexNAS100","version":"1.0","symbol":"NAS100","time":"2026-07-31 12:00:00","direction":"BUY",
 "entry":0.0,"stop_loss":0.0,"take_profit_1":0.0,"take_profit_2":0.0,"take_profit_3":0.0,
 "risk_reward":0.0,"score":0.0,"min_score":0.0,"regime":"TREND_UP","session":"NEWYORK","phase":"ACCUMULATION",
 "atr":0.0,"spread_points":0.0,"reasons":["..."],"expiry_seconds":900}
```
```cpp
class CSignalRelay {
public:
  bool Init(const CApexConfig &cfg);
  void Emit(const SSignal &sig, const string symbol);
};
```

## 6. Main EA (Apex_NAS100_EA.mq5) — Agent G (after merge)
Inputs: full list mirrored from CApexConfig (EA inputs → cfg loader function `void LoadConfig(CApexConfig&)`).
OnInit: resolve symbol, init all modules, seed history (CopyRates warmup), Restore risk.
OnTick: update orderbook-lite, delta(light), regime; on new M5 bar → full update of all modules →
engine.Evaluate() → if sig.valid && risk.CanOpen && regime.TradingAllowedNow:
MODE_AUTO/BOTH → executor.Execute(sig, risk.CalcLot); MODE_SIGNALS/BOTH → relay.Emit.
executor.Manage() every tick. risk.Update() every tick; if risk.Halted → CloseAll.
OnBookEvent → orderbook.OnBookEvent. OnTradeTransaction → risk.NotifyClosedDeal on DEAL_ENTRY_OUT.
OnDeinit → Persist, orderbook.Deinit.
Chart dashboard via Comment(): mode, regime/session/phase, score/minScore, daily DD, overall DD,
book imbalance, open P/L, last signal+reasons, halt state.

## 7. Indicator (Apex_NAS100_Indicator.mq5) — Agent G
Same modules, read-only: draws D/W/M/Q VWAP lines (+1σ bands daily), profile histogram on right margin
(POC/VAH/VAL highlighted), active OB/FVG rectangles, EQH/EQL lines, D/W/M H-L lines, signal arrows
(engine in signals mode), dashboard panel (same fields as EA). No trading, no file writes.

## 8. Coding style
Header comment block per file (name, purpose, author "Apex Orchestrator", date 2026-07-31).
Prefix public methods exactly as specced. Comments at every non-obvious decision.
Target: EA total < 4000 lines across files; modules 150–450 lines each.
