4.7 KiB
4.7 KiB
Coding Patterns — NT8 SDK Required Patterns
All code in the NT8 SDK MUST follow these patterns without exception.
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-catchwrapping the body- All
Dictionary/collection access insidelock (_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.batpasses