# PHASE 0: Code Review - Tommy-123_Pattern_Scanner_V8.0_Full.mq5

Reviewed: 2026-09-22. File: `source/Tommy-123_Pattern_Scanner_V8.0_Full.mq5`
(1839 lines, 61,110 bytes, UTF-8, CRLF). Read in full, every line.

Method: full manual read plus automated checks (duplicate-signature scan,
brace/paren balance, dead-symbol scan, trade-call scan). There is no MQL5
compiler in this environment, so nothing here is a compiler verdict; every
finding below is proven by reading the source.

## Verdict

The file is a dashboard scanner, not a trade-executing EA. It cannot compile
as-is because of duplicate function definitions. It also carries real logic
defects that would corrupt its data at runtime even after the duplicates are
removed. None of the defects require the EA to exist to fix; all are in the
scanner file itself.

## F1. Duplicate function definitions (compile blockers) - CONFIRMED

Tommy reported this himself on Sept 15 ("Functions are defined twice"). He
was right. Two functions are defined twice with identical signatures:

1. `double CalculateMLConfidence(int index)`
   - First definition: line 609 (header comment at 605-607 reads
     "Calculate ML Confidence (from EA MLPredict) - SINGLE DEFINITION").
     Reads features via struct COPY:
     `MLPatternFeatures f; f = g_pairs[index].currentPattern.features;`
   - Second definition: line 1163 (header comment at 1159-1161, no
     "SINGLE DEFINITION" mark). Reads features via reference:
     `MLPatternFeatures &f = g_pairs[index].currentPattern.features;`
   - The math in the two bodies is otherwise identical.

2. `void CalculateOverallStatus(int index)`
   - First definition: line 651 (header comment at 647-649 reads
     "Calculate Overall Status - SINGLE DEFINITION"). Accesses
     `g_pairs[index]` directly throughout.
   - Second definition: line 1203 (header comment at 1199-1201 reads
     "Calculate Overall Status    1" - the stray "1" is a leftover of a
     bad copy-paste merge). Aliases `PairAnalysis &pa = g_pairs[index];`
     and uses `pa` throughout.
   - The logic in the two bodies is otherwise identical.

MQL5 forbids two definitions of the same function signature in one file, so
this file cannot compile. The kept copies are the first ones (609 and 651):
they carry the "SINGLE DEFINITION" marks and avoid the
reference-to-array-element-member access the file's own comments flag as
problematic ("FIXED: Use pointer or copy, not reference to array element",
lines 605-607 and 653-655).

## F2. SortPairsByPriority corrupts index-keyed indicator data - CONFIRMED

- `SortPairsByPriority()` (line 1341) bubble-sorts the `g_pairs[]` array
  itself by swapping whole `PairAnalysis` structs (lines 1389-1393).
- But the indicator handles (`g_atrHandleTrend[]`, `g_rsiHandle[]`,
  `g_smaTrendHandle[]`, etc.) are created in `OnInit()` (lines 344-377)
  keyed to the ORIGINAL symbol order and are never re-sorted.
- `OnCalculate()` (line 437) runs `AnalyzePairFull(i)` for every index
  BEFORE `SortPairsByPriority()` (line 456).
- Consequence: on the first tick everything lines up. On every later tick,
  `AnalyzePairFull(i)` reads `g_pairs[i].symbol` in the new sorted order
  while `g_atrHandleTrigger[i]`, `g_rsiHandle[i]`, etc. still belong to
  whatever symbol originally sat at index i. ATR/RSI/MA/volume data is
  attributed to the wrong symbols from tick 2 onward. Dashboard numbers,
  pattern detection, and alerts are all computed from mismatched data.
- Fix direction: never reorder `g_pairs[]`; sort a separate display-order
  index array instead.

## F3. Scanner can never reach TRADE READY with ML disabled - CONFIRMED

- `AnalyzePairFull()` (lines 501-509): when `InpEnableML` is false,
  `mlConfidence` is set to 0.5.
- `CalculateOverallStatus()` (lines 708-711, kept copy): the STRONG/FULL
  branch requires `pa.mlConfidence >= InpMLMinConfidence` (default 0.55).
- 0.5 < 0.55, so with ML off the status can only ever be "PATTERN
  FORMING - MONITOR" or "NO VALID SETUP". Turning the ML input off silently
  disables the scanner's top two status levels.
- Fix direction: apply the ML confidence gate only when ML is enabled.

## F4. CheckRSICondition is a tautology (filter is a no-op) - CONFIRMED

- `CheckRSICondition()` (lines 820-838):
  `bullishOK = rsi <= InpRsiOverbought` (default 65);
  `bearishOK = rsi >= InpRsiOversold` (default 35);
  returns `bullishOK || bearishOK`.
- For ANY RSI value 0-100, at least one of those is true, so this function
  always returns true. The RSI "filter" at the pair level filters nothing.
  The comment admits it: "Will be refined based on pattern direction."
