1135 lines
32 KiB
Markdown
1135 lines
32 KiB
Markdown
# Phase C: Deployment Automation & Integration Testing - Detailed Specification
|
|
|
|
**For:** Kilocode AI Agent (Autonomous Implementation)
|
|
**Phase:** Phase C - Deployment & Testing
|
|
**Components:** Deployment Script + Integration Tests
|
|
**Estimated Time:** 3-4 hours
|
|
**Mode:** Code Mode
|
|
**Dependencies:** Phases A & B must be complete
|
|
|
|
---
|
|
|
|
## 🎯 Objective
|
|
|
|
Create automated deployment tooling and comprehensive integration tests to validate the complete NT8 SDK integration. This phase ensures our SDK can be reliably deployed to NinjaTrader 8 and runs correctly end-to-end.
|
|
|
|
---
|
|
|
|
## 📋 Component 1: Deployment Automation Script
|
|
|
|
### Overview
|
|
PowerShell script that automates the complete deployment process from SDK build to NT8 strategy compilation.
|
|
|
|
### Location
|
|
**Create:** `deployment/Deploy-To-NT8.ps1`
|
|
|
|
### Full Implementation
|
|
|
|
```powershell
|
|
<#
|
|
.SYNOPSIS
|
|
Automates deployment of NT8 SDK to NinjaTrader 8.
|
|
|
|
.DESCRIPTION
|
|
This script:
|
|
1. Builds SDK in Release mode
|
|
2. Runs all unit tests
|
|
3. Copies DLLs to NT8 Custom directory
|
|
4. Copies strategy files to NT8 Strategies directory
|
|
5. Verifies deployment
|
|
|
|
.PARAMETER BuildFirst
|
|
Build SDK before deploying (default: true)
|
|
|
|
.PARAMETER RunTests
|
|
Run tests before deploying (default: true)
|
|
|
|
.PARAMETER CopyStrategies
|
|
Copy strategy .cs files (default: true)
|
|
|
|
.PARAMETER SkipVerification
|
|
Skip deployment verification (default: false)
|
|
|
|
.EXAMPLE
|
|
.\Deploy-To-NT8.ps1
|
|
Full deployment with build, tests, and verification
|
|
|
|
.EXAMPLE
|
|
.\Deploy-To-NT8.ps1 -BuildFirst:$false -RunTests:$false
|
|
Deploy without building or testing (fast deployment)
|
|
|
|
.NOTES
|
|
Requires:
|
|
- .NET Framework 4.8 SDK
|
|
- NinjaTrader 8 installed
|
|
- PowerShell 5.1 or higher
|
|
#>
|
|
|
|
param(
|
|
[switch]$BuildFirst = $true,
|
|
[switch]$RunTests = $true,
|
|
[switch]$CopyStrategies = $true,
|
|
[switch]$SkipVerification = $false,
|
|
[string]$Configuration = "Release"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
#region Configuration
|
|
|
|
$sdkRoot = "C:\dev\nt8-sdk"
|
|
$nt8Custom = "$env:USERPROFILE\Documents\NinjaTrader 8\bin\Custom"
|
|
$nt8Strategies = "$nt8Custom\Strategies"
|
|
|
|
# Source paths
|
|
$coreDllPath = "$sdkRoot\src\NT8.Core\bin\$Configuration\net48"
|
|
$adaptersDllPath = "$sdkRoot\src\NT8.Adapters\bin\$Configuration\net48"
|
|
$strategiesPath = "$sdkRoot\src\NT8.Adapters\Strategies"
|
|
|
|
# Verify paths exist
|
|
if (-not (Test-Path $sdkRoot)) {
|
|
Write-Error "SDK root not found: $sdkRoot"
|
|
exit 1
|
|
}
|
|
|
|
if (-not (Test-Path $nt8Custom)) {
|
|
Write-Error "NinjaTrader 8 Custom directory not found: $nt8Custom"
|
|
Write-Host "Please verify NinjaTrader 8 is installed" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper Functions
|
|
|
|
function Write-Header {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host $Message -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
}
|
|
|
|
function Write-Step {
|
|
param(
|
|
[string]$Step,
|
|
[string]$Message
|
|
)
|
|
Write-Host "`n[$Step] $Message" -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-Success {
|
|
param([string]$Message)
|
|
Write-Host " ✓ $Message" -ForegroundColor Green
|
|
}
|
|
|
|
function Write-Warning {
|
|
param([string]$Message)
|
|
Write-Host " ⚠ $Message" -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-Failure {
|
|
param([string]$Message)
|
|
Write-Host " ✗ $Message" -ForegroundColor Red
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Main Deployment
|
|
|
|
Write-Header "NT8 SDK Deployment Script"
|
|
Write-Host "Configuration: $Configuration" -ForegroundColor White
|
|
Write-Host "SDK Root: $sdkRoot" -ForegroundColor White
|
|
Write-Host "NT8 Custom: $nt8Custom" -ForegroundColor White
|
|
Write-Host ""
|
|
|
|
$startTime = Get-Date
|
|
|
|
#region Step 1: Build SDK
|
|
|
|
if ($BuildFirst) {
|
|
Write-Step "1/6" "Building SDK..."
|
|
|
|
Push-Location $sdkRoot
|
|
|
|
try {
|
|
# Clean previous builds
|
|
Write-Host " Cleaning previous builds..."
|
|
& dotnet clean --configuration $Configuration --verbosity quiet
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Failure "Clean failed"
|
|
Pop-Location
|
|
exit 1
|
|
}
|
|
|
|
# Build
|
|
Write-Host " Building SDK..."
|
|
& dotnet build --configuration $Configuration --verbosity quiet
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Failure "Build failed"
|
|
Pop-Location
|
|
exit 1
|
|
}
|
|
|
|
Write-Success "Build succeeded"
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
else {
|
|
Write-Step "1/6" "Skipping build (BuildFirst = false)"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Step 2: Run Tests
|
|
|
|
if ($RunTests) {
|
|
Write-Step "2/6" "Running tests..."
|
|
|
|
Push-Location $sdkRoot
|
|
|
|
try {
|
|
Write-Host " Executing test suite..."
|
|
$testOutput = & dotnet test --configuration $Configuration --no-build --verbosity quiet
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Failure "Tests failed"
|
|
Write-Host $testOutput
|
|
Pop-Location
|
|
exit 1
|
|
}
|
|
|
|
# Parse test results
|
|
$passedMatch = $testOutput | Select-String "Passed!\s+-\s+Failed:\s+(\d+),\s+Passed:\s+(\d+)"
|
|
|
|
if ($passedMatch) {
|
|
$failed = $passedMatch.Matches[0].Groups[1].Value
|
|
$passed = $passedMatch.Matches[0].Groups[2].Value
|
|
|
|
if ($failed -eq "0") {
|
|
Write-Success "All tests passed ($passed tests)"
|
|
}
|
|
else {
|
|
Write-Failure "$failed tests failed, $passed passed"
|
|
Pop-Location
|
|
exit 1
|
|
}
|
|
}
|
|
else {
|
|
Write-Success "Tests completed"
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
else {
|
|
Write-Step "2/6" "Skipping tests (RunTests = false)"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Step 3: Copy SDK DLLs
|
|
|
|
Write-Step "3/6" "Copying SDK DLLs..."
|
|
|
|
# Core DLL
|
|
if (Test-Path "$coreDllPath\NT8.Core.dll") {
|
|
Copy-Item "$coreDllPath\NT8.Core.dll" $nt8Custom -Force
|
|
Write-Success "Copied NT8.Core.dll"
|
|
}
|
|
else {
|
|
Write-Failure "NT8.Core.dll not found"
|
|
exit 1
|
|
}
|
|
|
|
# Core PDB (for debugging)
|
|
if (Test-Path "$coreDllPath\NT8.Core.pdb") {
|
|
Copy-Item "$coreDllPath\NT8.Core.pdb" $nt8Custom -Force
|
|
Write-Success "Copied NT8.Core.pdb"
|
|
}
|
|
|
|
# Adapters DLL (if it exists - Phase A/B might not build it)
|
|
if (Test-Path "$adaptersDllPath\NT8.Adapters.dll") {
|
|
Copy-Item "$adaptersDllPath\NT8.Adapters.dll" $nt8Custom -Force
|
|
Write-Success "Copied NT8.Adapters.dll"
|
|
|
|
if (Test-Path "$adaptersDllPath\NT8.Adapters.pdb") {
|
|
Copy-Item "$adaptersDllPath\NT8.Adapters.pdb" $nt8Custom -Force
|
|
Write-Success "Copied NT8.Adapters.pdb"
|
|
}
|
|
}
|
|
else {
|
|
Write-Warning "NT8.Adapters.dll not found (expected if not built as DLL)"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Step 4: Copy Dependencies
|
|
|
|
Write-Step "4/6" "Copying dependencies..."
|
|
|
|
$dependencies = @(
|
|
"Microsoft.Extensions.*.dll",
|
|
"System.Memory.dll",
|
|
"System.Buffers.dll",
|
|
"System.Runtime.CompilerServices.Unsafe.dll"
|
|
)
|
|
|
|
$copiedCount = 0
|
|
|
|
foreach ($pattern in $dependencies) {
|
|
$files = Get-ChildItem "$coreDllPath\$pattern" -ErrorAction SilentlyContinue
|
|
|
|
foreach ($file in $files) {
|
|
Copy-Item $file.FullName $nt8Custom -Force
|
|
Write-Success "Copied $($file.Name)"
|
|
$copiedCount++
|
|
}
|
|
}
|
|
|
|
if ($copiedCount -eq 0) {
|
|
Write-Warning "No dependencies found (this may be normal)"
|
|
}
|
|
else {
|
|
Write-Success "Copied $copiedCount dependency files"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Step 5: Copy Strategy Files
|
|
|
|
if ($CopyStrategies) {
|
|
Write-Step "5/6" "Copying strategy files..."
|
|
|
|
# Ensure strategies directory exists
|
|
if (-not (Test-Path $nt8Strategies)) {
|
|
New-Item -ItemType Directory -Path $nt8Strategies -Force | Out-Null
|
|
Write-Success "Created Strategies directory"
|
|
}
|
|
|
|
$strategyFiles = @(
|
|
"NT8StrategyBase.cs",
|
|
"SimpleORBNT8.cs",
|
|
"MinimalTestStrategy.cs"
|
|
)
|
|
|
|
$copiedStrategies = 0
|
|
|
|
foreach ($file in $strategyFiles) {
|
|
$sourcePath = Join-Path $strategiesPath $file
|
|
|
|
if (Test-Path $sourcePath) {
|
|
Copy-Item $sourcePath $nt8Strategies -Force
|
|
Write-Success "Copied $file"
|
|
$copiedStrategies++
|
|
}
|
|
else {
|
|
Write-Warning "$file not found (skipping)"
|
|
}
|
|
}
|
|
|
|
if ($copiedStrategies -eq 0) {
|
|
Write-Failure "No strategy files copied"
|
|
exit 1
|
|
}
|
|
}
|
|
else {
|
|
Write-Step "5/6" "Skipping strategy files (CopyStrategies = false)"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Step 6: Verify Deployment
|
|
|
|
if (-not $SkipVerification) {
|
|
Write-Step "6/6" "Verifying deployment..."
|
|
|
|
$verificationPassed = $true
|
|
|
|
# Check Core DLL
|
|
if (Test-Path "$nt8Custom\NT8.Core.dll") {
|
|
$coreInfo = Get-Item "$nt8Custom\NT8.Core.dll"
|
|
$coreVersion = $coreInfo.VersionInfo.FileVersion
|
|
Write-Success "NT8.Core.dll present (v$coreVersion)"
|
|
}
|
|
else {
|
|
Write-Failure "NT8.Core.dll missing"
|
|
$verificationPassed = $false
|
|
}
|
|
|
|
# Check strategy files
|
|
foreach ($file in $strategyFiles) {
|
|
$targetPath = Join-Path $nt8Strategies $file
|
|
|
|
if (Test-Path $targetPath) {
|
|
$fileInfo = Get-Item $targetPath
|
|
$fileSizeKB = [math]::Round($fileInfo.Length / 1KB, 2)
|
|
Write-Success "$file present ($fileSizeKB KB)"
|
|
}
|
|
else {
|
|
Write-Failure "$file missing"
|
|
$verificationPassed = $false
|
|
}
|
|
}
|
|
|
|
if (-not $verificationPassed) {
|
|
Write-Host ""
|
|
Write-Failure "Deployment verification failed"
|
|
exit 1
|
|
}
|
|
}
|
|
else {
|
|
Write-Step "6/6" "Skipping verification (SkipVerification = true)"
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Completion
|
|
|
|
$endTime = Get-Date
|
|
$duration = $endTime - $startTime
|
|
|
|
Write-Header "Deployment Complete!"
|
|
|
|
Write-Host ""
|
|
Write-Host "Summary:" -ForegroundColor Cyan
|
|
Write-Host " Duration: $($duration.TotalSeconds.ToString('F1')) seconds"
|
|
Write-Host " SDK DLLs: Copied to $nt8Custom"
|
|
Write-Host " Strategies: Copied to $nt8Strategies"
|
|
Write-Host ""
|
|
|
|
Write-Host "Next Steps:" -ForegroundColor Yellow
|
|
Write-Host " 1. Open NinjaTrader 8"
|
|
Write-Host " 2. Tools → NinjaScript Editor (F5)"
|
|
Write-Host " 3. Compile → Compile All (F5)"
|
|
Write-Host " 4. Verify compilation succeeds"
|
|
Write-Host " 5. Create strategy instance on chart"
|
|
Write-Host " 6. Test in simulation before live trading"
|
|
Write-Host ""
|
|
|
|
Write-Host "Strategy Files Deployed:" -ForegroundColor Cyan
|
|
foreach ($file in $strategyFiles) {
|
|
$targetPath = Join-Path $nt8Strategies $file
|
|
if (Test-Path $targetPath) {
|
|
Write-Host " ✓ $file"
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Success "Deployment succeeded!"
|
|
|
|
#endregion
|
|
|
|
exit 0
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Component 2: Integration Test Suite
|
|
|
|
### Overview
|
|
Comprehensive integration tests that validate end-to-end functionality.
|
|
|
|
### Location
|
|
**Create:** `tests/NT8.Integration.Tests/NT8IntegrationTests.cs`
|
|
|
|
### Full Implementation
|
|
|
|
```csharp
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Xunit;
|
|
using FluentAssertions;
|
|
using NT8.Core.Common.Interfaces;
|
|
using NT8.Core.Common.Models;
|
|
using NT8.Core.Risk;
|
|
using NT8.Core.Sizing;
|
|
using NT8.Core.Logging;
|
|
using NT8.Adapters.NinjaTrader;
|
|
using NT8.Strategies.Examples;
|
|
|
|
namespace NT8.Integration.Tests
|
|
{
|
|
/// <summary>
|
|
/// Integration tests for NT8 SDK end-to-end workflows.
|
|
/// Tests complete flow: Strategy → Risk → Sizing → OMS → Adapter
|
|
/// </summary>
|
|
public class NT8IntegrationTests
|
|
{
|
|
#region Helper Methods
|
|
|
|
private StrategyContext CreateTestContext(
|
|
string symbol = "ES",
|
|
int positionQuantity = 0,
|
|
double equity = 100000.0,
|
|
double dailyPnL = 0.0)
|
|
{
|
|
var currentTime = new DateTime(2026, 2, 17, 10, 30, 0);
|
|
|
|
var position = new Position(
|
|
symbol, positionQuantity, 0, 0, dailyPnL, currentTime);
|
|
|
|
var account = new AccountInfo(
|
|
equity, equity * 2.5, dailyPnL, 0, currentTime);
|
|
|
|
var session = new MarketSession(
|
|
currentTime.Date.AddHours(9).AddMinutes(30),
|
|
currentTime.Date.AddHours(16),
|
|
true,
|
|
"RTH");
|
|
|
|
var customData = new Dictionary<string, object>();
|
|
|
|
return new StrategyContext(
|
|
symbol, currentTime, position, account, session, customData);
|
|
}
|
|
|
|
private BarData CreateTestBar(
|
|
string symbol = "ES",
|
|
double open = 4200.0,
|
|
double high = 4210.0,
|
|
double low = 4195.0,
|
|
double close = 4208.0)
|
|
{
|
|
return new BarData(
|
|
symbol,
|
|
new DateTime(2026, 2, 17, 10, 30, 0),
|
|
open, high, low, close,
|
|
10000,
|
|
TimeSpan.FromMinutes(5));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region End-to-End Workflow Tests
|
|
|
|
[Fact]
|
|
public void CompleteWorkflow_StrategyToExecution_ShouldProcessIntent()
|
|
{
|
|
// Arrange
|
|
var logger = new BasicLogger("IntegrationTest");
|
|
var riskManager = new BasicRiskManager(logger);
|
|
var positionSizer = new BasicPositionSizer(logger);
|
|
var executionAdapter = new NT8ExecutionAdapter();
|
|
|
|
var strategy = new SimpleORBStrategy(30, 1.0);
|
|
|
|
var config = new StrategyConfig(
|
|
"TestStrategy",
|
|
"ES",
|
|
new Dictionary<string, object>(),
|
|
new RiskConfig(1000.0, 200.0, 3, true),
|
|
new SizingConfig(SizingMethod.FixedDollarRisk, 1, 10, 100.0,
|
|
new Dictionary<string, object>())
|
|
);
|
|
|
|
strategy.Initialize(config, null, logger);
|
|
|
|
var context = CreateTestContext();
|
|
var bar = CreateTestBar();
|
|
|
|
// Act - Generate intent
|
|
var intent = strategy.OnBar(bar, context);
|
|
|
|
// If intent generated, process through risk/sizing
|
|
if (intent != null)
|
|
{
|
|
// Risk validation
|
|
var riskDecision = riskManager.ValidateOrder(
|
|
intent, context, config.RiskSettings);
|
|
|
|
riskDecision.Should().NotBeNull();
|
|
|
|
if (riskDecision.Approved)
|
|
{
|
|
// Position sizing
|
|
var sizingResult = positionSizer.CalculateSize(
|
|
intent, context, config.SizingSettings);
|
|
|
|
sizingResult.Should().NotBeNull();
|
|
sizingResult.Contracts.Should().BeGreaterOrEqualTo(1);
|
|
sizingResult.Contracts.Should().BeLessOrEqualTo(10);
|
|
|
|
// Order submission (tracked, not executed)
|
|
var orderRequest = new OrderRequest(
|
|
intent.Symbol,
|
|
intent.Side,
|
|
sizingResult.Contracts,
|
|
intent.EntryType,
|
|
intent.LimitPrice,
|
|
intent.StopPrice
|
|
);
|
|
|
|
var orderName = string.Format("TEST_{0}", Guid.NewGuid().ToString("N"));
|
|
var trackingInfo = executionAdapter.SubmitOrder(orderRequest, orderName);
|
|
|
|
trackingInfo.Should().NotBeNull();
|
|
trackingInfo.SdkOrderId.Should().Be(orderName);
|
|
trackingInfo.CurrentState.Should().Be(OrderState.Pending);
|
|
}
|
|
}
|
|
|
|
// Assert - No exceptions thrown
|
|
// Complete workflow executed successfully
|
|
}
|
|
|
|
[Fact]
|
|
public void DataConversion_NT8ToSDK_ShouldPreserveData()
|
|
{
|
|
// Arrange
|
|
var symbol = "ES";
|
|
var time = new DateTime(2026, 2, 17, 10, 0, 0);
|
|
var open = 4200.0;
|
|
var high = 4215.0;
|
|
var low = 4192.0;
|
|
var close = 4210.0;
|
|
var volume = 15000L;
|
|
var barSizeMinutes = 5;
|
|
|
|
// Act
|
|
var barData = NT8DataConverter.ConvertBar(
|
|
symbol, time, open, high, low, close, volume, barSizeMinutes);
|
|
|
|
// Assert
|
|
barData.Symbol.Should().Be(symbol);
|
|
barData.Time.Should().Be(time);
|
|
barData.Open.Should().Be(open);
|
|
barData.High.Should().Be(high);
|
|
barData.Low.Should().Be(low);
|
|
barData.Close.Should().Be(close);
|
|
barData.Volume.Should().Be(volume);
|
|
barData.BarSize.Should().Be(TimeSpan.FromMinutes(barSizeMinutes));
|
|
}
|
|
|
|
[Fact]
|
|
public void ExecutionAdapter_OrderLifecycle_ShouldTrackCorrectly()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8ExecutionAdapter();
|
|
var orderRequest = new OrderRequest(
|
|
"ES", OrderSide.Buy, 2, OrderType.Market, null, null);
|
|
var orderName = "TEST_001";
|
|
|
|
// Act & Assert - Submit
|
|
var trackingInfo = adapter.SubmitOrder(orderRequest, orderName);
|
|
trackingInfo.CurrentState.Should().Be(OrderState.Pending);
|
|
|
|
// Act & Assert - Working
|
|
adapter.ProcessOrderUpdate(
|
|
"NT8_123", orderName, "WORKING", 0, 0, 0, null);
|
|
|
|
var status = adapter.GetOrderStatus(orderName);
|
|
status.State.Should().Be(OrderState.Working);
|
|
|
|
// Act & Assert - Partial Fill
|
|
adapter.ProcessOrderUpdate(
|
|
"NT8_123", orderName, "PARTFILLED", 1, 4200.50, 0, null);
|
|
|
|
adapter.ProcessExecution(
|
|
"NT8_123", "EXEC_001", 4200.50, 1, DateTime.UtcNow);
|
|
|
|
status = adapter.GetOrderStatus(orderName);
|
|
status.State.Should().Be(OrderState.PartiallyFilled);
|
|
status.Filled.Should().Be(1);
|
|
|
|
// Act & Assert - Filled
|
|
adapter.ProcessOrderUpdate(
|
|
"NT8_123", orderName, "FILLED", 2, 4200.75, 0, null);
|
|
|
|
adapter.ProcessExecution(
|
|
"NT8_123", "EXEC_002", 4201.0, 1, DateTime.UtcNow);
|
|
|
|
status = adapter.GetOrderStatus(orderName);
|
|
status.State.Should().Be(OrderState.Filled);
|
|
status.Filled.Should().Be(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void RiskManager_DailyLossLimit_ShouldRejectOverRisk()
|
|
{
|
|
// Arrange
|
|
var logger = new BasicLogger("RiskTest");
|
|
var riskManager = new BasicRiskManager(logger);
|
|
|
|
var intent = new StrategyIntent(
|
|
"ES", OrderSide.Buy, OrderType.Market,
|
|
null, null, 10, 20, ConfidenceLevel.High, "Test");
|
|
|
|
// Context with daily loss near limit
|
|
var context = CreateTestContext(
|
|
dailyPnL: -950.0); // Close to $1000 limit
|
|
|
|
var riskConfig = new RiskConfig(
|
|
dailyLossLimit: 1000.0,
|
|
maxTradeRisk: 200.0,
|
|
maxOpenPositions: 3,
|
|
emergencyFlattenEnabled: true);
|
|
|
|
// Act
|
|
var decision = riskManager.ValidateOrder(intent, context, riskConfig);
|
|
|
|
// Assert
|
|
decision.Should().NotBeNull();
|
|
decision.Approved.Should().BeFalse();
|
|
decision.Reason.Should().Contain("daily loss");
|
|
}
|
|
|
|
[Fact]
|
|
public void PositionSizer_FixedDollarRisk_ShouldCalculateCorrectly()
|
|
{
|
|
// Arrange
|
|
var logger = new BasicLogger("SizingTest");
|
|
var sizer = new BasicPositionSizer(logger);
|
|
|
|
var intent = new StrategyIntent(
|
|
"ES", OrderSide.Buy, OrderType.Market,
|
|
null, null, 8, 16, ConfidenceLevel.High, "Test");
|
|
|
|
var context = CreateTestContext(equity: 100000.0);
|
|
|
|
var sizingConfig = new SizingConfig(
|
|
SizingMethod.FixedDollarRisk,
|
|
minContracts: 1,
|
|
maxContracts: 10,
|
|
riskPerTrade: 100.0,
|
|
methodParameters: new Dictionary<string, object>()
|
|
);
|
|
|
|
// Act
|
|
var result = sizer.CalculateSize(intent, context, sizingConfig);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result.Contracts.Should().BeGreaterOrEqualTo(1);
|
|
result.Contracts.Should().BeLessOrEqualTo(10);
|
|
result.Method.Should().Be(SizingMethod.FixedDollarRisk);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Thread Safety Tests
|
|
|
|
[Fact]
|
|
public void ExecutionAdapter_ConcurrentAccess_ShouldBeThreadSafe()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8ExecutionAdapter();
|
|
var threads = 10;
|
|
var ordersPerThread = 10;
|
|
var exceptions = new List<Exception>();
|
|
var successCount = 0;
|
|
var lockObj = new object();
|
|
|
|
// Act
|
|
var threadList = new List<Thread>();
|
|
|
|
for (int t = 0; t < threads; t++)
|
|
{
|
|
var threadNum = t;
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
for (int i = 0; i < ordersPerThread; i++)
|
|
{
|
|
var orderRequest = new OrderRequest(
|
|
"ES", OrderSide.Buy, 1, OrderType.Market, null, null);
|
|
|
|
var orderName = string.Format("THREAD_{0}_ORDER_{1}", threadNum, i);
|
|
|
|
var tracking = adapter.SubmitOrder(orderRequest, orderName);
|
|
|
|
// Simulate order update
|
|
adapter.ProcessOrderUpdate(
|
|
orderName + "_NT8",
|
|
orderName,
|
|
"WORKING",
|
|
0, 0, 0, null);
|
|
|
|
lock (lockObj)
|
|
{
|
|
successCount++;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lock (lockObj)
|
|
{
|
|
exceptions.Add(ex);
|
|
}
|
|
}
|
|
});
|
|
|
|
threadList.Add(thread);
|
|
thread.Start();
|
|
}
|
|
|
|
// Wait for all threads
|
|
foreach (var thread in threadList)
|
|
{
|
|
thread.Join();
|
|
}
|
|
|
|
// Assert
|
|
exceptions.Should().BeEmpty("No exceptions should occur in thread-safe code");
|
|
successCount.Should().Be(threads * ordersPerThread,
|
|
"All orders should be processed successfully");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Performance Tests
|
|
|
|
[Fact]
|
|
public void PerformanceTest_OnBarUpdate_ShouldComplete200ms()
|
|
{
|
|
// Arrange
|
|
var logger = new BasicLogger("PerfTest");
|
|
var strategy = new SimpleORBStrategy(30, 1.0);
|
|
|
|
var config = new StrategyConfig(
|
|
"PerfTest",
|
|
"ES",
|
|
new Dictionary<string, object>(),
|
|
new RiskConfig(1000.0, 200.0, 3, true),
|
|
new SizingConfig(SizingMethod.FixedDollarRisk, 1, 10, 100.0,
|
|
new Dictionary<string, object>())
|
|
);
|
|
|
|
strategy.Initialize(config, null, logger);
|
|
|
|
var context = CreateTestContext();
|
|
var bar = CreateTestBar();
|
|
|
|
// Warmup
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
strategy.OnBar(bar, context);
|
|
}
|
|
|
|
// Act - Measure 100 iterations
|
|
var iterations = 100;
|
|
var startTime = DateTime.UtcNow;
|
|
|
|
for (int i = 0; i < iterations; i++)
|
|
{
|
|
strategy.OnBar(bar, context);
|
|
}
|
|
|
|
var endTime = DateTime.UtcNow;
|
|
var totalMs = (endTime - startTime).TotalMilliseconds;
|
|
var avgMs = totalMs / iterations;
|
|
|
|
// Assert
|
|
avgMs.Should().BeLessThan(200.0,
|
|
"OnBar should complete in <200ms on average");
|
|
|
|
logger.LogInformation("Performance: {0:F2}ms avg over {1} iterations",
|
|
avgMs, iterations);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Component 3: Deployment Verification Script
|
|
|
|
### Overview
|
|
Lightweight verification script to check deployment status.
|
|
|
|
### Location
|
|
**Create:** `deployment/Verify-Deployment.ps1`
|
|
|
|
### Implementation
|
|
|
|
```powershell
|
|
<#
|
|
.SYNOPSIS
|
|
Verifies NT8 SDK deployment without rebuilding.
|
|
|
|
.DESCRIPTION
|
|
Checks that all required files are in place for NT8 SDK.
|
|
#>
|
|
|
|
param(
|
|
[switch]$Detailed
|
|
)
|
|
|
|
$nt8Custom = "$env:USERPROFILE\Documents\NinjaTrader 8\bin\Custom"
|
|
$nt8Strategies = "$nt8Custom\Strategies"
|
|
|
|
Write-Host "NT8 SDK Deployment Verification" -ForegroundColor Cyan
|
|
Write-Host ("=" * 50)
|
|
Write-Host ""
|
|
|
|
$allGood = $true
|
|
|
|
# Check Custom directory
|
|
Write-Host "Checking Custom directory..." -ForegroundColor Yellow
|
|
|
|
$requiredDlls = @("NT8.Core.dll")
|
|
$optionalDlls = @("NT8.Adapters.dll")
|
|
|
|
foreach ($dll in $requiredDlls) {
|
|
$path = Join-Path $nt8Custom $dll
|
|
if (Test-Path $path) {
|
|
$info = Get-Item $path
|
|
Write-Host " ✓ $dll" -ForegroundColor Green
|
|
|
|
if ($Detailed) {
|
|
Write-Host " Size: $([math]::Round($info.Length/1KB, 2)) KB" -ForegroundColor Gray
|
|
Write-Host " Modified: $($info.LastWriteTime)" -ForegroundColor Gray
|
|
}
|
|
}
|
|
else {
|
|
Write-Host " ✗ $dll (MISSING)" -ForegroundColor Red
|
|
$allGood = $false
|
|
}
|
|
}
|
|
|
|
foreach ($dll in $optionalDlls) {
|
|
$path = Join-Path $nt8Custom $dll
|
|
if (Test-Path $path) {
|
|
Write-Host " ✓ $dll (optional)" -ForegroundColor Green
|
|
}
|
|
else {
|
|
Write-Host " - $dll (optional, not present)" -ForegroundColor Gray
|
|
}
|
|
}
|
|
|
|
# Check Strategies directory
|
|
Write-Host "`nChecking Strategies directory..." -ForegroundColor Yellow
|
|
|
|
$strategyFiles = @(
|
|
"NT8StrategyBase.cs",
|
|
"SimpleORBNT8.cs",
|
|
"MinimalTestStrategy.cs"
|
|
)
|
|
|
|
foreach ($file in $strategyFiles) {
|
|
$path = Join-Path $nt8Strategies $file
|
|
if (Test-Path $path) {
|
|
$info = Get-Item $path
|
|
Write-Host " ✓ $file" -ForegroundColor Green
|
|
|
|
if ($Detailed) {
|
|
Write-Host " Size: $([math]::Round($info.Length/1KB, 2)) KB" -ForegroundColor Gray
|
|
Write-Host " Modified: $($info.LastWriteTime)" -ForegroundColor Gray
|
|
}
|
|
}
|
|
else {
|
|
Write-Host " ✗ $file (MISSING)" -ForegroundColor Red
|
|
$allGood = $false
|
|
}
|
|
}
|
|
|
|
# Final status
|
|
Write-Host ""
|
|
if ($allGood) {
|
|
Write-Host "✓ Deployment verified - All required files present" -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
else {
|
|
Write-Host "✗ Deployment incomplete - Missing required files" -ForegroundColor Red
|
|
Write-Host ""
|
|
Write-Host "Run: .\Deploy-To-NT8.ps1" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Verification & Testing
|
|
|
|
### Deployment Test Plan
|
|
|
|
**Test 1: Fresh Deployment**
|
|
```powershell
|
|
# Clean NT8 directories
|
|
Remove-Item "$env:USERPROFILE\Documents\NinjaTrader 8\bin\Custom\NT8.*.dll"
|
|
Remove-Item "$env:USERPROFILE\Documents\NinjaTrader 8\bin\Custom\Strategies\*NT8*.cs"
|
|
|
|
# Deploy
|
|
.\deployment\Deploy-To-NT8.ps1
|
|
|
|
# Verify
|
|
.\deployment\Verify-Deployment.ps1 -Detailed
|
|
```
|
|
|
|
**Test 2: Incremental Deployment**
|
|
```powershell
|
|
# Make changes to strategy
|
|
# Deploy without building
|
|
.\deployment\Deploy-To-NT8.ps1 -BuildFirst:$false -RunTests:$false
|
|
|
|
# Verify
|
|
.\deployment\Verify-Deployment.ps1
|
|
```
|
|
|
|
**Test 3: Build Verification**
|
|
```powershell
|
|
# Full deployment with verification
|
|
.\deployment\Deploy-To-NT8.ps1
|
|
|
|
# Should complete without errors
|
|
# Should show all tests passing
|
|
# Should verify all files copied
|
|
```
|
|
|
|
### Integration Test Execution
|
|
|
|
```bash
|
|
# Run integration tests
|
|
dotnet test tests/NT8.Integration.Tests --configuration Release
|
|
|
|
# Expected results:
|
|
# - All tests pass
|
|
# - No warnings
|
|
# - Performance tests meet targets
|
|
# - Thread safety validated
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Success Criteria
|
|
|
|
### Must Have (Release Blockers)
|
|
- [ ] Deploy-To-NT8.ps1 completes without errors
|
|
- [ ] All SDK DLLs copy correctly
|
|
- [ ] All strategy files copy correctly
|
|
- [ ] Verify-Deployment.ps1 reports all files present
|
|
- [ ] Integration tests all pass (15+ tests)
|
|
- [ ] Performance test meets <200ms target
|
|
- [ ] Thread safety test passes
|
|
- [ ] Complete workflow test passes
|
|
- [ ] Can deploy from clean state
|
|
- [ ] Can deploy incrementally
|
|
|
|
### Should Have (Quality Targets)
|
|
- [ ] Deployment completes in <30 seconds
|
|
- [ ] Clear progress indicators
|
|
- [ ] Helpful error messages
|
|
- [ ] Verification detailed output
|
|
- [ ] Integration test coverage >80%
|
|
|
|
---
|
|
|
|
## 🚨 Critical Constraints
|
|
|
|
### PowerShell Requirements
|
|
- PowerShell 5.1+ (built into Windows 10+)
|
|
- Execution policy allows scripts
|
|
- If script blocked: `Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned`
|
|
|
|
### Path Requirements
|
|
- SDK must be at `C:\dev\nt8-sdk`
|
|
- NT8 must be installed (default location)
|
|
- User has write access to Documents folder
|
|
|
|
### Build Requirements
|
|
- .NET Framework 4.8 SDK installed
|
|
- All Phase A & B code complete
|
|
- All tests passing
|
|
|
|
---
|
|
|
|
## 🔄 Implementation Workflow
|
|
|
|
### Step 1: Create Deployment Script (2 hours)
|
|
1. Create `deployment/Deploy-To-NT8.ps1`
|
|
2. Implement all deployment steps
|
|
3. Add comprehensive error handling
|
|
4. Test on clean system
|
|
5. Test incremental deployment
|
|
|
|
### Step 2: Create Verification Script (30 min)
|
|
1. Create `deployment/Verify-Deployment.ps1`
|
|
2. Implement file checks
|
|
3. Add detailed output option
|
|
4. Test verification
|
|
|
|
### Step 3: Create Integration Tests (1.5 hours)
|
|
1. Create `tests/NT8.Integration.Tests/NT8IntegrationTests.cs`
|
|
2. Implement all 15+ tests
|
|
3. Run and verify all pass
|
|
4. Check performance targets met
|
|
|
|
### Step 4: Documentation (30 min)
|
|
1. Update README with deployment instructions
|
|
2. Create deployment troubleshooting guide
|
|
3. Document test execution
|
|
|
|
### Step 5: Git Commit
|
|
```bash
|
|
git add deployment/
|
|
git add tests/NT8.Integration.Tests/NT8IntegrationTests.cs
|
|
git commit -m "feat: Add deployment automation and integration tests
|
|
|
|
Deployment:
|
|
- Deploy-To-NT8.ps1: Full automated deployment
|
|
- Verify-Deployment.ps1: Deployment verification
|
|
- Handles build, test, copy, verify
|
|
- Clear progress and error reporting
|
|
|
|
Integration Tests:
|
|
- 15+ end-to-end workflow tests
|
|
- Performance validation (<200ms)
|
|
- Thread safety validation
|
|
- Complete SDK workflow coverage
|
|
|
|
Tested:
|
|
- Fresh deployment successful
|
|
- Incremental deployment successful
|
|
- All integration tests passing
|
|
- Performance targets met
|
|
|
|
Phase C complete: Deployment automation ready"
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Deliverables Checklist
|
|
|
|
- [ ] `deployment/Deploy-To-NT8.ps1` (~300 lines)
|
|
- [ ] `deployment/Verify-Deployment.ps1` (~100 lines)
|
|
- [ ] `tests/NT8.Integration.Tests/NT8IntegrationTests.cs` (~500 lines)
|
|
- [ ] All deployment tests pass
|
|
- [ ] All integration tests pass (15+)
|
|
- [ ] Performance tests meet targets
|
|
- [ ] Documentation updated
|
|
- [ ] Git committed
|
|
|
|
---
|
|
|
|
## 🎯 Success Definition
|
|
|
|
**Phase C is complete when:**
|
|
|
|
1. ✅ Deploy-To-NT8.ps1 works from clean state
|
|
2. ✅ Deploy-To-NT8.ps1 works incrementally
|
|
3. ✅ Verify-Deployment.ps1 validates correctly
|
|
4. ✅ All 15+ integration tests passing
|
|
5. ✅ Performance test <200ms average
|
|
6. ✅ Thread safety test passes with 100 concurrent orders
|
|
7. ✅ Complete workflow test validates all layers
|
|
8. ✅ Documentation complete
|
|
9. ✅ Code committed to Git
|
|
|
|
**Time Target:** 3-4 hours total
|
|
|
|
---
|
|
|
|
**READY FOR KILOCODE EXECUTION IN CODE MODE** ✅
|
|
|
|
**Dependencies:** Phases A & B must be complete before starting Phase C
|