Production hardening: kill switch, circuit breaker, trailing stops, log level, holiday calendar
Some checks failed
Build and Test / build (push) Has been cancelled
Some checks failed
Build and Test / build (push) Has been cancelled
This commit is contained in:
@@ -1,272 +1,195 @@
|
||||
# Mandatory Coding Patterns
|
||||
# Coding Patterns — NT8 SDK Required Patterns
|
||||
|
||||
These patterns MUST be followed in all code you write for the NT8 SDK.
|
||||
All code in the NT8 SDK MUST follow these patterns without exception.
|
||||
|
||||
## Thread Safety - Dictionary Access
|
||||
---
|
||||
|
||||
ALL access to shared dictionaries MUST use locks.
|
||||
## 1. Thread Safety — Lock Everything Shared
|
||||
|
||||
### ❌ WRONG - No Lock
|
||||
```csharp
|
||||
_activeOrders[orderId] = orderStatus; // DANGEROUS!
|
||||
```
|
||||
|
||||
### ✅ CORRECT - With Lock
|
||||
```csharp
|
||||
lock (_lock)
|
||||
{
|
||||
_activeOrders[orderId] = orderStatus;
|
||||
}
|
||||
```
|
||||
|
||||
### Rule
|
||||
Every class with shared state MUST have:
|
||||
Every class with shared state must have a lock object:
|
||||
```csharp
|
||||
private readonly object _lock = new object();
|
||||
```
|
||||
|
||||
Every access to shared collections MUST be inside:
|
||||
Every access to shared `Dictionary`, `List`, `Queue`, or any field touched by multiple threads:
|
||||
```csharp
|
||||
// ❌ NEVER
|
||||
_activeOrders[orderId] = status;
|
||||
|
||||
// ✅ ALWAYS
|
||||
lock (_lock)
|
||||
{
|
||||
// Dictionary/List operations here
|
||||
_activeOrders[orderId] = status;
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling - Try-Catch Required
|
||||
|
||||
ALL public methods MUST have try-catch blocks.
|
||||
|
||||
### ❌ WRONG - No Error Handling
|
||||
### Read-then-write must be atomic
|
||||
```csharp
|
||||
public async Task<string> SubmitOrder(OrderRequest request)
|
||||
// ❌ WRONG — race condition between check and write
|
||||
if (!_orders.ContainsKey(id))
|
||||
_orders[id] = newOrder;
|
||||
|
||||
// ✅ CORRECT
|
||||
lock (_lock)
|
||||
{
|
||||
var orderId = GenerateOrderId();
|
||||
await _nt8Adapter.SubmitToNT8(orderStatus);
|
||||
return orderId;
|
||||
if (!_orders.ContainsKey(id))
|
||||
_orders[id] = newOrder;
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ CORRECT - With Error Handling
|
||||
```csharp
|
||||
public async Task<string> SubmitOrder(OrderRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException("request");
|
||||
|
||||
try
|
||||
{
|
||||
request.Validate();
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
_logger.LogError("Order validation failed: {0}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var orderId = GenerateOrderId();
|
||||
await _nt8Adapter.SubmitToNT8(orderStatus);
|
||||
return orderId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Order submission failed: {0}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## 2. Error Handling — Try-Catch on All Public Methods
|
||||
|
||||
### Pattern Template
|
||||
```csharp
|
||||
public ReturnType MethodName(Type parameter)
|
||||
{
|
||||
// 1. Validate parameters (throw ArgumentNullException/ArgumentException)
|
||||
// 1. Validate parameters first
|
||||
if (parameter == null)
|
||||
throw new ArgumentNullException("parameter");
|
||||
|
||||
// 2. Try-catch for operation-specific errors
|
||||
|
||||
// 2. Wrap the main logic
|
||||
try
|
||||
{
|
||||
// Main logic
|
||||
// Implementation
|
||||
return result;
|
||||
}
|
||||
catch (SpecificException ex)
|
||||
{
|
||||
_logger.LogError("Specific error: {0}", ex.Message);
|
||||
// Handle or re-throw
|
||||
_logger.LogError("Specific failure in MethodName: {0}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Unexpected error: {0}", ex.Message);
|
||||
_logger.LogError("Unexpected failure in MethodName: {0}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Logging - Structured and Consistent
|
||||
---
|
||||
|
||||
Use structured logging with string.Format (NOT string interpolation).
|
||||
## 3. Logging — Always string.Format, Never $""
|
||||
|
||||
### Log Levels
|
||||
|
||||
#### LogTrace - Detailed Flow
|
||||
```csharp
|
||||
_logger.LogTrace("Entering method {0} with parameter {1}", methodName, param);
|
||||
// ❌ 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);
|
||||
```
|
||||
|
||||
#### LogDebug - Normal Operations
|
||||
### 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
|
||||
_logger.LogDebug("Order {0} state is {1}", orderId, state);
|
||||
```
|
||||
|
||||
#### LogInformation - Important Events
|
||||
```csharp
|
||||
_logger.LogInformation("Order {0} submitted successfully at {1}", orderId, timestamp);
|
||||
_logger.LogInformation("Order {0} filled: {1} contracts @ {2:F2}", orderId, qty, price);
|
||||
```
|
||||
|
||||
#### LogWarning - Recoverable Issues
|
||||
```csharp
|
||||
_logger.LogWarning("Order validation failed: {0}", validationError);
|
||||
_logger.LogWarning("Maximum active orders reached: {0}", maxOrders);
|
||||
```
|
||||
|
||||
#### LogError - Failures
|
||||
```csharp
|
||||
_logger.LogError("Failed to submit order {0} to NT8: {1}", orderId, ex.Message);
|
||||
_logger.LogError("Invalid state transition: {0} -> {1}", fromState, toState);
|
||||
```
|
||||
|
||||
#### LogCritical - System Integrity Issues
|
||||
```csharp
|
||||
_logger.LogCritical("Emergency flatten failed for {0}: {1}", symbol, ex.Message);
|
||||
```
|
||||
|
||||
### ❌ WRONG - String Interpolation
|
||||
```csharp
|
||||
_logger.LogInformation($"Order {orderId} submitted"); // C# 6+ feature!
|
||||
```
|
||||
|
||||
### ✅ CORRECT - string.Format
|
||||
```csharp
|
||||
_logger.LogInformation("Order {0} submitted", orderId);
|
||||
```
|
||||
|
||||
## XML Documentation - Required
|
||||
|
||||
ALL public and protected members MUST have XML documentation.
|
||||
|
||||
### ❌ WRONG - No Documentation
|
||||
```csharp
|
||||
public interface IOrderManager
|
||||
// ❌ DEADLOCK RISK
|
||||
lock (_lock)
|
||||
{
|
||||
Task<string> SubmitOrder(OrderRequest request);
|
||||
_state = newState;
|
||||
OrderStateChanged?.Invoke(this, args); // handler may try to acquire _lock
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ CORRECT - With Documentation
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Order management interface - manages complete order lifecycle
|
||||
/// </summary>
|
||||
public interface IOrderManager
|
||||
// ✅ CORRECT
|
||||
OrderState newState;
|
||||
lock (_lock)
|
||||
{
|
||||
/// <summary>
|
||||
/// Submit new order for execution
|
||||
/// </summary>
|
||||
/// <param name="request">Order request with all parameters</param>
|
||||
/// <returns>Unique order ID for tracking</returns>
|
||||
/// <exception cref="ArgumentNullException">Request is null</exception>
|
||||
/// <exception cref="ArgumentException">Request validation fails</exception>
|
||||
Task<string> SubmitOrder(OrderRequest request);
|
||||
newState = CalculateNewState();
|
||||
_state = newState;
|
||||
}
|
||||
// Raise AFTER releasing lock
|
||||
RaiseOrderStateChanged(orderId, previousState, newState);
|
||||
```
|
||||
|
||||
### Template
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Brief description of what this does (one line)
|
||||
/// </summary>
|
||||
/// <param name="paramName">What this parameter represents</param>
|
||||
/// <returns>What this method returns</returns>
|
||||
/// <exception cref="ExceptionType">When this exception is thrown</exception>
|
||||
public ReturnType MethodName(Type paramName)
|
||||
{
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## Constructor Pattern
|
||||
## 5. Constructor — Validate All Dependencies
|
||||
|
||||
### ❌ WRONG - No Validation
|
||||
```csharp
|
||||
public BasicOrderManager(ILogger logger, INT8OrderAdapter adapter)
|
||||
{
|
||||
_logger = logger;
|
||||
_adapter = adapter;
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ CORRECT - Validate Dependencies
|
||||
```csharp
|
||||
public BasicOrderManager(ILogger<BasicOrderManager> logger, INT8OrderAdapter adapter)
|
||||
public MyClass(ILogger<MyClass> logger, ISomeDependency dep)
|
||||
{
|
||||
if (logger == null)
|
||||
throw new ArgumentNullException("logger");
|
||||
if (adapter == null)
|
||||
throw new ArgumentNullException("adapter");
|
||||
|
||||
if (dep == null)
|
||||
throw new ArgumentNullException("dep");
|
||||
|
||||
_logger = logger;
|
||||
_adapter = adapter;
|
||||
|
||||
_dep = dep;
|
||||
|
||||
// Initialize collections
|
||||
_activeOrders = new Dictionary<string, OrderStatus>();
|
||||
_completedOrders = new Dictionary<string, OrderStatus>();
|
||||
|
||||
// Register callbacks
|
||||
_adapter.RegisterOrderCallback(OnNT8OrderUpdate);
|
||||
|
||||
_logger.LogInformation("BasicOrderManager initialized");
|
||||
|
||||
_logger.LogInformation("MyClass initialized");
|
||||
}
|
||||
```
|
||||
|
||||
## Event Raising Pattern
|
||||
---
|
||||
|
||||
NEVER raise events inside locks (prevents deadlocks).
|
||||
## 6. XML Documentation — Required on All Public Members
|
||||
|
||||
### ❌ WRONG - Event Inside Lock
|
||||
```csharp
|
||||
lock (_lock)
|
||||
/// <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)
|
||||
{
|
||||
order.State = newState;
|
||||
OrderStateChanged?.Invoke(this, eventArgs); // DEADLOCK RISK!
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ CORRECT - Event Outside Lock
|
||||
---
|
||||
|
||||
## 7. NT8-Specific Patterns (NinjaScript)
|
||||
|
||||
When writing code that runs inside NinjaTrader (in `NT8.Adapters/`):
|
||||
|
||||
```csharp
|
||||
OrderState previousState;
|
||||
OrderState newState;
|
||||
|
||||
lock (_lock)
|
||||
// Always guard OnBarUpdate
|
||||
protected override void OnBarUpdate()
|
||||
{
|
||||
previousState = order.State;
|
||||
order.State = newState;
|
||||
// Update state inside lock
|
||||
if (BarsInProgress != 0) return;
|
||||
if (CurrentBar < BarsRequiredToTrade) return;
|
||||
// ...
|
||||
}
|
||||
|
||||
// Raise event OUTSIDE lock
|
||||
RaiseOrderStateChanged(orderId, previousState, newState, reason);
|
||||
// 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));
|
||||
```
|
||||
|
||||
## Verification Checklist
|
||||
---
|
||||
|
||||
Before completing ANY method, verify:
|
||||
- [ ] Parameter validation (ArgumentNullException/ArgumentException)
|
||||
- [ ] Try-catch on operation
|
||||
- [ ] Logging at appropriate level
|
||||
- [ ] Lock around shared state access
|
||||
- [ ] Events raised outside locks
|
||||
- [ ] XML documentation on public members
|
||||
- [ ] C# 5.0 syntax only (no $, ?., =>, etc.)
|
||||
## 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
|
||||
|
||||
Reference in New Issue
Block a user