- The effective RSI gate lives inside `DetectPatternForPair()` (lines
  907-909 and 949-951): buy rejected if RSI > 65, sell rejected if RSI < 35.
  That inner gate works; the outer one is dead weight.
- Fix direction: leave the working inner gate alone; do not pretend the
  outer filter does anything.

## F5. GetPipSize 4-digit branch is 100x off - CONFIRMED by arithmetic

- `GetPipSize()` (lines 291-310):
  `if(digits == 4) return point * 100;`
- On a 4-digit symbol, `SYMBOL_POINT` is 0.0001 and one pip IS 0.0001
  (point * 1). Returning point * 100 = 0.01 makes `riskPips`/`rewardPips`
  (lines 930-933, 979-982) read 100x too small, and makes
  `ZONE_MODE_FIXED_PIPS` (line 809: `InpZoneSizePips * GetPipSize(sym)`)
  build a pullback zone 100x too wide.
- The 5/3-digit branch (`point * 10`) is correct by the same arithmetic.
- The 2-digit branch (`point * 100`) is left as-is: pip conventions on
  2-digit metals vary by broker and need live confirmation, flagged here
  rather than changed.
- Fix direction: 4-digit branch returns `point`.

## F6. Vestigial EA inputs that do nothing in the scanner - CONFIRMED

Declared but never read anywhere in the file (declaration is the only
occurrence):
- `InpPatternExpiryBars` (line 69) - patterns never expire.
- `InpMLLookbackTrades` (line 79), `InpMLLotBoost` (line 81),
  `InpMLLotReduce` (line 82) - lot logic belongs to the EA; a scanner
  sizes nothing.
- `InpFontSize` (line 97) - dashboard font sizes are hardcoded
  (lines 1449-1566).
- `g_lastMLUpdate` (line 236) - written never, read never.
- `InpMLMode` (line 78) is read once, only to print the mode string on the
  dashboard (line 1454); it changes no behavior.

## Minor notes (not blockers)

- N1. `#property strict` (line 9) is an MQL4-era directive; harmless in
  MQL5, which is strict by default.
- N2. `#include <Trade\SymbolInfo.mqh>` (line 13) and
  `#include <Math\Stat\Math.mqh>` (line 14): neither library's classes are
  used. `MathSrand/MathRand/MathExp` are built-in MQL5 functions. Harmless
  dead includes.
- N3. `AnalyzeTimeframeState()` (lines 522-607) creates fresh iMA/iRSI
  handles for the trigger/precision timeframes on EVERY call (lines 547-564)
  and reads buffer position 0 immediately. A brand-new handle may have no
  data yet, so `GetMAValue` can return 0 (forcing `isUptrend = true` since
  close > 0) and `GetRSIValue` falls back to 50. Transient misreads on early
  ticks; also wasteful (handle churn every 2 seconds per pair).
- N4. `DetectPatternForPair()` (line 861) zero-checks only `h1`/`l1`
  (line 881) before using `h2/l2/o2/c2/o3/c3`; thin history on the trigger
  timeframe can feed zeros into the pattern math.
- N5. `CalculatePatternFeatures()` (lines 997-1070): `f.dayOfWeek =
  (dt.day_of_week - 1) / 5.0` yields -0.2 on Sundays. Cosmetic ML feature
  skew.
- N6. `CheckForAlerts()` (lines 1380-1440): the re-arm threshold
  `g_pairs[i].confidence < 60` (line 1389) is hardcoded and unrelated to
  `InpMinConfidenceForAlert` (default 70).
- N7. `PlaySound("buy.wav"/"sell.wav")` (line 1428): fails silently if the
  wav files are not installed. Cosmetic.
- N8. Brace/paren balance check: 119/119, 677/677 - clean. 858 semicolons.

## Trade-execution audit

Grep for `OrderSend`, `CTrade`, `PositionOpen`, `OrderCalc`, `HistoryDeal`
returns zero hits. `CheckExistingTrade()` (line 1302) is read-only
(`PositionsTotal`/`PositionGetTicket`/`PositionGetDouble`); it displays live
position info on the dashboard but opens, modifies, and closes nothing.
The file is scanner-only. There is no live-trading code path to disable.

## What Phase 1 must do (hand-off)

1. Delete the second copies: lines 1160-1298 (both duplicate function
   blocks plus their header comments). Keep lines 609 and 651.
2. Fix F2: sort a display-order index array, never `g_pairs[]` itself.
3. Fix F3: gate the ML confidence threshold on `InpEnableML`.
4. Fix F5: 4-digit `GetPipSize` branch returns `point`.
5. Leave F4's inner RSI gate exactly as-is; document the outer no-op.
6. Leave F6 inputs declared (harmless) but documented as vestigial.
7. Rebrand visible strings to Miroscope; keep behavior identical otherwise.
8. No compiler exists here; MetaEditor compilation is still required and
   must be reported as unverified.
