Compare commits
5 Commits
v0.5.0
...
cleanup/do
| Author | SHA1 | Date | |
|---|---|---|---|
| ce74f68e54 | |||
| 3ccd3a8bfd | |||
| e2ea45b58f | |||
| 9a28a49292 | |||
| d856f3949d |
BIN
.gitattributes
vendored
Normal file
BIN
.gitattributes
vendored
Normal file
Binary file not shown.
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,7 +1,87 @@
|
||||
# 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.
|
||||
|
||||
---
|
||||
|
||||
## Onboarding / First Read (Canonical Order)
|
||||
|
||||
For every new Kilo session, start with `docs/00-governance/` as the primary source of truth:
|
||||
1. `docs/00-governance/executive_summary.md`
|
||||
2. `docs/00-governance/current_status.md`
|
||||
3. `docs/00-governance/active_work.md`
|
||||
4. `docs/00-governance/architecture.md`
|
||||
5. `docs/00-governance/roadmap.md`
|
||||
|
||||
`PROJECT_HANDOVER.md` and `DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md` are historical/contextual references only. Use them for background, not for authoritative current direction.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
---
|
||||
|
||||
|
||||
13
README.md
13
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
13
docs/00-governance/CLEANUP_LOG.md
Normal file
13
docs/00-governance/CLEANUP_LOG.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Governance Cleanup Log
|
||||
|
||||
## 2026-04-05
|
||||
- Established `docs/00-governance/` as canonical governance entry point.
|
||||
- Added concise governance baseline documents:
|
||||
- `executive_summary.md`
|
||||
- `architecture.md`
|
||||
- `current_status.md`
|
||||
- `roadmap.md`
|
||||
- `active_work.md`
|
||||
- Updated onboarding guidance in `.kilocode/rules/project_context.md` to point new sessions to governance docs first.
|
||||
- Reclassified `PROJECT_HANDOVER.md` and `DESIGNED_VS_IMPLEMENTED_GAP_ANALYSIS.md` as historical/contextual references, not primary truth.
|
||||
- No code files modified and no file moves performed.
|
||||
17
docs/00-governance/active_work.md
Normal file
17
docs/00-governance/active_work.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Active Work
|
||||
|
||||
## Current Execution Focus
|
||||
- Validate runner-leg dual-fill behavior in Strategy Analyzer/session logs.
|
||||
- Close remaining Sprint 2 hardening items already identified in task specs.
|
||||
- Preserve strict compile and deployment verification sequence for every change.
|
||||
|
||||
## In-Scope Hardening Themes
|
||||
- Strategy safety controls and execution circuit-breaker wiring.
|
||||
- Trailing stop calculation correctness.
|
||||
- Logging verbosity controls for production noise reduction.
|
||||
- Session/holiday awareness to avoid invalid trading windows.
|
||||
|
||||
## Working Rules
|
||||
- Apply changes only to explicitly scoped files per task.
|
||||
- Keep NT8 API signatures and managed-order sequencing exact.
|
||||
- Maintain C# 5.0 compatibility and existing interface boundaries.
|
||||
28
docs/00-governance/architecture.md
Normal file
28
docs/00-governance/architecture.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Architecture Governance
|
||||
|
||||
## Runtime Flow (Authoritative)
|
||||
```
|
||||
SimpleORBNT8.cs
|
||||
-> NT8StrategyBase.cs
|
||||
-> SimpleORBStrategy.cs
|
||||
-> NT8OrderAdapter.cs
|
||||
-> PortfolioRiskManager.cs
|
||||
-> NinjaTrader 8
|
||||
```
|
||||
|
||||
## Responsibilities
|
||||
- `SimpleORBNT8.cs`: NT8 entry point and platform lifecycle bridge.
|
||||
- `NT8StrategyBase.cs`: orchestration, risk gate sequencing, execution handoff, platform callbacks.
|
||||
- `SimpleORBStrategy.cs`: signal generation and confluence grading only.
|
||||
- `NT8OrderAdapter.cs`: execution bridge to NT8 managed order APIs.
|
||||
- `PortfolioRiskManager.cs`: cross-strategy risk controls and account-level enforcement.
|
||||
|
||||
## Architectural Constraints
|
||||
- Risk-first flow is mandatory; no strategy-level bypass of risk validation.
|
||||
- Managed-order sequence remains required (`SetStopLoss` / `SetProfitTarget` before entry).
|
||||
- C# 5.0 syntax only, .NET Framework 4.8 only.
|
||||
- NT8 signatures must be verified against official NinjaTrader docs before API-touching edits.
|
||||
|
||||
## Governance Notes
|
||||
- Core Risk/Sizing/OMS/Intelligence/Analytics layers are treated as complete and stable unless explicitly re-opened by approved work.
|
||||
- Hardening changes are concentrated in targeted adapter/utility components per active-work scope.
|
||||
0
docs/00-governance/common_failures.md
Normal file
0
docs/00-governance/common_failures.md
Normal file
0
docs/00-governance/compile_guardrails.md
Normal file
0
docs/00-governance/compile_guardrails.md
Normal file
20
docs/00-governance/current_status.md
Normal file
20
docs/00-governance/current_status.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Current Status
|
||||
|
||||
## Snapshot (2026-04-05)
|
||||
- Program phase: Sprint 2 SIM validation with production-hardening follow-through.
|
||||
- Core implementation: complete across major layers with 240+ passing tests.
|
||||
- Live focus: execution safety, validation depth, and operational reliability.
|
||||
|
||||
## Confirmed Working Areas
|
||||
- End-to-end strategy pipeline from signal -> risk -> sizing -> NT8 managed execution.
|
||||
- Session handling, portfolio risk controls, and base strategy orchestration.
|
||||
- Existing analytics/risk/sizing/OMS foundations remain stable.
|
||||
|
||||
## Open Findings Driving Work
|
||||
- Runner leg behavior requires explicit validation evidence (`Qty=2` path confirmation).
|
||||
- Risk/config consistency checks need tightening in runtime safeguards.
|
||||
- Operational controls (CI automation, alerting, and out-of-sample validation) remain pending.
|
||||
|
||||
## Operational Reality
|
||||
- `dotnet build` success is necessary but not sufficient; NT8 NinjaScript compile remains a separate required validation step.
|
||||
- Deployment integrity requires keeping repo and NT8 runtime strategy copies synchronized.
|
||||
0
docs/00-governance/decisions.md
Normal file
0
docs/00-governance/decisions.md
Normal file
20
docs/00-governance/executive_summary.md
Normal file
20
docs/00-governance/executive_summary.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Executive Summary
|
||||
|
||||
## Scope
|
||||
- This folder (`docs/00-governance/`) is the canonical governance source for project state, priorities, architecture intent, and active execution guidance.
|
||||
- Governance is aligned to Sprint 2 (SIM validation) with production hardening gaps tracked and prioritized.
|
||||
|
||||
## Current Position (2026-04-05)
|
||||
- Core engine is implemented with 240+ passing tests across core components.
|
||||
- NT8 execution path is wired and validated in SIM for baseline operation.
|
||||
- Remaining work is focused on closing operational hardening gaps and validating runner behavior under production-like conditions.
|
||||
|
||||
## Approved Direction
|
||||
- Keep execution in managed-order NT8 patterns and C# 5.0 / .NET Framework 4.8 constraints.
|
||||
- Complete critical and high-priority hardening tasks before expanding strategy scope.
|
||||
- Treat historical handover artifacts as context only; governance decisions flow from this folder.
|
||||
|
||||
## Immediate Priorities
|
||||
- Confirm runner-leg dual-fill behavior in analyzer/session logs.
|
||||
- Close safety and observability items already identified in Sprint 2/3 gap tracking.
|
||||
- Maintain strict file-boundary and compile-guardrail discipline for all changes.
|
||||
0
docs/00-governance/patterns_and_antipatterns.md
Normal file
0
docs/00-governance/patterns_and_antipatterns.md
Normal file
21
docs/00-governance/roadmap.md
Normal file
21
docs/00-governance/roadmap.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Roadmap
|
||||
|
||||
## Sprint 2 (Active): SIM Validation
|
||||
- Validate dual-leg execution behavior with log evidence.
|
||||
- Complete remaining safety/consistency fixes tied to active gap list.
|
||||
- Exit criteria: stable SIM behavior with no unresolved critical gaps.
|
||||
|
||||
## Sprint 3: Production Hardening
|
||||
- Implement CI build/test automation and operational alerting.
|
||||
- Complete lower-priority runtime consistency and observability improvements.
|
||||
- Add walk-forward and broader validation coverage beyond in-sample checks.
|
||||
|
||||
## Sprint 4: Live Capital Readiness
|
||||
- Gate on sustained SIM metrics and drawdown controls.
|
||||
- Introduce go-live runbook and operational controls for controlled capital exposure.
|
||||
|
||||
## Sprint 5: ML Extension (Deferred)
|
||||
- Add inference integration only after sufficient live/sim data and stable production operations.
|
||||
|
||||
## Sequencing Rule
|
||||
- No feature expansion ahead of unresolved safety and validation gates.
|
||||
@@ -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
|
||||
|
||||
@@ -54,6 +54,11 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
private DateTime _lastBarTime;
|
||||
private bool _killSwitchTriggered;
|
||||
private bool _connectionLost;
|
||||
private bool _realtimeBarSeen;
|
||||
private bool _breakevenMoved;
|
||||
private string _scalerSignalName;
|
||||
private string _runnerSignalName;
|
||||
private bool _runnerActive;
|
||||
private ExecutionCircuitBreaker _circuitBreaker;
|
||||
private System.IO.StreamWriter _fileLog;
|
||||
private readonly object _fileLock = new object();
|
||||
@@ -117,6 +122,29 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
[Display(Name = "Enable Short Trades", GroupName = "Trade Direction", Order = 2)]
|
||||
public bool EnableShortTrades { get; set; }
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Enable Auto Breakeven", GroupName = "Exit Management", Order = 1)]
|
||||
public bool EnableAutoBreakeven { get; set; }
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Breakeven Trigger Ticks", GroupName = "Exit Management", Order = 2)]
|
||||
[Range(1, 100)]
|
||||
public int BreakevenTriggerTicks { get; set; }
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Breakeven Offset Ticks", GroupName = "Exit Management", Order = 3)]
|
||||
[Range(0, 20)]
|
||||
public int BreakevenOffsetTicks { get; set; }
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Enable Runner", GroupName = "Exit Management", Order = 4)]
|
||||
public bool EnableRunner { get; set; }
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Runner Trail Ticks", GroupName = "Exit Management", Order = 5)]
|
||||
[Range(4, 100)]
|
||||
public int RunnerTrailTicks { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
// INT8ExecutionBridge implementation
|
||||
@@ -154,6 +182,16 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
ExitShort("EmergencyFlatten");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
protected virtual bool GetForceSessionReset()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the SDK strategy instance.
|
||||
/// </summary>
|
||||
@@ -171,7 +209,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
Description = "SDK-integrated strategy base";
|
||||
// Name intentionally not set - this is an abstract base class
|
||||
Calculate = Calculate.OnBarClose;
|
||||
EntriesPerDirection = 1;
|
||||
EntriesPerDirection = 2;
|
||||
EntryHandling = EntryHandling.AllEntries;
|
||||
IsExitOnSessionCloseStrategy = true;
|
||||
ExitOnSessionCloseSeconds = 30;
|
||||
@@ -179,12 +217,12 @@ 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;
|
||||
StopTargetHandling = StopTargetHandling.PerEntryExecution;
|
||||
BarsRequiredToTrade = 50;
|
||||
BarsRequiredToTrade = 1;
|
||||
|
||||
EnableSDK = true;
|
||||
DailyLossLimit = 1000.0;
|
||||
@@ -192,7 +230,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
MaxOpenPositions = 3;
|
||||
RiskPerTrade = 100.0;
|
||||
MinContracts = 1;
|
||||
MaxContracts = 10;
|
||||
MaxContracts = 3;
|
||||
EnableKillSwitch = false;
|
||||
EnableVerboseLogging = false;
|
||||
MinTradeGrade = 4;
|
||||
@@ -200,6 +238,11 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
LogDirectory = string.Empty;
|
||||
EnableLongTrades = true;
|
||||
EnableShortTrades = true;
|
||||
EnableAutoBreakeven = true;
|
||||
BreakevenTriggerTicks = 20;
|
||||
BreakevenOffsetTicks = 1;
|
||||
EnableRunner = true;
|
||||
RunnerTrailTicks = 20;
|
||||
_killSwitchTriggered = false;
|
||||
_connectionLost = false;
|
||||
}
|
||||
@@ -209,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;
|
||||
@@ -226,6 +277,19 @@ 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<string, object>();
|
||||
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)
|
||||
@@ -243,6 +307,22 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
if (BarsInProgress != 0)
|
||||
return;
|
||||
|
||||
// Require 7 completed daily bars before allowing any signal.
|
||||
// NarrowRangeFactorCalculator needs 7 daily bars for NR7 scoring.
|
||||
// Without this guard, NR7 silently returns its floor score (0.3)
|
||||
// which may suppress trades via the MinTradeGrade confluence gate.
|
||||
if (BarsArray != null && BarsArray.Length > 1
|
||||
&& CurrentBars != null && CurrentBars.Length > 1
|
||||
&& CurrentBars[1] < 7)
|
||||
{
|
||||
// Always print warm-up status — visible in Strategy Analyzer output
|
||||
// to confirm how many daily bars are available on a given backtest range.
|
||||
if (CurrentBar % 20 == 0)
|
||||
Print(String.Format("[SDK] Daily warm-up: {0}/7 bars — waiting for NR7 history. Extend backtest start date or add pre-load days.",
|
||||
CurrentBars[1] + 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_sdkInitialized || _sdkStrategy == null)
|
||||
{
|
||||
return;
|
||||
@@ -256,8 +336,25 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
if (Time[0] == _lastBarTime)
|
||||
return;
|
||||
|
||||
if (Time[0].Date != _lastBarTime.Date && _lastBarTime != DateTime.MinValue)
|
||||
{
|
||||
_runnerActive = false;
|
||||
_breakevenMoved = false;
|
||||
_scalerSignalName = null;
|
||||
_runnerSignalName = null;
|
||||
}
|
||||
|
||||
_lastBarTime = Time[0];
|
||||
|
||||
// Mark first bar seen after going realtime. Until this fires, we're
|
||||
// processing catch-up replay bars and must not submit orders.
|
||||
if (State == State.Realtime && !_realtimeBarSeen)
|
||||
{
|
||||
_realtimeBarSeen = true;
|
||||
Print(string.Format("[SDK] First realtime bar seen: {0}", Time[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
// Sync actual open position to portfolio manager on every bar
|
||||
PortfolioRiskManager.Instance.UpdateOpenContracts(Name, Math.Abs(Position.Quantity));
|
||||
|
||||
@@ -329,6 +426,50 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
Close[0]));
|
||||
}
|
||||
|
||||
// --- Breakeven and runner trailing monitor ---
|
||||
if (_runnerActive && !string.IsNullOrEmpty(_runnerSignalName) && Position.Quantity != 0)
|
||||
{
|
||||
double entryPrice = Position.AveragePrice;
|
||||
double currentClose = Close[0];
|
||||
bool isLong = Position.MarketPosition == MarketPosition.Long;
|
||||
|
||||
double profitTicks = isLong
|
||||
? (currentClose - entryPrice) / TickSize
|
||||
: (entryPrice - currentClose) / TickSize;
|
||||
|
||||
// Move runner stop to breakeven + offset once trigger is reached
|
||||
if (EnableAutoBreakeven && !_breakevenMoved && profitTicks >= BreakevenTriggerTicks)
|
||||
{
|
||||
double bePrice = isLong
|
||||
? entryPrice + (BreakevenOffsetTicks * TickSize)
|
||||
: entryPrice - (BreakevenOffsetTicks * TickSize);
|
||||
|
||||
SetStopLoss(_runnerSignalName, CalculationMode.Price, bePrice, false);
|
||||
_breakevenMoved = true;
|
||||
|
||||
Print(String.Format("[SDK] Runner breakeven set at {0:F2} (profit={1:F0} ticks)",
|
||||
bePrice, profitTicks));
|
||||
if (EnableFileLogging)
|
||||
FileLog(String.Format("BREAKEVEN runner stop -> {0:F2} profit={1:F0}ticks",
|
||||
bePrice, profitTicks));
|
||||
}
|
||||
|
||||
// Activate trailing stop on runner once breakeven is secured
|
||||
if (_breakevenMoved && RunnerTrailTicks > 0)
|
||||
{
|
||||
SetTrailStop(_runnerSignalName, CalculationMode.Ticks, RunnerTrailTicks, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear runner state when flat
|
||||
if (Position.Quantity == 0 && _runnerActive)
|
||||
{
|
||||
_runnerActive = false;
|
||||
_breakevenMoved = false;
|
||||
if (EnableFileLogging && !string.IsNullOrEmpty(_runnerSignalName))
|
||||
FileLog("RUNNER closed — position flat");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var barData = ConvertCurrentBar();
|
||||
@@ -427,7 +568,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
}
|
||||
|
||||
protected override void OnPositionUpdate(
|
||||
Position position,
|
||||
NinjaTrader.Cbi.Position position,
|
||||
double averagePrice,
|
||||
int quantity,
|
||||
MarketPosition marketPosition)
|
||||
@@ -442,7 +583,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
{
|
||||
try
|
||||
{
|
||||
dayPnL = Account.Get(AccountItem.GainLoss, Currency.UsDollar);
|
||||
dayPnL = Account.Get(AccountItem.RealizedProfitLoss, Currency.UsDollar);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -637,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],
|
||||
@@ -761,6 +917,11 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
|
||||
private void ProcessStrategyIntent(StrategyIntent intent, StrategyContext context)
|
||||
{
|
||||
// In live/SIM: block if we haven't seen a genuine realtime bar yet (replay guard).
|
||||
// In Strategy Analyzer (State.Historical): always allow — backtest must execute normally.
|
||||
if (State == State.Realtime && !_realtimeBarSeen)
|
||||
return;
|
||||
|
||||
// Portfolio-level risk check — runs before per-strategy risk validation
|
||||
var portfolioDecision = PortfolioRiskManager.Instance.ValidatePortfolioRisk(Name, intent);
|
||||
if (!portfolioDecision.Allow)
|
||||
@@ -840,30 +1001,36 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
|
||||
private void SubmitOrderToNT8(OmsOrderRequest request, StrategyIntent intent)
|
||||
{
|
||||
// Circuit breaker gate
|
||||
if (State == State.Historical)
|
||||
if (State != State.Historical)
|
||||
{
|
||||
// Skip circuit breaker during backtest — wall-clock timeout is meaningless on historical data.
|
||||
}
|
||||
else if (_circuitBreaker != null && !_circuitBreaker.ShouldAllowOrder())
|
||||
if (_circuitBreaker != null && !_circuitBreaker.ShouldAllowOrder())
|
||||
{
|
||||
var state = _circuitBreaker.GetState();
|
||||
Print(string.Format("[SDK] Circuit breaker OPEN — order blocked: {0}", state.Reason));
|
||||
var cbState = _circuitBreaker.GetState();
|
||||
Print(String.Format("[SDK] Circuit breaker OPEN — order blocked: {0}", cbState.Reason));
|
||||
if (_logger != null)
|
||||
_logger.LogWarning("Circuit breaker blocked order: {0}", state.Reason);
|
||||
_logger.LogWarning("Circuit breaker blocked order: {0}", cbState.Reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var orderName = string.Format("SDK_{0}_{1}", intent.Symbol, Guid.NewGuid().ToString("N").Substring(0, 12));
|
||||
bool useRunner = EnableRunner && request.Quantity >= 2;
|
||||
|
||||
int scalerQty = useRunner ? request.Quantity - 1 : request.Quantity;
|
||||
int runnerQty = useRunner ? 1 : 0;
|
||||
|
||||
string baseId = Guid.NewGuid().ToString("N").Substring(0, 12);
|
||||
_scalerSignalName = String.Format("SDK_{0}_S_{1}", intent.Symbol, baseId);
|
||||
_runnerSignalName = useRunner ? String.Format("SDK_{0}_R_{1}", intent.Symbol, baseId) : null;
|
||||
_breakevenMoved = false;
|
||||
_runnerActive = useRunner;
|
||||
|
||||
if (EnableFileLogging)
|
||||
{
|
||||
string grade = "N/A";
|
||||
string score = "N/A";
|
||||
string factors = string.Empty;
|
||||
|
||||
if (intent.Metadata != null && intent.Metadata.ContainsKey("confluence_score"))
|
||||
{
|
||||
var cs = intent.Metadata["confluence_score"] as NT8.Core.Intelligence.ConfluenceScore;
|
||||
@@ -871,55 +1038,46 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
{
|
||||
grade = cs.Grade.ToString();
|
||||
score = cs.WeightedScore.ToString("F3");
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
foreach (var f in cs.Factors)
|
||||
sb.Append(string.Format("{0}={1:F2} ", f.Type, f.Score));
|
||||
sb.Append(String.Format("{0}={1:F2} ", f.Type, f.Score));
|
||||
factors = sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
FileLog(string.Format("SIGNAL {0} | Grade={1} | Score={2}", intent.Side, grade, score));
|
||||
FileLog(String.Format("SIGNAL {0} | Grade={1} | Score={2}", intent.Side, grade, score));
|
||||
if (!string.IsNullOrEmpty(factors))
|
||||
FileLog(string.Format(" Factors: {0}", factors));
|
||||
FileLog(string.Format("SUBMIT {0} {1} @ Market | Stop={2} Target={3}{4}",
|
||||
intent.Side,
|
||||
request.Quantity,
|
||||
intent.StopTicks,
|
||||
intent.TargetTicks,
|
||||
intent.Metadata != null && intent.Metadata.ContainsKey("dynamic_target_ticks") ? " [dynamic]" : ""));
|
||||
FileLog(String.Format(" Factors: {0}", factors));
|
||||
FileLog(String.Format("SUBMIT Scaler={0} Runner={1} Stop={2} Target={3}",
|
||||
scalerQty, runnerQty, intent.StopTicks,
|
||||
intent.TargetTicks.HasValue ? intent.TargetTicks.Value.ToString() : "none"));
|
||||
}
|
||||
|
||||
_executionAdapter.SubmitOrder(request, orderName);
|
||||
|
||||
// Register stop and target BEFORE submitting the entry order.
|
||||
// NT8 requires stop/target to be pre-registered to the signal name
|
||||
// so they are applied correctly in both backtest and live/SIM modes.
|
||||
// --- Submit scaler leg ---
|
||||
if (intent.StopTicks > 0)
|
||||
SetStopLoss(orderName, CalculationMode.Ticks, (int)intent.StopTicks, false);
|
||||
|
||||
SetStopLoss(_scalerSignalName, CalculationMode.Ticks, (int)intent.StopTicks, false);
|
||||
if (intent.TargetTicks.HasValue && intent.TargetTicks.Value > 0)
|
||||
SetProfitTarget(orderName, CalculationMode.Ticks, (int)intent.TargetTicks.Value);
|
||||
SetProfitTarget(_scalerSignalName, CalculationMode.Ticks, (int)intent.TargetTicks.Value);
|
||||
|
||||
if (request.Side == OmsOrderSide.Buy)
|
||||
EnterLong(scalerQty, _scalerSignalName);
|
||||
else
|
||||
EnterShort(scalerQty, _scalerSignalName);
|
||||
|
||||
// --- Submit runner leg (no fixed target — exits via trailing stop) ---
|
||||
if (useRunner)
|
||||
{
|
||||
if (request.Type == OmsOrderType.Market)
|
||||
EnterLong(request.Quantity, orderName);
|
||||
else if (request.Type == OmsOrderType.Limit && request.LimitPrice.HasValue)
|
||||
EnterLongLimit(request.Quantity, (double)request.LimitPrice.Value, orderName);
|
||||
else if (request.Type == OmsOrderType.StopMarket && request.StopPrice.HasValue)
|
||||
EnterLongStopMarket(request.Quantity, (double)request.StopPrice.Value, orderName);
|
||||
}
|
||||
else if (request.Side == OmsOrderSide.Sell)
|
||||
{
|
||||
if (request.Type == OmsOrderType.Market)
|
||||
EnterShort(request.Quantity, orderName);
|
||||
else if (request.Type == OmsOrderType.Limit && request.LimitPrice.HasValue)
|
||||
EnterShortLimit(request.Quantity, (double)request.LimitPrice.Value, orderName);
|
||||
else if (request.Type == OmsOrderType.StopMarket && request.StopPrice.HasValue)
|
||||
EnterShortStopMarket(request.Quantity, (double)request.StopPrice.Value, orderName);
|
||||
if (intent.StopTicks > 0)
|
||||
SetStopLoss(_runnerSignalName, CalculationMode.Ticks, (int)intent.StopTicks, false);
|
||||
// No SetProfitTarget on runner — trail stop will manage exit
|
||||
|
||||
if (request.Side == OmsOrderSide.Buy)
|
||||
EnterLong(runnerQty, _runnerSignalName);
|
||||
else
|
||||
EnterShort(runnerQty, _runnerSignalName);
|
||||
}
|
||||
|
||||
_executionAdapter.SubmitOrder(request, _scalerSignalName);
|
||||
|
||||
if (_circuitBreaker != null)
|
||||
_circuitBreaker.OnSuccess();
|
||||
}
|
||||
@@ -927,8 +1085,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
{
|
||||
if (_circuitBreaker != null)
|
||||
_circuitBreaker.OnFailure();
|
||||
|
||||
Print(string.Format("[SDK] SubmitOrderToNT8 failed: {0}", ex.Message));
|
||||
Print(String.Format("[SDK] SubmitOrderToNT8 failed: {0}", ex.Message));
|
||||
if (_logger != null)
|
||||
_logger.LogError("SubmitOrderToNT8 failed: {0}", ex.Message);
|
||||
throw;
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
/// </summary>
|
||||
public class SimpleORBNT8 : NT8StrategyBase
|
||||
{
|
||||
private int _lastSignalDirection;
|
||||
|
||||
[NinjaScriptProperty]
|
||||
[Display(Name = "Opening Range Minutes", GroupName = "ORB Strategy", Order = 1)]
|
||||
[Range(5, 120)]
|
||||
@@ -38,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)]
|
||||
@@ -59,16 +65,24 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
|
||||
DailyLossLimit = 1000.0;
|
||||
MaxTradeRisk = 200.0;
|
||||
MaxOpenPositions = 1;
|
||||
MaxOpenPositions = 2;
|
||||
RiskPerTrade = 100.0;
|
||||
MinContracts = 1;
|
||||
MaxContracts = 3;
|
||||
|
||||
Calculate = Calculate.OnBarClose;
|
||||
BarsRequiredToTrade = 50;
|
||||
MinTradeGrade = 5;
|
||||
EnableLongTrades = true;
|
||||
// Long-only: short trades permanently disabled pending backtest confirmation
|
||||
EnableShortTrades = false;
|
||||
EnableAutoBreakeven = true;
|
||||
BreakevenTriggerTicks = 20;
|
||||
BreakevenOffsetTicks = 1;
|
||||
EnableRunner = true;
|
||||
RunnerTrailTicks = 20;
|
||||
ForceSessionReset = false;
|
||||
StartBehavior = StartBehavior.AdoptAccountPosition;
|
||||
}
|
||||
else if (State == State.Configure)
|
||||
{
|
||||
@@ -82,11 +96,24 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
{
|
||||
if (_strategyConfig != null && BarsArray != null && BarsArray.Length > 1)
|
||||
{
|
||||
DailyBarContext dailyContext = BuildDailyBarContext(0, 0.0, (double)Volume[0]);
|
||||
DailyBarContext dailyContext = BuildDailyBarContext(_lastSignalDirection, 0.0, (double)Volume[0]);
|
||||
_strategyConfig.Parameters["daily_bars"] = dailyContext;
|
||||
}
|
||||
|
||||
base.OnBarUpdate();
|
||||
|
||||
if (Position != null)
|
||||
{
|
||||
if (Position.MarketPosition == MarketPosition.Long)
|
||||
_lastSignalDirection = 1;
|
||||
else if (Position.MarketPosition == MarketPosition.Short)
|
||||
_lastSignalDirection = -1;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool GetForceSessionReset()
|
||||
{
|
||||
return ForceSessionReset;
|
||||
}
|
||||
|
||||
protected override IStrategy CreateSdkStrategy()
|
||||
@@ -160,6 +187,10 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
lines.Insert(endIdx + 6, string.Format("StopDollars : {0:C}", StopTicks * tickDollarValue));
|
||||
lines.Insert(endIdx + 7, string.Format("TargetDollars : {0:C}", TargetTicks * tickDollarValue));
|
||||
lines.Insert(endIdx + 8, string.Format("RR_Ratio : {0:F2}:1", (double)TargetTicks / StopTicks));
|
||||
lines.Insert(endIdx + 9, String.Format("AutoBreakeven : {0} @ {1}ticks + {2}tick offset",
|
||||
EnableAutoBreakeven, BreakevenTriggerTicks, BreakevenOffsetTicks));
|
||||
lines.Insert(endIdx + 10, String.Format("Runner : {0} | Trail={1}ticks",
|
||||
EnableRunner, RunnerTrailTicks));
|
||||
|
||||
return lines;
|
||||
}
|
||||
@@ -225,4 +256,3 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
/// <param name="parameters">Parameter map.</param>
|
||||
public void SetParameters(Dictionary<string, object> 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()
|
||||
|
||||
Reference in New Issue
Block a user