Files
nt8-sdk/.kilocode/rules/coding_patterns.md
mo 9a28a49292
Some checks failed
Build and Test / build (push) Has been cancelled
Pre-cleanup baseline snapshot
2026-04-05 16:50:18 -04:00

6.3 KiB
Raw Permalink Blame History

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)

// ❌ 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

// 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:

private readonly object _lock = new object();

Every access to shared Dictionary, List, Queue, or any field touched by multiple threads:

// ❌ NEVER
_activeOrders[orderId] = status;

// ✅ ALWAYS
lock (_lock)
{
    _activeOrders[orderId] = status;
}

Read-then-write must be atomic

// ❌ WRONG — race condition between check and write
if (!_orders.ContainsKey(id))
    _orders[id] = newOrder;

// ✅ CORRECT
lock (_lock)
{
    if (!_orders.ContainsKey(id))
        _orders[id] = newOrder;
}

2. Error Handling — Try-Catch on All Public Methods

public ReturnType MethodName(Type parameter)
{
    // 1. Validate parameters first
    if (parameter == null)
        throw new ArgumentNullException("parameter");

    // 2. Wrap the main logic
    try
    {
        // Implementation
        return result;
    }
    catch (SpecificException ex)
    {
        _logger.LogError("Specific failure in MethodName: {0}", ex.Message);
        throw;
    }
    catch (Exception ex)
    {
        _logger.LogError("Unexpected failure in MethodName: {0}", ex.Message);
        throw;
    }
}

3. Logging — Always string.Format, Never $""

// ❌ NEVER — C# 6 syntax, breaks NT8 compile
_logger.LogInformation($"Order {orderId} filled");

// ✅ ALWAYS
_logger.LogInformation("Order {0} filled", orderId);
_logger.LogWarning("Risk check failed for {0}: {1}", symbol, reason);
_logger.LogError("Exception in {0}: {1}", "MethodName", ex.Message);
_logger.LogCritical("Emergency flatten triggered: {0}", reason);

Log level guide

Level When to use
LogTrace Entering/exiting methods, fine-grained flow
LogDebug State reads, normal data flow
LogInformation Important events: order submitted, filled, cancelled
LogWarning Recoverable issues: validation failed, limit approaching
LogError Failures: exceptions, unexpected states
LogCritical System integrity issues: emergency flatten, data corruption

4. Events — Never Raise Inside Locks

Raising events inside a lock causes deadlocks when event handlers acquire other locks.

// ❌ DEADLOCK RISK
lock (_lock)
{
    _state = newState;
    OrderStateChanged?.Invoke(this, args);  // handler may try to acquire _lock
}

// ✅ CORRECT
OrderState newState;
lock (_lock)
{
    newState = CalculateNewState();
    _state = newState;
}
// Raise AFTER releasing lock
RaiseOrderStateChanged(orderId, previousState, newState);

5. Constructor — Validate All Dependencies

public MyClass(ILogger<MyClass> logger, ISomeDependency dep)
{
    if (logger == null)
        throw new ArgumentNullException("logger");
    if (dep == null)
        throw new ArgumentNullException("dep");

    _logger = logger;
    _dep = dep;

    // Initialize collections
    _activeOrders = new Dictionary<string, OrderStatus>();

    _logger.LogInformation("MyClass initialized");
}

6. XML Documentation — Required on All Public Members

/// <summary>
/// Brief one-line description of what this does.
/// </summary>
/// <param name="intent">The trading intent to validate.</param>
/// <param name="context">Current strategy context with account state.</param>
/// <returns>Risk decision indicating allow or reject.</returns>
/// <exception cref="ArgumentNullException">Thrown when intent or context is null.</exception>
public RiskDecision ValidateOrder(StrategyIntent intent, StrategyContext context)
{
    ...
}

7. NT8-Specific Patterns (NinjaScript)

When writing code that runs inside NinjaTrader (in NT8.Adapters/):

// Always guard OnBarUpdate
protected override void OnBarUpdate()
{
    if (BarsInProgress != 0) return;
    if (CurrentBar < BarsRequiredToTrade) return;
    // ...
}

// Managed order pattern — set stops BEFORE entry
SetStopLoss("SignalName", CalculationMode.Ticks, stopTicks, false);
SetProfitTarget("SignalName", CalculationMode.Ticks, targetTicks);
EnterLong(contracts, "SignalName");

// Use string.Format for Print() too
Print(string.Format("Order submitted: {0} contracts at {1}", qty, price));

8. Checklist Before Marking Any Method Complete

  • Parameter null checks at the top
  • try-catch wrapping the body
  • All Dictionary/collection access inside lock (_lock)
  • All logging uses string.Format() (no $"")
  • XML /// <summary> on every public method, property, class
  • No C# 6+ syntax
  • Events raised outside lock blocks
  • verify-build.bat passes