Strategy Order Lifecycle & Multi-Specialist Concurrency — Reference
status: ACTIVE tier: 2 (design reference) last-verified: 2026-10-16 verified-against: a18c123f procedure-skill: .agents/skills/publish/SKILL.md
1. Executive Summary & Design Rationale
Automated strategies in the Trader-Jan ecosystem (TJ Alpha Strategy and Options Strategy) execute alongside each other across multiple market regimes, sessions (ASIA, EU, US RTH), and bar granularities (M5, M15, T900, Renko 64, Renko 100).
When running multiple specialist strategy instances against a shared single trading account (or segregated sub-accounts), each strategy instance must strictly observe execution boundaries: 1. Zero Interference: A strategy must never cancel, modify, or liquidate orders belonging to another strategy instance or manual trader. 2. Deterministic Ownership: Orders must carry robust, persistent cryptographic identifiers that survive template reloads, workspace saves, and ATAS restarts. 3. No Silent Background Sweeping: Automated cleanup loops must only operate on orders demonstrably placed by the active strategy instance. Unclaimed or dead-strategy orders must never be swept blindly by peer strategies on hot paths. 4. Concurrency Safety: Bracket containment and shared position guards prevent race conditions when two specialists trade concurrently on the same account. 5. Session Ledger Isolation: Mid-session restarts and recalculations must attribute historical fills strictly to the owning strategy instance to protect daily profit/loss circuit breakers.
2. Order Identification & Tag Specification
All strategy-originated orders carry a standardized four-token structured comment:
TJ|{GenomeUid}|{ChartInstanceKey}|{SignalIdOrPayload}
Token Anatomy
| Token Index | Field | Format / Example | Purpose |
|---|---|---|---|
0 |
Platform Namespace | TJ |
Identifies orders belonging to the Trader-Jan strategy family. |
1 |
Specialist Genome UID | 2B353E2A or CUSTOM |
8-character hex UID of the loaded specialist parameter genome. |
2 |
Chart Instance Key | aa5ff956 |
8-character hex unique persistent identifier bound to this chart instance. |
3 |
Signal ID / Action | 9 or EXIT |
1-based sequential signal index for entry limits, or EXIT for market liquidations. |
Chart Instance Key Resolution
To guarantee uniqueness across sibling strategies running on identical instruments and chart types (e.g., both ASIA and EU specialists running on MNQ Renko 64), each strategy exposes:
[Parameter]
[Display(Name = "Chart Instance Key", GroupName = "Strategy — Identity", Order = 10)]
public string ChartInstanceKey { get; set; } = Guid.NewGuid().ToString("N")[..8];
- Serialization Guarantee: When added to a chart or saved in an ATAS workspace/template, ATAS persists
ChartInstanceKey. Sibling charts each receive a unique GUID prefix that never collides. - Unique Random Fallback: If deserialized with a null/empty key, it immediately initializes with a fresh, distinct 8-character GUID prefix (
Guid.NewGuid().ToString("N")[..8]), eliminating colliding static hash fallbacks.
[!WARNING] Chart Duplication & Template Cloning Procedure: In ATAS, cloning a chart or applying an existing chart template deep-copies all serialized
[Parameter]values, includingChartInstanceKey. If you duplicate a chart containing a configured specialist, the newly cloned chart will initially inherit the same instance key!Operational Protocol: When duplicating a chart or creating a new specialist from an existing chart template, you must rotate or clear the
ChartInstanceKeyparameter in the strategy settings before enabling it. If cleared, the strategy will automatically generate a fresh unique key upon start.
3. Pure-Core Ownership Classification Model
Order attribution logic is encapsulated in TraderJan.Indicators.TJOrderOwnership to enable bit-level NUnit testing without ATAS UI or TradingManager dependencies.
flowchart TD
Order[Order Received] --> SLTP{Is StopLoss or<br/>TakeProfit order?}
SLTP -- Yes --> Foreign[Foreign / Broker Bracket]
SLTP -- No --> Tracked{Matches _myPlacedOrderIds<br/>or _activeEntryOrder?}
Tracked -- Yes --> Own[Own Order]
Tracked -- No --> Prefix{Comment starts<br/>with 'TJ|'?}
Prefix -- No --> Foreign
Prefix -- Yes --> TokenCount{Tokens >= 3?}
TokenCount -- No --> Foreign
TokenCount -- Yes --> KeyCheck{parts[2] == StrategyInstanceId?}
KeyCheck -- Yes --> Own
KeyCheck -- No --> PeerOrUnclaimed{Known Peer Instance?}
PeerOrUnclaimed -- Active Sibling --> Peer[Peer Order]
PeerOrUnclaimed -- Unmatched / Dead --> Unclaimed[Unclaimed Order]
The Four Ownership Categories
Own: Placed directly by this running strategy instance.- Matched by pointer (
_activeEntryOrder), in-memory registration (_myPlacedOrderIds), or tag:parts[0] == "TJ" && parts[2] == StrategyInstanceId. - Authority: Full management rights (cancel on expiry, modify SL/TP, liquidate on exit).
Peer: Placed by an active sibling strategy instance on the same account.- Tagged
TJ|*, butparts[2] != StrategyInstanceId. - Authority: Strictly READ-ONLY / IGNORE. Never cancel, modify, or sweep.
Unclaimed: TaggedTJ|*from an orphaned or deleted strategy instance whose host chart was closed or replaced.- Authority: Log warning to HUD / Execution Log. Never auto-cancel on the hot-path. Requires explicit user action or dedicated off-path governance.
Foreign: Manual orders, third-party indicators/bots, or platform-native ATM brackets.- Comment lacks
TJ|namespace or is a broker child order (IsStopLossOrder,IsTakeProfitOrder). - Authority: Completely ignored by strategy lifecycle methods.
4. State Machine Architecture & Transitions
stateDiagram-v2
[*] --> Idle
Idle --> SignalDetected: Bar Close & Signal Series != 0
SignalDetected --> Idle: Blocked (Daily Limit / Restricted Window / Interlock Position Guard)
SignalDetected --> PendingEntry: Risk Check Pass -> Submit Limit Entry (Tag: TJ|UID|Key|SignalId)
PendingEntry --> InPosition: OnPositionChanged (Entry Filled) -> Register Bracket SL/TP
PendingEntry --> Idle: Signal Window Expired (Bar > SignalBar + Window) -> CancelOrder
PendingEntry --> Idle: Session Restricted Window Reached -> CancelOrder
InPosition --> InPosition: Candle Close -> Ratchet Breakeven / Trailing ATR SL
InPosition --> ExitPending: Profit Target / Stop Loss Hit (OCO) OR Time Forced Flatten
ExitPending --> InPosition: ReconcileExitState Timeout (< 3 retries) -> Re-fire Flatten
ExitPending --> Liquidated: OnPositionChanged (PosQty == 0) -> Purge Local State
ExitPending --> ManualAlert: Retries Exceeded (3/3) -> Sound Alarm, Require Manual Intervention
Liquidated --> Idle: Safe Reset -> Ready for Next Bar
5. Core Operational Scenarios
Scenario A: Clean Single-Specialist Trade Execution
- Strategy evaluates signal on bar close. Passes time window, daily drawdown, and position guards.
- Orders submitted with
TJ|{UID}|{Key}|{SignalId}. Placed order ID stored in_myPlacedOrderIds. - Fill occurs. Strategy adopts position, establishes OCO brackets via ATAS TradingManager.
- Profit target hit. OCO cancels stop loss. Strategy resets to
Idle.
Scenario B: Unfilled Entry Window Expiration
- Strategy places limit entry order at
29992.00on Bar 100. - Price does not touch limit order within
FillWindowBars(e.g. 1 bar). - On Bar 102 close, strategy evaluates expiry:
- Finds order where
IsMyOrder(o) == true. - Adds
order.Idto_pendingCancelIdsimmediately. - Invokes
TradingManager.CancelOrderAsync(order). - Order cancelled cleanly without cancel storms.
Scenario C: Concurrent Sibling on Different Timeframe (Renko 64 + Renko 100)
- Strategy 1 (
Renko 64, Keya1b2c3d4) and Strategy 2 (Renko 100, Keye5f6g7h8) trade simultaneously. - Strategy 1 places an entry limit. Strategy 2 runs
ManagePendingOrderson its bar close. - Strategy 2 calls
TJOrderOwnership.Classify(order.Comment, "e5f6g7h8")-> ReturnsOrderOwnershipCategory.Peer. - Strategy 2 ignores Strategy 1's order completely. Strategy 1's order remains active.
Scenario D: Concurrent Sibling on Identical Timeframe (Renko 64 ASIA + Renko 64 EU)
- Both strategies share
MNQ Renko 64. - Under legacy versions, FNV-1a hash of
Alpha|MNQ|Renko 64caused identical instance keys. - Under
v3.4.17+/v1.3.21+, each strategy generates and persists an independentChartInstanceKeyparameter in its chart template. - Instance keys are distinct (
3f8a1290vs7d04e1bc), preventing any identity collision.
Scenario E: Overlapping Session Boundary Transition
- EU Specialist reaches end of session window at 08:30 CT (
Restricted Time Window). - US Specialist opens its session window at 08:30 CT and places an entry limit at
08:30:00.150. - EU Specialist calls
CancelAllPendingOrders("Restricted Time Window"). CancelAllPendingOrdersfilters orders strictly withIsMyOrder(o).- EU Specialist cancels only its own resting orders; US Specialist's entry order is completely unaffected.
Scenario F: Position Liquidation & ReconcileExitState Recovery
- Strategy initiates
FlattenPosition()on circuit breaker or session end. ReconcileExitState()monitors the market exit order.- Filter verifies
activeMarketExitwithIsMyOrder(o). - If a peer strategy happens to execute a market order at the same second, it is not mistaken for this strategy's exit order. Recovery retries continue accurately until local position is verified flat.
Scenario G: Dead Instance Governance (Chart Closed While Order Resting)
- User closes or removes an older chart from the workspace while a limit order was resting.
- Running strategies maintain a process-wide thread-safe fleet registry (
TJOrderOwnership.RegisterInstance/UnregisterInstance). - Background Keep-Alive Timer & Extended TTL: Live strategies run an independent 30-second
System.Threading.Timeron the ThreadPool (TJOrderOwnership.KeepAlive(StrategyInstanceId)), refreshed also on market ticks. This decouples liveness tracking from tick cadence, guaranteeing that quiet, low-activity, or off-hours charts are never mistakenly classified as dead. - Fail-Safe 30-Minute Staleness Eviction: If an instance crashes or is terminated without invoking
OnStopping,TJOrderOwnership.SweepStaleInstances()evicts its key after 30 minutes of complete inactivity (DefaultInstanceTtl = TimeSpan.FromMinutes(30)). - Zero-Allocation Hot Path Classification: When surviving strategies scan resting orders,
TJOrderOwnership.Classifyexecutes an O(1) zero-allocation lookup directly against the registry (ContainsKey), eliminating per-tick array allocations and LINQ evaluation on foreign orders. - When an order tagged
TJ|*belongs to an instance key that is no longer active in the fleet registry,TJOrderOwnership.Classifyevaluates it asUnclaimed. - Diagnostic Warning Throttling: To eliminate high-frequency log flooding on every market tick, the strategy de-duplicates warnings using
_warnedUnclaimedOrderIds, emitting an explicit warning exactly once per active order ID:[Safety] Unclaimed TJ Order #{Id} (Tag: {Comment}) detected from an inactive chart. - Configurable Purge Policy (
PurgeInactiveOrders): - By default (
PurgeInactiveOrders = false), the strategy warns and ignores the order to avoid interfering with charts running outside the process or manual operations. - If enabled (
PurgeInactiveOrders = true), the strategy actively purges the dead order from the book and logs:[Safety] Purged Unclaimed Order #{Id} per user policy. - Safety Rule:
PurgeInactiveOrdersshould remainfalseunless all charts are confirmed to run with active keep-alives and within the same ATAS process.
6. Shared Account vs. Multi-Account Guidelines
Deployment Mode 1: Segregated Accounts (Recommended for Production)
- Each specialist runs on a dedicated sub-account (e.g.,
Apex-01,Apex-02,Tradovate-Sim-1). - Portfolios are completely isolated by broker infrastructure.
- Full parallel position concurrency is natively supported.
Deployment Mode 2: Single Shared Account (Interlock Guard & Ledger Isolation Active)
- Multiple specialists execute against a single account (e.g.
Apex-PA-Single). -
Interlock Position Guard:
csharp [Parameter] [Display(Name = "Interlock Position Guard", GroupName = "Strategy — Safety", Order = 15, Description = "Wait for account to be completely flat before entering new trades on shared accounts.")] public bool EnableInterlockPositionGuard { get; set; } = true;csharp if (EnableInterlockPositionGuard && TradingManager?.Position != null && TradingManager.Position.Volume != 0 && _currentPosQty == 0) { this.LogWarn($"[Safety] Signal blocked: Account has an active position ({TradingManager.Position.Volume}) from a peer strategy or manual trade. Waiting for flat."); return; }If Specialist A holds an active 2-contract long position, Specialist B will defer new signal entries until the account returns to completely flat (0). This prevents bracket cross-contamination and aggregate position margin violations. Users may toggle this guard off if running multi-contract basket specialists. -
Session Ledger Isolation (Loophole 4 Resolution): When a strategy restarts mid-session or recalculates parameters, it restores historical trades via
ReconstructSessionStatsFromPlatform(). On a shared account, the platform history contains trades from all sibling specialists. - Both
HistoryMyTradesand in-memoryTradingManager.MyTradesare filtered usingIsMyHistoryTrade(t): ```csharp private bool IsMyHistoryTrade(HistoryMyTrade t) { if (t == null) return false; string? comment = t.EnterTrade?.Order?.Comment ?? t.Comment; if (!string.IsNullOrEmpty(comment)) return TJOrderOwnership.Classify(comment, StrategyInstanceId) == OrderOwnershipCategory.Own;if (TradingManager != null && t.EnterTrade != null && !string.IsNullOrEmpty(t.EnterTrade.Id)) { var enterFill = TradingManager.MyTrades.FirstOrDefault(f => f.Id == t.EnterTrade.Id); if (enterFill?.Order?.Comment != null) return TJOrderOwnership.Classify(enterFill.Order.Comment, StrategyInstanceId) == OrderOwnershipCategory.Own; } return false;} ``` - This guarantees that peer trades never contaminate this specialist's realized session P&L, win count, or daily circuit breaker thresholds.
Scope Note: Strategy Attribution vs. Aggregate Account Risk
[!NOTE] Attribution vs. Account Budgeting: The session ledger isolation mechanism ensures accurate, segregated P&L and trade-count attribution per strategy instance. This protects each specialist from false circuit breaker halts caused by sibling trades. However, running multiple specialists concurrently against a single shared account does not natively enforce an aggregate portfolio-level daily stop budget (e.g., if three specialists each have a -$500 stop, total account drawdown can reach -$1,500). Fleet-level account budget synthesis is governed separately under Limits-Aware Specialist Compilation.
7. Verification & Compliance Checklist
Before publishing any modification to strategy order handling:
- [x] Run dotnet test TraderJan.Tests/TraderJan.Tests.csproj -p:Platform=Cross (Assert ≥ 252 tests pass).
- [x] Run ./audit.sh "TraderJan.Strategies/TJ Alpha Strategy.cs" --build (7/7 checks, 0 warnings/errors).
- [x] Run ./audit.sh "TraderJan.Strategies/Options Strategy.cs" --build (7/7 checks, 0 warnings/errors).
- [x] Run ./Backtesting/run_tests.sh --parity (100% green, 14/14).
- [x] Run ./recon.sh --summary (Pre-existing baseline status verified; R3 INVESTIGATE noted).
- [x] Confirm no #if CROSS_PLATFORM guards or raw WPF namespaces in strategy code.