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

253 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```csharp
private readonly object _lock = new object();
```
Every access to shared `Dictionary`, `List`, `Queue`, or any field touched by multiple threads:
```csharp
// ❌ NEVER
_activeOrders[orderId] = status;
// ✅ ALWAYS
lock (_lock)
{
_activeOrders[orderId] = status;
}
```
### Read-then-write must be atomic
```csharp
// ❌ 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
```csharp
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 $""
```csharp
// ❌ 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.
```csharp
// ❌ 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
```csharp
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
```csharp
/// <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/`):
```csharp
// 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