diff --git a/.kilocode/rules/coding_patterns.md b/.kilocode/rules/coding_patterns.md
index 89f6671..e123b0c 100644
--- a/.kilocode/rules/coding_patterns.md
+++ b/.kilocode/rules/coding_patterns.md
@@ -1,9 +1,66 @@
# Coding Patterns — NT8 SDK Required Patterns
+**Last Updated:** 2026-03-27
All code in the NT8 SDK MUST follow these patterns without exception.
---
+## 0. C# 5.0 Hard Constraints (NinjaScript Compiler)
+
+```csharp
+// ❌ PROHIBITED — compiler will fail silently or error
+$"Hello {name}" // no string interpolation
+obj?.Method() // no null-conditional
+public int Prop => _value; // no expression body
+nameof(SomeClass) // no nameof
+await SomeAsync() // no async/await
+
+// ✅ REQUIRED
+string.Format("Hello {0}", name)
+obj != null ? obj.Method() : null
+public int Prop { get { return _value; } }
+"SomeClass" // string literal
+// synchronous only
+```
+
+---
+
+## 0b. NT8-Specific Critical Rules
+
+```csharp
+// SetStopLoss/SetProfitTarget MUST come BEFORE EnterLong/EnterShort
+// Calling them after is silently ignored in backtest
+SetStopLoss(signalName, CalculationMode.Ticks, stopTicks, false); // FIRST
+SetProfitTarget(signalName, CalculationMode.Ticks, targetTicks); // SECOND
+EnterShort(qty, signalName); // THIRD
+
+// OnBarUpdate must guard secondary series
+protected override void OnBarUpdate()
+{
+ if (BarsInProgress != 0) return; // CRITICAL: ignore daily bar series updates
+ // ...
+}
+
+// State guard in ProcessStrategyIntent
+// Allows Historical (backtest), blocks Realtime replay burst:
+if (State == State.Realtime && !_realtimeBarSeen)
+ return;
+```
+
+---
+
+## Sizing Formula
+
+```
+contracts = floor(RiskPerTrade / (StopTicks × TickValue))
+NQ tick value = $5.00
+$100 / (8 × $5) = 2 contracts
+$200 / (8 × $5) = 5 contracts (capped at MaxContracts)
+Always verify: RiskPerTrade ≤ MaxTradeRisk
+```
+
+---
+
## 1. Thread Safety — Lock Everything Shared
Every class with shared state must have a lock object:
diff --git a/.kilocode/rules/development_workflow.md b/.kilocode/rules/development_workflow.md
index 576169f..9d739a5 100644
--- a/.kilocode/rules/development_workflow.md
+++ b/.kilocode/rules/development_workflow.md
@@ -1,7 +1,108 @@
-# NT8 Institutional SDK - Development Workflow
+# NT8-SDK — Kilocode Development Workflow
+**Last Updated:** 2026-03-27
-## Overview
-This document outlines the development workflow for the NT8 Institutional SDK, following the Archon workflow principles even in the absence of the Archon MCP server.
+This is the authoritative workflow for all development work on NT8-SDK using Kilocode.
+
+---
+
+## Division of Labor
+
+| Role | Responsibility |
+|---|---|
+| **Claude** | Architecture, diagnosis, Kilocode prompt authoring, sequencing |
+| **Kilocode** | ALL code implementation — zero exceptions |
+| **Mo** | Strategy direction, backtest execution, log collection, go/no-go |
+
+---
+
+## Per-Task Workflow
+
+1. **Claude writes Kilocode prompt** — exact Find/Replace, both file paths, build command, validation checklist
+2. **Mo runs Kilocode** — pastes prompt, Kilocode executes
+3. **Kilocode reports** — build output + files changed
+4. **Mo brings results to Claude** — Kilocode report + session log + CSV
+5. **Claude diagnoses** — confirms or issues follow-up prompt
+6. **Mo commits:** `git add` → `git commit` → `git push`
+7. **Update SPRINT_BOARD** — task to Done or Blocked
+
+---
+
+## Kilocode Prompt Template
+
+```
+TASK: [one-line description]
+
+CONTEXT:
+[1-3 sentences explaining why]
+
+FILES TO MODIFY:
+1. C:\dev\nt8-sdk\src\... [repo path]
+2. C:\Users\billy\...\Strategies\... [NT8 path — same change]
+
+CHANGE 1 — [description]:
+File: [path]
+Find:
+[exact existing code]
+Replace with:
+[new code]
+
+BUILD & DEPLOY:
+1. dotnet build NT8-SDK.sln --configuration Release
+2. deployment\deploy-to-nt8.bat
+3. NT8: Tools → Edit NinjaScript → open NT8StrategyBase.cs → save
+
+VALIDATION:
+- Run Strategy Analyzer: NQ JUN26, Jan 1 2026 → Mar 27 2026
+- Look for in session log: [specific confirmation lines]
+```
+
+---
+
+## Guardrails — Kilocode MUST NEVER
+
+1. Modify files outside the task spec
+2. Use C# 6+ syntax
+3. Remove existing comments or XML documentation
+4. Change interface signatures
+5. Deploy without building first
+6. Edit NT8 path without also updating repo path
+7. Guess NT8 API signatures
+8. Introduce async/await
+
+---
+
+## Log Analysis Quick Reference
+
+**Healthy dual-leg trade in session log:**
+```
+SIGNAL Sell | Grade=A | Score=0.820
+SUBMIT Scaler=1 Runner=1 Stop=8 Target=20
+FILL Short 1 @ XXXXX <- scaler fill
+PNL_UPDATE Position=Short Qty=1
+FILL Short 1 @ XXXXX <- runner fill
+PNL_UPDATE Position=Short Qty=2 <- CRITICAL: Qty=2 = runner entered
+```
+
+**Warning signs:**
+- SUBMIT then only 1 FILL → runner blocked (check EntriesPerDirection + MaxOpenPositions)
+- Multiple SIGNALs in milliseconds → replay burst (_realtimeBarSeen not working)
+- SIGNAL then nothing → ProcessStrategyIntent guard blocking backtest
+
+---
+
+## Commit Message Format
+
+```
+feat: description <- new feature
+fix: description <- bug fix
+refactor: description <- no behavior change
+test: description <- tests only
+docs: description <- documentation only
+```
+
+---
+
+## Original Archon Workflow (2025, archived below)
## Archon Workflow Principles
diff --git a/.kilocode/rules/project_context.md b/.kilocode/rules/project_context.md
index e6a7508..d2c1391 100644
--- a/.kilocode/rules/project_context.md
+++ b/.kilocode/rules/project_context.md
@@ -1,7 +1,74 @@
-# Project Context — NT8 SDK (Production Hardening Phase)
+# Project Context — NT8 SDK (Sprint 2: SIM Validation)
+**Last Updated:** 2026-03-27
-You are working on the **NT8 SDK** — an institutional-grade algorithmic trading framework for NinjaTrader 8.
-This is production trading software. Bugs cause real financial losses.
+You are working on the **NT8 SDK** — an institutional-grade algorithmic futures trading system for NinjaTrader 8.
+This is **production trading software**. Bugs cause real financial losses. Never take shortcuts.
+
+---
+
+## Critical Rules for Kilocode
+
+1. **Only modify files listed in the task spec.** Never touch adjacent code.
+2. **C# 5.0 only.** No `$""`, no `?.`, no `=>` bodies, no `nameof()`, no async/await.
+3. **Never remove XML documentation or comments.**
+4. **Never change interface signatures** — IStrategy, IRiskManager, IPositionSizer, INT8ExecutionBridge are frozen.
+5. **Always build before deploying:** `dotnet build NT8-SDK.sln --configuration Release`
+6. **Always deploy to BOTH paths** after every code change:
+ - Repo: `C:\dev\nt8-sdk\src\NT8.Adapters\Strategies\`
+ - NT8: `C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies\`
+7. **Never guess NT8 API signatures.** Verify at `https://developer.ninjatrader.com/docs/desktop`.
+
+---
+
+## Current State (2026-03-27)
+
+**What works end-to-end:**
+- SimpleORBStrategy with 10-factor confluence scoring
+- NT8StrategyBase with session management, kill switch, connection recovery
+- Dual-leg scaler + runner architecture (EntriesPerDirection=2 restored)
+- PortfolioRiskManager singleton (kill switch, daily loss, contract cap)
+- File logging (session log + settings export)
+- Historical replay guard (_realtimeBarSeen)
+- Execution confirmed in SIM on 2026-03-27
+
+**Pending validation:**
+- Runner leg dual-fill (Qty=2) — run backtest to confirm
+- Breakeven + trail in live multi-bar scenario
+
+---
+
+## Architecture
+
+```
+SimpleORBNT8.cs NT8 entry point
+ ↓
+NT8StrategyBase.cs Abstract base: bar routing, kill switch, breakeven, runner trail
+ ↓
+SimpleORBStrategy.cs Signal: ORB detection, 10-factor confluence, _tradeTaken lock
+ ↓
+NT8OrderAdapter.cs INT8ExecutionBridge: EnterLong/EnterShort/SetStopLoss
+ ↓
+PortfolioRiskManager.cs Singleton: cross-strategy risk
+ ↓
+NinjaTrader 8
+```
+
+## Key Files
+
+| File | Path |
+|---|---|
+| NT8StrategyBase.cs | `src\NT8.Adapters\Strategies\` |
+| SimpleORBNT8.cs | `src\NT8.Adapters\Strategies\` |
+| SimpleORBStrategy.cs | `src\NT8.Strategies\Examples\` |
+| NT8OrderAdapter.cs | `src\NT8.Adapters\NinjaTrader\` |
+| PortfolioRiskManager.cs | `src\NT8.Core\Risk\` |
+| deploy-to-nt8.bat | `deployment\` |
+
+## Active Sprint Tasks
+
+See `SPRINT_BOARD.md` (at `docs\architecture\phase1_sprint_plan.md`) for full task list.
+
+Immediate next action: Run Strategy Analyzer backtest (NQ JUN26, Jan 1 2026 → Mar 27 2026) and confirm `PNL_UPDATE Position=Short Qty=2` in session log to validate runner leg.
---
diff --git a/DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md b/DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md
index 50372d1..9ca7f12 100644
--- a/DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md
+++ b/DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md
@@ -1,4 +1,69 @@
-# Designed vs. Implemented Features - Gap Analysis
+# NT8-SDK — Gap Analysis & Roadmap
+**Version:** 3.0 | **Date:** 2026-03-27 | Supersedes all previous gap analysis documents.
+
+---
+
+## Open Gaps
+
+| ID | Description | Priority | Sprint |
+|---|---|---|---|
+| GAP-001 | Runner leg backtest validation (Qty=2 check). EntriesPerDirection=2 restored but not backtested. | CRITICAL | S2-05 |
+| GAP-002 | orbRangeTicks not wired in DailyBarContext (hardcoded 0.0 in SimpleORBNT8.OnBarUpdate) | LOW | Sprint 3 |
+| GAP-003 | Risk parameter consistency: RiskPerTrade can exceed MaxTradeRisk silently. No assertion. | HIGH | S2-06 |
+| GAP-004 | GetRiskStatus() returns hardcoded limit rather than registered strategy config value | LOW | Sprint 3 |
+| GAP-005 | No Gitea CI pipeline. Build and test are manual. | MEDIUM | Sprint 3 |
+| GAP-006 | No n8n webhook alerts for fills, risk events, connection loss | MEDIUM | Sprint 3 |
+| GAP-007 | No walk-forward / out-of-sample validation. All backtests are in-sample. | HIGH | S2-08 |
+| GAP-008 | Short-side profitable only in crash regimes. No regime filter. | MEDIUM | Sprint 3 |
+| GAP-009 | No tick replay backtest. OnBarClose simulation compresses trade duration. | LOW | Sprint 3 |
+
+---
+
+## Sprint Roadmap
+
+### Sprint 2 (ACTIVE) — SIM Validation
+Goal: 2+ weeks unattended SIM with dual-leg execution confirmed.
+Key pending: GAP-001 (runner validation), GAP-003 (risk consistency), GAP-007 (walk-forward).
+
+### Sprint 3 — Production Hardening
+Goal: 30-day SIM clean. CI and alerts wired.
+Key work: GAP-004 through GAP-009, VWAPMeanReversion skeleton.
+
+### Sprint 4 — Live Capital
+Gate: 30-day SIM PF > 2.0, max DD < $500.
+Key work: Go live 1 NQ contract, OvernightGap strategies, ops runbook.
+
+### Sprint 5 — ML Inference
+Prerequisite: 60 days live data.
+Key work: FastAPI /predict, MLSignalFactorCalculator as 11th factor.
+
+---
+
+## Strategy Backlog
+
+| ID | Strategy | Priority | Notes |
+|---|---|---|---|
+| STRAT_079 | Liquidity Sweep Reversal | Medium | Potential short-trade improvement |
+| STRAT_154 | Overnight Gap Continuation | High | Leverages existing SessionManager |
+| STRAT_214 | Overnight Gap Reversion | High | Counter to STRAT_154 |
+| LondonORB | London ORB (3:00 AM ET) | Medium | Separate LondonORBNT8 strategy file |
+| VWAP-MR | VWAP Mean Reversion | High | Sprint 3 build target |
+
+---
+
+## Backtest Performance Reference
+
+| Date | Period | Trades | Win% | PF | Net | Config |
+|---|---|---|---|---|---|---|
+| 2026-03-27 | Jan–Mar 2026 | 20 | 75% | **7.00** | $1,200 | trail=20 ✅ Production config |
+| 2026-03-27 | Jan–Mar 2026 | 40 | 75% | 3.69 | $1,075 | trail=12 |
+| 2026-03-27 | Mar 2025–Mar 2026 | 148 | 51% | 3.15 | $71,303 | 9 cts experimental |
+
+Note: 148-trade run used RiskPerTrade=$500 + EntriesPerDirection=1 (runner blocked). Not a production reference.
+
+---
+
+# ARCHIVED BELOW — Original Gap Analysis (2026-02-17, superseded)
**Date:** February 17, 2026
**Status:** Post Phase A-B-C NT8 Integration
diff --git a/PROJECT_HANDOVER.md b/PROJECT_HANDOVER.md
index bcb13ae..399d428 100644
--- a/PROJECT_HANDOVER.md
+++ b/PROJECT_HANDOVER.md
@@ -1,9 +1,134 @@
-# NT8 SDK Project - Comprehensive Recap & Handover
+# NT8-SDK — Project Context & Current State
+**Version:** 3.0 | **Date:** 2026-03-27 | **Status:** Sprint 2 Active — SIM Validation
-**Document Version:** 2.0
-**Date:** February 16, 2026
-**Current Phase:** Phase 5 Complete
-**Project Completion:** ~85%
+> This file supersedes the previous PROJECT_HANDOVER.md and is the live source of truth.
+> See also: `SPRINT_BOARD.md`, `GAP_ANALYSIS_AND_ROADMAP.md`, `CODING_PATTERNS.md`, `KILOCODE_WORKFLOW.md`
+> Full formatted handover: `NT8_SDK_Handover_Package.docx`
+
+---
+
+## 1. What This Is
+
+NT8-SDK is an institutional-grade algorithmic futures trading system built on NinjaTrader 8. It is not a research prototype — it is production trading software where bugs equal real financial losses.
+
+The system trades NQ (Nasdaq 100 E-mini futures) using a 30-minute Opening Range Breakout strategy (SimpleORB) with a 10-factor confluence scoring engine that grades each signal A+ through F before allowing execution. A scaler/runner dual-leg architecture captures quick targets on the scaler while the runner trails for extended moves.
+
+**Division of labor:** Claude handles architecture, diagnosis, and Kilocode prompt design. Kilocode executes ALL code changes. Mo owns strategy direction and go/no-go decisions.
+
+---
+
+## 2. Technology Stack
+
+| Layer | Technology | Constraint |
+|---|---|---|
+| Language | C# 5.0 | Hard — NinjaScript compiler limit |
+| Framework | .NET Framework 4.8 | Hard — NT8 requirement |
+| Platform | NinjaTrader 8 | Hard — execution environment |
+| Local repo | `C:\dev\nt8-sdk` | Windows path |
+| NT8 deploy | `C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies\` | Must match source |
+| Deploy script | `deployment\deploy-to-nt8.bat` | Creates timestamped backups |
+| VCS | Gitea (self-hosted) | `https://git.thehussains.org/mo/nt8-sdk` |
+| AI coding | Kilocode | Executes ALL code changes |
+| Automation | n8n (self-hosted) | Deferred to Sprint 4 |
+| ML inference | Ollama (local) | Deferred to Sprint 5 |
+
+**Critical C# constraint:** No `$""`, no `?.`, no `=>`, no async/await. Use `string.Format()`, explicit null checks, full method bodies.
+
+---
+
+## 3. Architecture (Top to Bottom)
+
+```
+SimpleORBNT8.cs NT8 entry point — sets defaults, adds daily bar series, builds DailyBarContext
+ ↓
+NT8StrategyBase.cs Abstract base — bar routing, session management, kill switch, breakeven, runner
+ ↓
+SimpleORBStrategy.cs Core signal — ORB detection, 10-factor confluence, _tradeTaken session lock
+ ↓
+NT8OrderAdapter.cs INT8ExecutionBridge — calls EnterLong/EnterShort/SetStopLoss
+ ↓
+PortfolioRiskManager.cs Singleton — cross-strategy daily loss + contract cap
+ ↓
+NinjaTrader 8 Execution, fills, order management
+```
+
+---
+
+## 4. Current Production Parameters (SIM: SimSimple ORB)
+
+| Parameter | Value | Notes |
+|---|---|---|
+| Instrument | NQ JUN26 | Primary instrument |
+| Bar type | 13-Range bars | |
+| BarsRequiredToTrade | 50 | Warm-up guard |
+| DailyLossLimit | $1,000 | |
+| MaxTradeRisk | $200 | |
+| RiskPerTrade | $100 | 2 contracts at current NQ prices |
+| MinContracts | 1 | |
+| MaxContracts | 3 | |
+| MaxOpenPositions | 2 | Scaler + runner |
+| EntriesPerDirection | 2 | Scaler slot 1, runner slot 2 |
+| MinTradeGrade | 5 (A) | 4 (B) for broad SIM testing |
+| EnableShortTrades | False | Long-only until short backtest done |
+| BreakevenTriggerTicks | 20 | Tuned from 12 |
+| RunnerTrailTicks | 20 | Tuned from 12 |
+| OpeningRangeMinutes | 30 | 9:30–10:00 ET |
+| StopTicks | 8 | $40 per contract NQ |
+| TargetTicks | 16 (dynamic) | Scales with ORB/ATR ratio |
+
+---
+
+## 5. Two-Path Deployment Rule
+
+Every code change MUST be applied to both:
+1. `C:\dev\nt8-sdk\src\NT8.Adapters\Strategies\` (repo source)
+2. `C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies\` (NT8 runtime)
+
+After deployment, NT8 must recompile: Tools → Edit NinjaScript → open `NT8StrategyBase.cs` → save.
+
+---
+
+## 6. Key Learnings (Hard-Won)
+
+1. **`State.Historical` guard** — `if (State == State.Realtime && !_realtimeBarSeen) return;` in `ProcessStrategyIntent` allows backtest (Historical), blocks replay burst in live.
+
+2. **`_realtimeBarSeen` flag** — reset to `false` in `State.Realtime`, set `true` on first bar. Skips catch-up bar on live load to prevent replay burst.
+
+3. **`EntriesPerDirection = 2`** — required for scaler + runner. Setting to 1 silently blocks the runner with no error.
+
+4. **`SetStopLoss`/`SetProfitTarget` before `EnterLong`/`EnterShort`** — calling after entry is silently ignored in backtest.
+
+5. **`Calculate.OnBarClose` backtest** — trades appear to close in under 1 second in logs. NT8 simulation artifact, not a bug. Live trades hold 2–20 minutes.
+
+6. **NR7 warm-up** — requires 7 daily bars. Warm-up messages (0/7 bars) are correct behavior.
+
+7. **NT8 never auto-recompiles** — always force recompile after file changes via NinjaScript Editor.
+
+8. **Dual-path deployment mandatory** — stale deployed files cause phantom bugs where code looks right but behaves wrong.
+
+9. **`PortfolioRiskManager` is a singleton** — fully implemented, no changes required. Kill switch, daily loss, contract cap all working.
+
+10. **Sizing formula** — `floor(RiskPerTrade / (StopTicks × $5.00))`. NQ: `$100 / (8 × $5) = 2 contracts`. Always verify `RiskPerTrade <= MaxTradeRisk`.
+
+---
+
+## 7. Validated Backtest Results
+
+| Date | Period | Trades | Win% | PF | Net | Config |
+|---|---|---|---|---|---|---|
+| 2026-03-27 | Jan–Mar 2026 | 20 | 75% | **7.00** | $1,200 | 1 ct, trail=20 ✅ Best |
+| 2026-03-27 | Jan–Mar 2026 | 40 | 75% | 3.69 | $1,075 | 1 ct, trail=12 |
+| 2026-03-27 | Mar 2025–Mar 2026 | 148 | 51% | 3.15 | $71,303 | 9 cts (experimental) |
+
+The 9-contract run used `RiskPerTrade=$500` — not a production configuration. Runner leg was also blocked (`EntriesPerDirection=1`) for that run. Re-run required after Sprint 2 fixes.
+
+---
+
+## 8. Immediate Next Actions Before Market Open
+
+1. Run Strategy Analyzer (NQ JUN26, Jan 1 2026 → Mar 27 2026) and confirm `PNL_UPDATE Position=Short Qty=2` in session log — validates runner leg
+2. Verify SIM account settings: `RiskPerTrade=$100`, `BreakevenTriggerTicks=20`, `RunnerTrailTicks=20`
+3. Only re-enable BX68915-15 after runner validation passes — long-only, `MaxContracts=2`, `DailyLossLimit=$500`
---
diff --git a/README.md b/README.md
index 44a180b..86e9f96 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,16 @@
+# NT8-SDK — Institutional Algorithmic Futures Trading System
+
+**See `NT8_SDK_Handover_Package.docx` for the complete milestone handover document.**
+
+Quick reference docs in repo root:
+- `PROJECT_CONTEXT.md` — current state, parameters, key learnings
+- `SPRINT_BOARD.md` — active sprint tasks
+- `GAP_ANALYSIS_AND_ROADMAP.md` — open gaps and sprint plan
+- `CODING_PATTERNS.md` — C# 5.0 rules, NT8 quirks
+- `KILOCODE_WORKFLOW.md` — Kilocode prompt template and guardrails
+
+---
+
# NT8 Institutional Trading SDK
**Version:** 0.2.0
diff --git a/deployment/Deploy-To-NT8.ps1 b/deployment/Deploy-To-NT8.ps1
index c13c102..73a5660 100644
--- a/deployment/Deploy-To-NT8.ps1
+++ b/deployment/Deploy-To-NT8.ps1
@@ -173,7 +173,7 @@ else {
}
if (-not $SkipVerification) {
- Write-Step "6/6" "Verifying deployment"
+ Write-Step "6/7" "Verifying deployment"
$ok = $true
if (-not (Test-Path "$nt8Custom\NT8.Core.dll")) {
@@ -195,13 +195,43 @@ if (-not $SkipVerification) {
Write-Success "Deployment verification passed"
}
else {
- Write-Step "6/6" "Skipping verification"
+ Write-Step "6/7" "Skipping verification"
+}
+
+# Step 7: Delete the compiled NinjaScript assembly so NT8 is forced to do a
+# full recompile from source on next startup. Without this, NT8 may load a
+# stale cached assembly and silently ignore updated .cs files.
+Write-Step "7/7" "Invalidating NinjaScript compiled assembly"
+$compiledDll = Join-Path $nt8Custom "NinjaTrader.Custom.dll"
+$compiledPdb = Join-Path $nt8Custom "NinjaTrader.Custom.pdb"
+$compiledXml = Join-Path $nt8Custom "NinjaTrader.Custom.xml"
+
+$invalidated = 0
+foreach ($artifact in @($compiledDll, $compiledPdb)) {
+ if (Test-Path $artifact) {
+ Remove-Item $artifact -Force
+ Write-Success ("Deleted {0}" -f (Split-Path $artifact -Leaf))
+ $invalidated++
+ }
+}
+
+if ($invalidated -gt 0) {
+ Write-Host " NT8 will perform a full recompile on next startup." -ForegroundColor Green
+ Write-Host " The .xml file is documentation only and was left in place." -ForegroundColor Gray
+} else {
+ Write-Warn "No compiled assembly found to delete (NT8 may not have been run yet)"
}
$duration = (Get-Date) - $startTime
Write-Header "Deployment Complete"
Write-Host ("Duration: {0:F1} seconds" -f $duration.TotalSeconds)
-Write-Host "Next: Open NinjaTrader 8 -> NinjaScript Editor -> Compile All"
+Write-Host ""
+Write-Host "NEXT STEPS:" -ForegroundColor Cyan
+Write-Host " 1. Start NinjaTrader 8 (full recompile will happen automatically)" -ForegroundColor White
+Write-Host " 2. Wait for compilation to complete in the Output window" -ForegroundColor White
+Write-Host " 3. Remove and re-add the strategy to the chart" -ForegroundColor White
+Write-Host " 4. Verify defaults: BreakevenTriggerTicks=20 RunnerTrailTicks=20 MaxContracts=3" -ForegroundColor White
+Write-Host " 5. Confirm NT8 Output shows: StartBehavior=AdoptAccountPosition EntriesPerDirection=2" -ForegroundColor White
exit 0
diff --git a/deployment/deploy-to-nt8.bat b/deployment/deploy-to-nt8.bat
index e9880ce..2e992a9 100644
--- a/deployment/deploy-to-nt8.bat
+++ b/deployment/deploy-to-nt8.bat
@@ -147,10 +147,32 @@ echo.
echo Deployment complete.
echo Backup location: %BACKUP_DIR%
echo Manifest file : %MANIFEST_FILE%
+
echo.
-echo Next steps:
-echo 1. Open NinjaTrader 8.
-echo 2. Open NinjaScript Editor and press F5 (Compile).
-echo 3. Verify strategies appear in the Strategies list.
+echo Invalidating NinjaScript compiled assembly...
+set "COMPILED_DLL=%NT8_CUSTOM%\NinjaTrader.Custom.dll"
+set "COMPILED_PDB=%NT8_CUSTOM%\NinjaTrader.Custom.pdb"
+
+if exist "%COMPILED_DLL%" (
+ del /F /Q "%COMPILED_DLL%"
+ echo [OK] Deleted NinjaTrader.Custom.dll
+) else (
+ echo [WARN] NinjaTrader.Custom.dll not found - NT8 may not have been run yet
+)
+
+if exist "%COMPILED_PDB%" (
+ del /F /Q "%COMPILED_PDB%"
+ echo [OK] Deleted NinjaTrader.Custom.pdb
+)
+
+echo.
+echo ============================================================
+echo NEXT STEPS:
+echo 1. Start NinjaTrader 8 (full recompile happens automatically)
+echo 2. Wait for compilation to finish in the Output window
+echo 3. Remove and re-add the strategy to the chart
+echo 4. Verify defaults: BreakevenTriggerTicks=20 RunnerTrailTicks=20 MaxContracts=3
+echo 5. NT8 Output must show: StartBehavior=AdoptAccountPosition EntriesPerDirection=2
+echo ============================================================
exit /b 0
diff --git a/docs/architecture/phase1_sprint_plan.md b/docs/architecture/phase1_sprint_plan.md
index f442599..b61961f 100644
--- a/docs/architecture/phase1_sprint_plan.md
+++ b/docs/architecture/phase1_sprint_plan.md
@@ -1,7 +1,99 @@
-# NT8 Institutional SDK - Phase 1 Sprint Plan
+# NT8-SDK — Sprint Board
+**Last Updated:** 2026-03-27 | **Active Sprint:** Sprint 2
-## Overview
-This document outlines the sprint plan for Phase 1 development of the NT8 Institutional SDK. Phase 1 builds upon the completed Phase 0 foundation to deliver a more complete trading system with Order Management System (OMS), NinjaTrader 8 integration, enhanced risk controls, and market data handling.
+---
+
+## Sprint 2 — SIM Validation (ACTIVE)
+**Goal:** Strategy runs unattended 2+ weeks in SIM with correct dual-leg execution.
+**Gate:** Zero unhandled exceptions, both scaler and runner filling (Qty=2 in log), daily loss limit never accidentally triggered.
+
+| # | Task | Status | Notes |
+|---|---|---|---|
+| S2-01 | Restore `EntriesPerDirection=2` | ✅ Done | Deployed 2026-03-27 |
+| S2-02 | `MaxOpenPositions=2` in SimpleORBNT8 | ✅ Done | Deployed 2026-03-27 |
+| S2-03 | `_realtimeBarSeen` replay guard | ✅ Done | Blocks live replay burst, allows backtest |
+| S2-04 | `State.Historical` guard in ProcessStrategyIntent | ✅ Done | Restores backtest execution |
+| S2-05 | Validate runner leg — Qty=2 in session log | ⬜ Pending | Run backtest after S2-01 |
+| S2-06 | Risk parameter consistency validation | ⬜ Pending | Assert RiskPerTrade ≤ MaxTradeRisk |
+| S2-07 | SIM unattended run 2+ weeks | ▶ In progress | Started 2026-03-27 |
+| S2-08 | Walk-forward backtest split | ⬜ Pending | Mar–Sep 2025 train / Oct–Mar 2026 test |
+| S2-09 | BX68915-15 prop firm re-enable | 🔴 Blocked | Gate: S2-05 + S2-07 pass |
+
+---
+
+## Sprint 3 — Production Hardening
+**Goal:** 30-day SIM run clean. CI and alerts live.
+
+| # | Task | Status | Notes |
+|---|---|---|---|
+| S3-01 | Gitea CI — build + test on push | ⬜ Pending | `.gitea/workflows/build.yml` |
+| S3-02 | n8n webhook — fills + risk events | ⬜ Pending | HTTP POST from NT8StrategyBase |
+| S3-03 | Fix `GetRiskStatus()` hardcoded limit | ⬜ Pending | Read from registered strategy config |
+| S3-04 | `orbRangeTicks` wiring in DailyBarContext | ⬜ Pending | Low priority |
+| S3-05 | Short-side regime filter | ⬜ Pending | 20-day MA or VIX-based gate |
+| S3-06 | Tick replay validation | ⬜ Pending | 30-day window |
+| S3-07 | VWAPMeanReversion strategy skeleton | ⬜ Pending | New IStrategy implementation |
+
+---
+
+## Sprint 4 — Live Capital
+**Gate:** 30-day SIM pass rate > 70%, PF > 2.0, max drawdown < $500
+
+| # | Task | Status |
+|---|---|---|
+| S4-01 | Go live 1 NQ contract | ⬜ Pending gate |
+| S4-02 | OvernightGapContinuation strategy | ⬜ Pending |
+| S4-03 | OvernightGapReversion strategy | ⬜ Pending |
+| S4-04 | Ops runbook and emergency procedures | ⬜ Pending |
+| S4-05 | Connection loss recovery testing | ⬜ Pending |
+| S4-06 | CME holiday filter | ⬜ Pending |
+
+---
+
+## Sprint 5 — ML Inference
+**Prerequisite:** 60 days of live trading data.
+
+| # | Task |
+|---|---|
+| S5-01 | FastAPI /predict endpoint on Ollama workstation |
+| S5-02 | HTTP client in SimpleORBStrategy |
+| S5-03 | MLSignalFactorCalculator as IFactorCalculator |
+| S5-04 | Feature engineering from live trade history |
+| S5-05 | A/B test: with vs without ML factor |
+
+---
+
+## Completed (Historical)
+
+| Task | Sprint | Completed |
+|---|---|---|
+| Execute trades in NT8 SIM (execution bridge wired) | Sprint 1 | 2026-03-27 |
+| Historical replay burst fix | Sprint 1 | 2026-03-27 |
+| NR7 warm-up guard | Phase 4 | 2026-02 |
+| 10-factor confluence scoring engine | Phase 4 | 2026-02 |
+| PortfolioRiskManager singleton | Phase 4 | 2026-02 |
+| Analytics layer (240+ tests) | Phase 5 | 2026-02-16 |
+| Breakeven + runner trail logic | Sprint 1 | 2026-03 |
+| Connection loss detection | Sprint 1 | 2026-03 |
+| File logging + settings export | Sprint 1 | 2026-03 |
+
+---
+
+## Backtest Results History
+
+| Date | Period | Trades | Win% | PF | Net | Config |
+|---|---|---|---|---|---|---|
+| 2026-03-27 | Jan–Mar 2026 | 20 | 75% | 7.00 | $1,200 | trail=20, grade=B ✅ Use this |
+| 2026-03-27 | Jan–Mar 2026 | 40 | 75% | 3.69 | $1,075 | trail=12, grade=B |
+| 2026-03-27 | Mar 2025–Mar 2026 | 148 | 51% | 3.15 | $71,303 | 9 cts experimental |
+
+**Note:** The 148-trade run used `RiskPerTrade=$500` producing 9 contracts AND had `EntriesPerDirection=1` blocking the runner. Not a production reference. Re-run required post Sprint 2.
+
+---
+# ARCHIVED BELOW — Phase 1 Sprint Plan (2025-09-15, superseded)
+
+## Overview (ARCHIVED)
+This document originally outlined Phase 1 sprint planning from September 2025.
## Sprint Goals
1. Implement Order Management System (OMS) with smart order routing
diff --git a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs
index e45f132..3cb1809 100644
--- a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs
+++ b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs
@@ -182,6 +182,16 @@ namespace NinjaTrader.NinjaScript.Strategies
ExitShort("EmergencyFlatten");
}
+ ///
+ /// Returns true if the concrete strategy has ForceSessionReset enabled.
+ /// Override in subclass to expose the NinjaScript parameter value.
+ /// Default returns false so base class never forces a reset unless overridden.
+ ///
+ protected virtual bool GetForceSessionReset()
+ {
+ return false;
+ }
+
///
/// Create the SDK strategy instance.
///
@@ -207,7 +217,7 @@ namespace NinjaTrader.NinjaScript.Strategies
MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
OrderFillResolution = OrderFillResolution.Standard;
Slippage = 0;
- StartBehavior = StartBehavior.WaitUntilFlat;
+ StartBehavior = StartBehavior.AdoptAccountPosition;
TimeInForce = TimeInForce.Gtc;
TraceOrders = false;
RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
@@ -220,7 +230,7 @@ namespace NinjaTrader.NinjaScript.Strategies
MaxOpenPositions = 3;
RiskPerTrade = 100.0;
MinContracts = 1;
- MaxContracts = 10;
+ MaxContracts = 3;
EnableKillSwitch = false;
EnableVerboseLogging = false;
MinTradeGrade = 4;
@@ -229,10 +239,10 @@ namespace NinjaTrader.NinjaScript.Strategies
EnableLongTrades = true;
EnableShortTrades = true;
EnableAutoBreakeven = true;
- BreakevenTriggerTicks = 12;
+ BreakevenTriggerTicks = 20;
BreakevenOffsetTicks = 1;
EnableRunner = true;
- RunnerTrailTicks = 12;
+ RunnerTrailTicks = 20;
_killSwitchTriggered = false;
_connectionLost = false;
}
@@ -242,6 +252,14 @@ namespace NinjaTrader.NinjaScript.Strategies
{
try
{
+ // DIAGNOSTIC: Print actual runtime property values to confirm
+ // what NT8 loaded vs what SetDefaults specified.
+ Print(string.Format("[SDK-DIAG] SetDefaults check: BE={0} Trail={1} MaxC={2} SB={3} EPD={4}",
+ BreakevenTriggerTicks,
+ RunnerTrailTicks,
+ MaxContracts,
+ StartBehavior,
+ EntriesPerDirection));
InitFileLog();
InitializeSdkComponents();
_sdkInitialized = true;
@@ -260,6 +278,18 @@ namespace NinjaTrader.NinjaScript.Strategies
else if (State == State.Realtime)
{
_realtimeBarSeen = false;
+
+ // If ForceSessionReset is enabled, push a reset signal into the SDK strategy
+ // so _tradeTaken is cleared before any live bar is processed.
+ // This recovers from replay-burst scenarios where historical bars set _tradeTaken.
+ if (_sdkStrategy != null && GetForceSessionReset())
+ {
+ var resetParams = new Dictionary();
+ resetParams.Add("force_session_reset", true);
+ _sdkStrategy.SetParameters(resetParams);
+ Print(string.Format("[SDK] ForceSessionReset: _tradeTaken cleared on live start at {0}", DateTime.Now.ToString("HH:mm:ss")));
+ }
+
WriteSettingsFile();
}
else if (State == State.Terminated)
@@ -748,9 +778,24 @@ namespace NinjaTrader.NinjaScript.Strategies
private BarData ConvertCurrentBar()
{
+ DateTime barTimeEt;
+ try
+ {
+ var centralZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
+ var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
+ DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(
+ DateTime.SpecifyKind(Time[0], DateTimeKind.Unspecified),
+ centralZone);
+ barTimeEt = TimeZoneInfo.ConvertTimeFromUtc(utcTime, easternZone);
+ }
+ catch
+ {
+ barTimeEt = Time[0];
+ }
+
return NT8DataConverter.ConvertBar(
Instrument.MasterInstrument.Name,
- Time[0],
+ barTimeEt,
Open[0],
High[0],
Low[0],
diff --git a/src/NT8.Adapters/Strategies/SimpleORBNT8.cs b/src/NT8.Adapters/Strategies/SimpleORBNT8.cs
index 0326961..2a9c4c7 100644
--- a/src/NT8.Adapters/Strategies/SimpleORBNT8.cs
+++ b/src/NT8.Adapters/Strategies/SimpleORBNT8.cs
@@ -40,6 +40,10 @@ namespace NinjaTrader.NinjaScript.Strategies
[Range(1, 50)]
public int StopTicks { get; set; }
+ [NinjaScriptProperty]
+ [Display(Name = "Force Session Reset On Start", GroupName = "ORB Strategy", Order = 10)]
+ public bool ForceSessionReset { get; set; }
+
[NinjaScriptProperty]
[Display(Name = "Profit Target Ticks", GroupName = "ORB Risk", Order = 2)]
[Range(1, 100)]
@@ -73,10 +77,12 @@ namespace NinjaTrader.NinjaScript.Strategies
// Long-only: short trades permanently disabled pending backtest confirmation
EnableShortTrades = false;
EnableAutoBreakeven = true;
- BreakevenTriggerTicks = 12;
+ BreakevenTriggerTicks = 20;
BreakevenOffsetTicks = 1;
EnableRunner = true;
- RunnerTrailTicks = 12;
+ RunnerTrailTicks = 20;
+ ForceSessionReset = false;
+ StartBehavior = StartBehavior.AdoptAccountPosition;
}
else if (State == State.Configure)
{
@@ -105,6 +111,11 @@ namespace NinjaTrader.NinjaScript.Strategies
}
}
+ protected override bool GetForceSessionReset()
+ {
+ return ForceSessionReset;
+ }
+
protected override IStrategy CreateSdkStrategy()
{
return new SdkSimpleORB(OpeningRangeMinutes, StdDevMultiplier);
diff --git a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs
index 5d1469d..b06279b 100644
--- a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs
+++ b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs
@@ -203,7 +203,15 @@ namespace NT8.Strategies.Examples
}
if (_tradeTaken)
+ {
+ if (_logger != null)
+ _logger.LogDebug(
+ "SimpleORBStrategy skip: trade already taken for session {0:yyyy-MM-dd}; bar={1:yyyy-MM-dd HH:mm}; symbol={2}",
+ _currentSessionDate,
+ bar.Time,
+ context.Symbol);
return null;
+ }
var openingRange = _openingRangeHigh - _openingRangeLow;
var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0);
@@ -286,6 +294,15 @@ namespace NT8.Strategies.Examples
_tradeTaken = true;
+ if (_logger != null)
+ _logger.LogDebug(
+ "SimpleORBStrategy flag set: tradeTaken={0} session={1:yyyy-MM-dd}; bar={2:yyyy-MM-dd HH:mm}; side={3}; symbol={4}",
+ _tradeTaken,
+ _currentSessionDate,
+ bar.Time,
+ candidate.Side,
+ candidate.Symbol);
+
if (_logger != null && score.Factors != null)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
@@ -347,7 +364,27 @@ namespace NT8.Strategies.Examples
/// Parameter map.
public void SetParameters(Dictionary parameters)
{
- // Constructor-bound parameters intentionally remain immutable for deterministic behavior.
+ if (parameters == null)
+ return;
+
+ // force_session_reset: clear _tradeTaken and ORB state so a fresh live session
+ // can trade even if historical replay set _tradeTaken before going realtime.
+ if (parameters.ContainsKey("force_session_reset"))
+ {
+ var val = parameters["force_session_reset"];
+ if (val is bool && (bool)val)
+ {
+ lock (_lock)
+ {
+ _tradeTaken = false;
+ _openingRangeReady = false;
+ _openingRangeHigh = Double.MinValue;
+ _openingRangeLow = Double.MaxValue;
+ if (_logger != null)
+ _logger.LogInformation("ForceSessionReset: _tradeTaken cleared, ORB state reset for live session");
+ }
+ }
+ }
}
private void EnsureInitialized()