20 KiB
20 KiB
AI Agent Workflow and Code Templates
Pre-Implementation Checklist
Before Starting ANY NT8 Integration Task:
-
Environment Verification
cd C:\dev\nt8-sdk .\verify-build.bat # MUST output: "✅ All checks passed!" -
Documentation Review
- Read
AI_DEVELOPMENT_GUIDELINES.md - Read
NT8_INTEGRATION_GUIDELINES.md - Review task-specific requirements
- Check dependency tasks are complete
- Read
-
Pattern Study
- Review existing code in
src/NT8.Core/ - Study similar implementations in the repository
- Understand data flow and architecture
- Review existing code in
Code Templates
Template 1: NT8 Strategy Wrapper
using System;
using System.Collections.Generic;
using NinjaTrader.Cbi;
using NinjaTrader.NinjaScript.Strategies;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
using NT8.Core.Logging;
namespace NinjaTrader.NinjaScript.Strategies
{
/// <summary>
/// NT8 wrapper for [StrategyName] SDK strategy
/// Bridges NinjaTrader 8 with institutional SDK framework
/// </summary>
public class [StrategyName]NT8Wrapper : Strategy
{
#region SDK Components
private IStrategy _sdkStrategy;
private IRiskManager _riskManager;
private IPositionSizer _positionSizer;
private ILogger _logger;
#endregion
#region NT8 Parameters (Show in UI)
[NinjaScriptProperty]
[Range(1, 50)]
[Display(Name = "Stop Loss Ticks", Description = "Stop loss in ticks", Order = 1, GroupName = "Risk")]
public int StopTicks { get; set; } = 8;
[NinjaScriptProperty]
[Range(1, 100)]
[Display(Name = "Profit Target Ticks", Description = "Profit target in ticks", Order = 2, GroupName = "Risk")]
public int TargetTicks { get; set; } = 16;
[NinjaScriptProperty]
[Range(100, 5000)]
[Display(Name = "Daily Loss Limit", Description = "Maximum daily loss in dollars", Order = 3, GroupName = "Risk")]
public double DailyLossLimit { get; set; } = 1000;
[NinjaScriptProperty]
[Range(50, 1000)]
[Display(Name = "Risk Per Trade", Description = "Dollar risk per trade", Order = 4, GroupName = "Sizing")]
public double RiskPerTrade { get; set; } = 200;
#endregion
#region NT8 Lifecycle
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
InitializeNT8Properties();
InitializeSdkComponents();
}
else if (State == State.DataLoaded)
{
ValidateConfiguration();
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < 1) return;
try
{
// Convert NT8 data to SDK format
var barData = ConvertToSdkBar();
var context = CreateStrategyContext();
// Get trading intent from SDK strategy
var intent = _sdkStrategy.OnBar(barData, context);
if (intent == null) return;
// Risk validation
var riskDecision = ValidateRisk(intent, context);
if (!riskDecision.Allow)
{
_logger.LogWarning("Trade rejected: {0}", riskDecision.RejectReason);
return;
}
// Position sizing
var sizingResult = CalculatePositionSize(intent, context);
if (sizingResult.Contracts <= 0)
{
_logger.LogWarning("No contracts calculated");
return;
}
// Execute in NT8
ExecuteTradeInNT8(intent, sizingResult);
}
catch (Exception ex)
{
_logger.LogError("OnBarUpdate error: {0}", ex.Message);
HandleError(ex);
}
}
#endregion
#region Initialization
private void InitializeNT8Properties()
{
Description = "[StrategyName] using NT8 SDK";
Name = "[StrategyName]NT8";
Calculate = Calculate.OnBarClose;
EntriesPerDirection = 1;
EntryHandling = EntryHandling.AllEntries;
IsExitOnSessionCloseStrategy = true;
ExitOnSessionCloseSeconds = 30;
BarsRequiredToTrade = 20;
StartBehavior = StartBehavior.WaitUntilFlat;
TimeInForce = TimeInForce.Gtc;
}
private void InitializeSdkComponents()
{
try
{
_logger = new BasicLogger(Name);
_riskManager = new BasicRiskManager(_logger);
_positionSizer = new BasicPositionSizer(_logger);
// Initialize specific strategy
_sdkStrategy = new [ConcreteStrategyClass](_logger);
var config = CreateSdkConfiguration();
_sdkStrategy.Initialize(config, null, _logger);
_logger.LogInformation("SDK components initialized successfully");
}
catch (Exception ex)
{
throw new InvalidOperationException(String.Format("Failed to initialize SDK: {0}", ex.Message), ex);
}
}
#endregion
#region Data Conversion
private BarData ConvertToSdkBar()
{
return new BarData(
symbol: Instrument.MasterInstrument.Name,
time: Time[0],
open: Open[0],
high: High[0],
low: Low[0],
close: Close[0],
volume: Volume[0],
barSize: TimeSpan.FromMinutes(BarsPeriod.Value)
);
}
private StrategyContext CreateStrategyContext()
{
var position = new Position(
symbol: Instrument.MasterInstrument.Name,
quantity: Position.Quantity,
averagePrice: Position.AveragePrice,
unrealizedPnL: Position.GetUnrealizedProfitLoss(PerformanceUnit.Currency),
realizedPnL: SystemPerformance.AllTrades.TradesPerformance.Currency.CumProfit,
lastUpdate: DateTime.UtcNow
);
var account = new AccountInfo(
equity: Account.Get(AccountItem.CashValue, Currency.UsDollar),
buyingPower: Account.Get(AccountItem.BuyingPower, Currency.UsDollar),
dailyPnL: SystemPerformance.AllTrades.TradesPerformance.Currency.CumProfit,
maxDrawdown: SystemPerformance.AllTrades.TradesPerformance.Currency.MaxDrawdown,
lastUpdate: DateTime.UtcNow
);
var session = new MarketSession(
sessionStart: SessionIterator.GetTradingDay().Date.Add(TimeSpan.FromHours(9.5)),
sessionEnd: SessionIterator.GetTradingDay().Date.Add(TimeSpan.FromHours(16)),
isRth: true,
sessionName: "RTH"
);
return new StrategyContext(
symbol: Instrument.MasterInstrument.Name,
currentTime: DateTime.UtcNow,
currentPosition: position,
account: account,
session: session,
customData: new Dictionary<string, object>()
);
}
#endregion
#region SDK Integration
private StrategyConfig CreateSdkConfiguration()
{
var parameters = new Dictionary<string, object>();
parameters.Add("StopTicks", StopTicks);
parameters.Add("TargetTicks", TargetTicks);
// Add strategy-specific parameters here
var riskConfig = new RiskConfig(
dailyLossLimit: DailyLossLimit,
maxTradeRisk: RiskPerTrade,
maxOpenPositions: 3,
emergencyFlattenEnabled: true
);
var sizingConfig = new SizingConfig(
method: SizingMethod.FixedDollarRisk,
minContracts: 1,
maxContracts: 10,
riskPerTrade: RiskPerTrade,
methodParameters: new Dictionary<string, object>()
);
return new StrategyConfig(
name: Name,
symbol: Instrument.MasterInstrument.Name,
parameters: parameters,
riskSettings: riskConfig,
sizingSettings: sizingConfig
);
}
private RiskDecision ValidateRisk(StrategyIntent intent, StrategyContext context)
{
var riskConfig = new RiskConfig(
dailyLossLimit: DailyLossLimit,
maxTradeRisk: RiskPerTrade,
maxOpenPositions: 3,
emergencyFlattenEnabled: true
);
return _riskManager.ValidateOrder(intent, context, riskConfig);
}
private SizingResult CalculatePositionSize(StrategyIntent intent, StrategyContext context)
{
var sizingConfig = new SizingConfig(
method: SizingMethod.FixedDollarRisk,
minContracts: 1,
maxContracts: 10,
riskPerTrade: RiskPerTrade,
methodParameters: new Dictionary<string, object>()
);
return _positionSizer.CalculateSize(intent, context, sizingConfig);
}
#endregion
#region NT8 Order Execution
private void ExecuteTradeInNT8(StrategyIntent intent, SizingResult sizing)
{
try
{
if (intent.Side == OrderSide.Buy)
{
EnterLong(sizing.Contracts, "SDK_Long");
SetStopLoss("SDK_Long", CalculationMode.Ticks, intent.StopTicks);
if (intent.TargetTicks.HasValue)
SetProfitTarget("SDK_Long", CalculationMode.Ticks, intent.TargetTicks.Value);
}
else if (intent.Side == OrderSide.Sell)
{
EnterShort(sizing.Contracts, "SDK_Short");
SetStopLoss("SDK_Short", CalculationMode.Ticks, intent.StopTicks);
if (intent.TargetTicks.HasValue)
SetProfitTarget("SDK_Short", CalculationMode.Ticks, intent.TargetTicks.Value);
}
_logger.LogInformation("Trade executed: {0} {1} contracts, Stop: {2}, Target: {3}",
intent.Side, sizing.Contracts, intent.StopTicks, intent.TargetTicks);
}
catch (Exception ex)
{
_logger.LogError("Trade execution failed: {0}", ex.Message);
throw;
}
}
#endregion
#region Error Handling
private void ValidateConfiguration()
{
if (StopTicks <= 0)
throw new ArgumentException("Stop ticks must be greater than 0");
if (TargetTicks <= 0)
throw new ArgumentException("Target ticks must be greater than 0");
if (DailyLossLimit <= 0)
throw new ArgumentException("Daily loss limit must be greater than 0");
if (RiskPerTrade <= 0)
throw new ArgumentException("Risk per trade must be greater than 0");
}
private void HandleError(Exception ex)
{
// Log error and optionally halt strategy
_logger.LogCritical("Critical error in strategy: {0}", ex.Message);
// Consider emergency flatten if needed
// _riskManager.EmergencyFlatten("Critical error occurred");
}
#endregion
}
}
Template 2: Data Converter Class
using System;
using System.Collections.Generic;
using NT8.Core.Common.Models;
using NT8.Core.Logging;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Converts between NinjaTrader 8 data formats and SDK data models
/// Handles all data type conversions with proper error handling
/// </summary>
public static class NT8DataConverter
{
/// <summary>
/// Convert NT8 bar data to SDK BarData format
/// </summary>
public static BarData ConvertBar(/* NT8 bar parameters */)
{
try
{
return new BarData(
symbol: /* NT8 symbol */,
time: /* NT8 time */,
open: /* NT8 open */,
high: /* NT8 high */,
low: /* NT8 low */,
close: /* NT8 close */,
volume: /* NT8 volume */,
barSize: /* NT8 bar size */
);
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format("Failed to convert NT8 bar data: {0}", ex.Message), ex);
}
}
/// <summary>
/// Convert NT8 account info to SDK AccountInfo format
/// </summary>
public static AccountInfo ConvertAccount(/* NT8 account parameters */)
{
try
{
return new AccountInfo(
equity: /* NT8 equity */,
buyingPower: /* NT8 buying power */,
dailyPnL: /* NT8 daily PnL */,
maxDrawdown: /* NT8 max drawdown */,
lastUpdate: DateTime.UtcNow
);
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format("Failed to convert NT8 account data: {0}", ex.Message), ex);
}
}
/// <summary>
/// Convert NT8 position to SDK Position format
/// </summary>
public static Position ConvertPosition(/* NT8 position parameters */)
{
try
{
return new Position(
symbol: /* NT8 symbol */,
quantity: /* NT8 quantity */,
averagePrice: /* NT8 average price */,
unrealizedPnL: /* NT8 unrealized PnL */,
realizedPnL: /* NT8 realized PnL */,
lastUpdate: DateTime.UtcNow
);
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format("Failed to convert NT8 position data: {0}", ex.Message), ex);
}
}
/// <summary>
/// Validate data before conversion
/// </summary>
private static void ValidateData(object data, string dataType)
{
if (data == null)
throw new ArgumentNullException("data", String.Format("{0} data cannot be null", dataType));
}
}
}
Template 3: Unit Test Class
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Adapters.NinjaTrader;
using NT8.Core.Common.Models;
using System;
namespace NT8.Integration.Tests.Adapters
{
[TestClass]
public class NT8DataConverterTests
{
[TestMethod]
public void ConvertBar_ValidData_ShouldSucceed()
{
// Arrange
// Create test NT8 bar data
// Act
var result = NT8DataConverter.ConvertBar(/* test data */);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual("ES", result.Symbol);
// Add more specific assertions
}
[TestMethod]
public void ConvertBar_NullData_ShouldThrowException()
{
// Act & Assert
Assert.ThrowsException<ArgumentNullException>(() =>
NT8DataConverter.ConvertBar(null));
}
[TestMethod]
public void ConvertAccount_ValidData_ShouldSucceed()
{
// Arrange
// Create test NT8 account data
// Act
var result = NT8DataConverter.ConvertAccount(/* test data */);
// Assert
Assert.IsNotNull(result);
Assert.IsTrue(result.Equity > 0);
// Add more specific assertions
}
}
}
Implementation Workflow
Step-by-Step Process for Each Task:
Phase 1: Setup and Planning
-
Read Task Requirements
- Review specific task in task breakdown
- Understand deliverables and success criteria
- Check dependencies on other tasks
-
Study Existing Code
# Look at similar implementations find src/NT8.Core -name "*.cs" | xargs grep -l "pattern" -
Create File Structure
# Create required directories if needed mkdir -p src/NT8.Adapters/NinjaTrader mkdir -p src/NT8.Adapters/Wrappers
Phase 2: Implementation
-
Start with Template
- Copy appropriate template from above
- Replace placeholders with actual implementation
- Follow C# 5.0 syntax requirements
-
Implement Core Functionality
- Focus on main requirements first
- Add error handling as you go
- Use existing SDK patterns
-
Test Early and Often
# Test compilation frequently .\verify-build.bat
Phase 3: Integration Testing
-
Unit Tests
- Create comprehensive unit tests
- Test edge cases and error conditions
- Ensure >80% code coverage
-
NT8 Integration
- Copy files to NT8 directories
- Compile in NT8 NinjaScript Editor
- Test on simulation account
Phase 4: Finalization
-
Code Review
- Use
CODE_REVIEW_CHECKLIST.md - Ensure all requirements met
- Verify coding standards compliance
- Use
-
Documentation
- Update relevant documentation
- Add code comments
- Update deployment instructions
Common Patterns and Best Practices
Error Handling Pattern
try
{
// Main logic here
}
catch (SpecificException ex)
{
_logger.LogError("Specific error: {0}", ex.Message);
// Handle specific case
throw; // Re-throw if needed
}
catch (Exception ex)
{
_logger.LogError("Unexpected error in {0}: {1}", methodName, ex.Message);
throw new InvalidOperationException(
String.Format("Operation failed: {0}", ex.Message), ex);
}
Parameter Validation Pattern
public ReturnType MethodName(Type parameter)
{
if (parameter == null)
throw new ArgumentNullException("parameter");
if (!IsValid(parameter))
throw new ArgumentException("Invalid parameter value", "parameter");
// Implementation
}
Logging Pattern
_logger.LogDebug("Method {0} called with parameter: {1}", methodName, parameter);
_logger.LogInformation("Operation completed successfully: {0}", result);
_logger.LogWarning("Potential issue detected: {0}", issue);
_logger.LogError("Error occurred: {0}", error.Message);
_logger.LogCritical("Critical failure: {0}", criticalError.Message);
Quality Checkpoints
Before Submitting Code:
- Compiles without errors (
.\verify-build.bat) - Follows C# 5.0 syntax requirements
- Includes comprehensive error handling
- Has unit tests with >80% coverage
- Follows established code patterns
- Includes proper documentation
- Tested with NT8 (for wrapper classes)
Code Review Criteria:
- Meets all task requirements
- Proper integration with existing SDK
- No breaking changes to Core
- Performance acceptable
- Maintainable and readable code
This workflow and templates provide AI agents with clear guidance and proven patterns for implementing NT8 integration while maintaining the quality and compatibility standards of the SDK.