bin/Custom/AddOns/SentinelLogService_v1_0_0.cs
| Family | AddOns / runtime |
| Version | 1.0.0 |
| Size | 522 lines |
| Scope | public — ships in sentinel-suite |
| Class | SentinelLogService |
| Namespace | NinjaTrader.NinjaScript.AddOns.Sentinel |
| Documented by | no doc tracks this artifact |
Rendered from the published copy in
sentinel-suite/src/, not the author’s private tree — so this page describes the file you actually have.
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
one at https://mozilla.org/MPL/2.0/.
Copyright (c) 2026 silentsudo-io and the Sentinel Suite contributors.
─────────────────────────────────────────────────────────────────────────────
═════════════════════════════════════════════════════════════════════════════
SentinelLogService — headless tier-1 zero-touch MAE/MFE capture (NT8 AddOn)
File: SentinelLogService_v1_0_0.cs
Service version: v1.0.0 (pairs with SentinelLogEngine schema 1.0)
─────────────────────────────────────────────────────────────────────────────
WHAT THIS IS
A headless, always-on AddOn service. It runs as a singleton for the lifetime of
the NinjaTrader process (AddOnBase auto-starts at platform launch / recompile).
NO WINDOW — the future dashboard window attaches to this service; closing a window
must never stop logging. (Architecture decision: service runs always.)
WHAT IT DOES (tier-1, zero-touch — spec §2)
For accounts/strategies the trader has enabled for logging, it logs MAE/MFE WITHOUT
any change to those strategies:
• Subscribes to every Account's PositionUpdate (and ExecutionUpdate for fills).
• On a position going flat -> open, opens an SentinelLogEngine record (tier 1, no ctx) and
starts a raw-tick MarketData subscription for that instrument.
• Each incoming tick updates running raw MAE/MFE against the entry price.
• On open -> flat, finalizes the engine record (one JSONL line) and releases the
market-data subscription.
WHY RAW TICKS (verified, spec §11.2)
BarsRequest data "cannot be used as input for an indicator" and isn't synchronized to
a strategy's series, and subfolders/bar-type matching is brittle. So tier-1 measures
RAW-TICK high/low excursion against entry — universal, faithful, no bar-type coupling.
(Tier-2 strategies still supply bar/HA excursion + ctx from inside themselves.)
VERIFIED API NOTES (NT8 help guide + forum; see spec §11)
• AddOnBase auto-starts as a singleton; OnStateChange fires for Active/Terminated.
GOTCHA: the base constructor calls OnStateChange BEFORE the subclass constructor —
so we DO NOT rely on field initializers; we lazily init inside OnStateChange.
• Position is authoritative only inside PositionUpdate (Account.Positions can be stale
inside ExecutionUpdate). Event order (exec vs position) is NOT guaranteed.
• Flat is signaled by Operation.Remove on the position update.
• MarketData (Level-1) updates arrive on a BACKGROUND thread — this service does no UI,
so no Dispatcher needed here; a future dashboard marshals to its own UI thread.
• MUST unsubscribe all events + dispose all MarketData in Terminated (leak otherwise).
STILL TO VALIDATE ON FIRST RUN (commented inline as VALIDATE:)
• Exact MarketData class surface / event arg property names in this NT8 build.
• Account.Name availability + which accounts to enumerate at Active.
• That entry price from PositionUpdate.AveragePrice is the right basis vs first exec.
CHANGELOG
sentinel-rebrand (2026-07-01) — MAECaptureService → SentinelLogService; namespace
MAELogging → Sentinel (now part of the Sentinel Suite). Logging routes through
SentinelCore.Log (Output window + Sentinel\sentinel.log). Uses SentinelLogEngine.
The old MAE* files were ARCHIVED out of bin\Custom; the monitor is now the Suite's
"Log" tab (standalone MAEDashboard window removed). No capture-logic change.
v1.0.4 — unified open-position registry (dashboard shows BOTH tiers).
- Subscribes SentinelLogEngine.OnEngineTradeOpened/Closed so tier-2 strategies that log
themselves now appear in GetOpenSnapshots() alongside tier-1 captures. The empty-
dashboard-while-tier2-strategies-run gap is closed. OpenSnapshot gains Tier +
Strategy so the dashboard can distinguish/zero-touch vs rich. Reference-keyed
registry; unsubscribed on Stop.
v1.0.3 — dashboard support surface (no logging-path changes).
- Added read-only live-state API for the dashboard window: GetOpenSnapshots()
returns a snapshot list of currently-open tracked positions (account, instrument,
dir, entry, running MAE/MFE ticks, last price). TradeClosed event fires on each
close so the window can refresh. Track now mirrors running MAE/MFE + last price
for display (engine still owns the authoritative logged values).
v1.0.2 — FILL-PRICE FIX (first live-data findings).
- BUG: every tier-1 trade logged entryPx == exitPx, pnlTicks == 0. Root cause:
PositionUpdate.AveragePrice reports the ENTRY price even on the closing/flat
update (verified NT8 behavior), so using it for the exit was always wrong.
- FIX: source fill prices from EXECUTIONS (e.Execution.Price), which are
authoritative. OnExecutionUpdate now records the last fill per account+instrument;
the position handler uses that for exit (and entry, falling back to position avg
only if no execution seen yet — safe because on the OPEN update the position avg
IS the entry price; the bug is exit-only).
- Clear last-fill on flat so it cannot leak into the next trade on that instrument.
- NOTE: the large entry-to-first-tick gap also seen in early data should shrink now
that entry uses the true fill; VALIDATE on next run that MAE/MFE look sane.
v1.0.1 — compile fix + teardown hardening (first-compile findings).
- MarketData has NO Dispose() (unlike BarsRequest); release is just
`Update -= handler`. Removed the erroneous Dispose() call (CS1061).
- Guard unsubscribe behind an MdSubscribed flag: unsubscribing a MarketData
Update event that was never subscribed throws an NRE in remove_Update
(documented NT8 behavior). Now we only detach if attach succeeded.
v1.0.0 — initial headless tier-1 capture service. Account subscription + raw-tick
excursion + SentinelLogEngine feed. No UI. Logging enabled per-account via the
EnabledAccounts set (default: log all sim accounts; live opt-in).