254 lines
8.4 KiB
C#
254 lines
8.4 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using NT8.Adapters.NinjaTrader;
|
|
using NT8.Core.Common.Models;
|
|
using NT8.Core.Risk;
|
|
using NT8.Core.Sizing;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace NT8.Integration.Tests
|
|
{
|
|
/// <summary>
|
|
/// Integration tests for NT8OrderAdapter behavior.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class NT8OrderAdapterIntegrationTests
|
|
{
|
|
private class FakeBridge : INT8ExecutionBridge
|
|
{
|
|
public void EnterLongManaged(int q, string n, int s, int t, double ts) { }
|
|
public void EnterShortManaged(int q, string n, int s, int t, double ts) { }
|
|
public void ExitLongManaged(string n) { }
|
|
public void ExitShortManaged(string n) { }
|
|
public void FlattenAll() { }
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Initialize_NullRiskManager_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
var sizer = new TestPositionSizer(1);
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<ArgumentNullException>(
|
|
() => adapter.Initialize(null, sizer));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Initialize_NullPositionSizer_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
var risk = new TestRiskManager(true);
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<ArgumentNullException>(
|
|
() => adapter.Initialize(risk, null));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ExecuteIntent_NotInitialized_ThrowsInvalidOperationException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<InvalidOperationException>(
|
|
() => adapter.ExecuteIntent(CreateIntent(), CreateContext(), CreateConfig()));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ExecuteIntent_RiskRejected_DoesNotRecordExecution()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
var risk = new TestRiskManager(false);
|
|
var sizer = new TestPositionSizer(3);
|
|
adapter.Initialize(risk, sizer);
|
|
|
|
// Act
|
|
adapter.ExecuteIntent(CreateIntent(), CreateContext(), CreateConfig());
|
|
var history = adapter.GetExecutionHistory();
|
|
|
|
// Assert
|
|
Assert.AreEqual(0, history.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ExecuteIntent_AllowedAndSized_RecordsExecution()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
var risk = new TestRiskManager(true);
|
|
var sizer = new TestPositionSizer(4);
|
|
adapter.Initialize(risk, sizer);
|
|
|
|
// Act
|
|
adapter.ExecuteIntent(CreateIntent(), CreateContext(), CreateConfig());
|
|
var history = adapter.GetExecutionHistory();
|
|
|
|
// Assert
|
|
Assert.AreEqual(1, history.Count);
|
|
Assert.AreEqual("ES", history[0].Symbol);
|
|
Assert.AreEqual(OrderSide.Buy, history[0].Side);
|
|
Assert.AreEqual(OrderType.Market, history[0].EntryType);
|
|
Assert.AreEqual(4, history[0].Contracts);
|
|
Assert.AreEqual(8, history[0].StopTicks);
|
|
Assert.AreEqual(16, history[0].TargetTicks);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetExecutionHistory_ReturnsCopy_NotMutableInternalReference()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
var risk = new TestRiskManager(true);
|
|
var sizer = new TestPositionSizer(2);
|
|
adapter.Initialize(risk, sizer);
|
|
adapter.ExecuteIntent(CreateIntent(), CreateContext(), CreateConfig());
|
|
|
|
// Act
|
|
var history = adapter.GetExecutionHistory();
|
|
history.Clear();
|
|
var historyAfterClear = adapter.GetExecutionHistory();
|
|
|
|
// Assert
|
|
Assert.AreEqual(1, historyAfterClear.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void OnOrderUpdate_EmptyOrderId_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<ArgumentException>(
|
|
() => adapter.OnOrderUpdate("", 0, 0, 1, 0, 0, "Working", DateTime.UtcNow, "", ""));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void OnExecutionUpdate_EmptyExecutionId_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<ArgumentException>(
|
|
() => adapter.OnExecutionUpdate("", "O1", 100, 1, "Long", DateTime.UtcNow));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void OnExecutionUpdate_EmptyOrderId_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var adapter = new NT8OrderAdapter(new FakeBridge());
|
|
|
|
// Act / Assert
|
|
Assert.ThrowsException<ArgumentException>(
|
|
() => adapter.OnExecutionUpdate("E1", "", 100, 1, "Long", DateTime.UtcNow));
|
|
}
|
|
|
|
private static StrategyIntent CreateIntent()
|
|
{
|
|
return new StrategyIntent(
|
|
"ES",
|
|
OrderSide.Buy,
|
|
OrderType.Market,
|
|
null,
|
|
8,
|
|
16,
|
|
0.8,
|
|
"Order adapter integration test",
|
|
new Dictionary<string, object>());
|
|
}
|
|
|
|
private static StrategyContext CreateContext()
|
|
{
|
|
return new StrategyContext(
|
|
"ES",
|
|
DateTime.UtcNow,
|
|
new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
|
|
new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
|
|
new MarketSession(DateTime.Today.AddHours(9.5), DateTime.Today.AddHours(16), true, "RTH"),
|
|
new Dictionary<string, object>());
|
|
}
|
|
|
|
private static StrategyConfig CreateConfig()
|
|
{
|
|
return new StrategyConfig(
|
|
"Test",
|
|
"ES",
|
|
new Dictionary<string, object>(),
|
|
new RiskConfig(1000, 500, 5, true),
|
|
new SizingConfig(SizingMethod.FixedContracts, 1, 10, 500, new Dictionary<string, object>()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test risk manager implementation for adapter tests.
|
|
/// </summary>
|
|
private class TestRiskManager : IRiskManager
|
|
{
|
|
private readonly bool _allow;
|
|
|
|
public TestRiskManager(bool allow)
|
|
{
|
|
_allow = allow;
|
|
}
|
|
|
|
public RiskDecision ValidateOrder(StrategyIntent intent, StrategyContext context, RiskConfig config)
|
|
{
|
|
return new RiskDecision(
|
|
_allow,
|
|
_allow ? null : "Rejected by test risk manager",
|
|
intent,
|
|
RiskLevel.Low,
|
|
new Dictionary<string, object>());
|
|
}
|
|
|
|
public void OnFill(OrderFill fill)
|
|
{
|
|
}
|
|
|
|
public void OnPnLUpdate(double netPnL, double dayPnL)
|
|
{
|
|
}
|
|
|
|
public Task<bool> EmergencyFlatten(string reason)
|
|
{
|
|
return Task.FromResult(true);
|
|
}
|
|
|
|
public RiskStatus GetRiskStatus()
|
|
{
|
|
return new RiskStatus(true, 0, 1000, 0, 0, DateTime.UtcNow, new List<string>());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test position sizer implementation for adapter tests.
|
|
/// </summary>
|
|
private class TestPositionSizer : IPositionSizer
|
|
{
|
|
private readonly int _contracts;
|
|
|
|
public TestPositionSizer(int contracts)
|
|
{
|
|
_contracts = contracts;
|
|
}
|
|
|
|
public SizingResult CalculateSize(StrategyIntent intent, StrategyContext context, SizingConfig config)
|
|
{
|
|
return new SizingResult(_contracts, 100, SizingMethod.FixedContracts, new Dictionary<string, object>());
|
|
}
|
|
|
|
public SizingMetadata GetMetadata()
|
|
{
|
|
return new SizingMetadata("TestSizer", "Test sizer", new List<string>());
|
|
}
|
|
}
|
|
}
|
|
}
|