Skip to content

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, including ChartInstanceKey. 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 ChartInstanceKey parameter 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

  1. Own: Placed directly by this running strategy instance.
  2. Matched by pointer (_activeEntryOrder), in-memory registration (_myPlacedOrderIds), or tag: parts[0] == "TJ" && parts[2] == StrategyInstanceId.
  3. Authority: Full management rights (cancel on expiry, modify SL/TP, liquidate on exit).
  4. Peer: Placed by an active sibling strategy instance on the same account.
  5. Tagged TJ|*, but parts[2] != StrategyInstanceId.
  6. Authority: Strictly READ-ONLY / IGNORE. Never cancel, modify, or sweep.
  7. Unclaimed: Tagged TJ|* from an orphaned or deleted strategy instance whose host chart was closed or replaced.
  8. Authority: Log warning to HUD / Execution Log. Never auto-cancel on the hot-path. Requires explicit user action or dedicated off-path governance.
  9. Foreign: Manual orders, third-party indicators/bots, or platform-native ATM brackets.
  10. Comment lacks TJ| namespace or is a broker child order (IsStopLossOrder, IsTakeProfitOrder).
  11. 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

  1. Strategy evaluates signal on bar close. Passes time window, daily drawdown, and position guards.
  2. Orders submitted with TJ|{UID}|{Key}|{SignalId}. Placed order ID stored in _myPlacedOrderIds.
  3. Fill occurs. Strategy adopts position, establishes OCO brackets via ATAS TradingManager.
  4. Profit target hit. OCO cancels stop loss. Strategy resets to Idle.

Scenario B: Unfilled Entry Window Expiration

  1. Strategy places limit entry order at 29992.00 on Bar 100.
  2. Price does not touch limit order within FillWindowBars (e.g. 1 bar).
  3. On Bar 102 close, strategy evaluates expiry:
  4. Finds order where IsMyOrder(o) == true.
  5. Adds order.Id to _pendingCancelIds immediately.
  6. Invokes TradingManager.CancelOrderAsync(order).
  7. Order cancelled cleanly without cancel storms.

Scenario C: Concurrent Sibling on Different Timeframe (Renko 64 + Renko 100)

  • Strategy 1 (Renko 64, Key a1b2c3d4) and Strategy 2 (Renko 100, Key e5f6g7h8) trade simultaneously.
  • Strategy 1 places an entry limit. Strategy 2 runs ManagePendingOrders on its bar close.
  • Strategy 2 calls TJOrderOwnership.Classify(order.Comment, "e5f6g7h8") -> Returns OrderOwnershipCategory.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 64 caused identical instance keys.
  • Under v3.4.17+ / v1.3.21+, each strategy generates and persists an independent ChartInstanceKey parameter in its chart template.
  • Instance keys are distinct (3f8a1290 vs 7d04e1bc), 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").
  • CancelAllPendingOrders filters orders strictly with IsMyOrder(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 activeMarketExit with IsMyOrder(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.Timer on 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.Classify executes 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.Classify evaluates it as Unclaimed.
  • 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: PurgeInactiveOrders should remain false unless all charts are confirmed to run with active keep-alives and within the same ATAS process.

6. Shared Account vs. Multi-Account Guidelines

  • 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 HistoryMyTrades and in-memory TradingManager.MyTrades are filtered using IsMyHistoryTrade(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.