using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using NT8.Adapters.NinjaTrader; using NT8.Core.OMS; namespace NT8.Core.Tests.Adapters { /// /// Unit tests for NT8ExecutionAdapter. /// [TestClass] public class NT8ExecutionAdapterTests { [TestMethod] public void SubmitOrder_WithValidRequest_ShouldCreateTrackingInfo() { var adapter = new NT8ExecutionAdapter(); var request = CreateRequest(); var info = adapter.SubmitOrder(request, "SDK-1"); Assert.IsNotNull(info); Assert.AreEqual("SDK-1", info.SdkOrderId); Assert.AreEqual(OrderState.Pending, info.CurrentState); Assert.AreEqual(0, info.FilledQuantity); } [TestMethod] public void SubmitOrder_WithDuplicateOrderId_ShouldThrowInvalidOperationException() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); Assert.ThrowsException( delegate { adapter.SubmitOrder(CreateRequest(), "SDK-1"); }); } [TestMethod] public void SubmitOrder_WithNullRequest_ShouldThrowArgumentNullException() { var adapter = new NT8ExecutionAdapter(); Assert.ThrowsException( delegate { adapter.SubmitOrder(null, "SDK-1"); }); } [TestMethod] public void ProcessOrderUpdate_WithWorkingState_ShouldUpdateState() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "WORKING", 0, 0.0, 0, null); var status = adapter.GetOrderStatus("SDK-1"); Assert.IsNotNull(status); Assert.AreEqual(OrderState.Working, status.State); } [TestMethod] public void ProcessOrderUpdate_WithFilledState_ShouldMarkFilled() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(2), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "FILLED", 2, 4205.25, 0, null); var status = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.Filled, status.State); Assert.AreEqual(2, status.FilledQuantity); } [TestMethod] public void ProcessOrderUpdate_WithRejection_ShouldSetRejectedState() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "WORKING", 0, 0.0, 123, "Rejected by broker"); var status = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.Rejected, status.State); } [TestMethod] public void ProcessExecution_WithFullFill_ShouldMarkFilled() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(3), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "PARTFILLED", 3, 4202.0, 0, null); adapter.ProcessExecution("NT8-1", "EX-1", 4202.0, 3, DateTime.UtcNow); var status = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.Filled, status.State); } [TestMethod] public void ProcessExecution_WithPartialFill_ShouldMarkPartiallyFilled() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(3), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "PARTFILLED", 1, 4202.0, 0, null); adapter.ProcessExecution("NT8-1", "EX-1", 4202.0, 1, DateTime.UtcNow); var status = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.PartiallyFilled, status.State); } [TestMethod] public void CancelOrder_WithWorkingOrder_ShouldReturnTrue() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "WORKING", 0, 0.0, 0, null); var result = adapter.CancelOrder("SDK-1"); Assert.IsTrue(result); } [TestMethod] public void CancelOrder_WithFilledOrder_ShouldReturnFalse() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "FILLED", 1, 4202.0, 0, null); var result = adapter.CancelOrder("SDK-1"); Assert.IsFalse(result); } [TestMethod] public void GetOrderStatus_WithExistingOrder_ShouldReturnStatus() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); var status = adapter.GetOrderStatus("SDK-1"); Assert.IsNotNull(status); Assert.AreEqual("SDK-1", status.OrderId); Assert.AreEqual("ES", status.Symbol); } [TestMethod] public void GetOrderStatus_WithNonExistentOrder_ShouldReturnNull() { var adapter = new NT8ExecutionAdapter(); var status = adapter.GetOrderStatus("MISSING"); Assert.IsNull(status); } [DataTestMethod] [DataRow("ACCEPTED", OrderState.Working)] [DataRow("FILLED", OrderState.Filled)] [DataRow("CANCELLED", OrderState.Cancelled)] [DataRow("REJECTED", OrderState.Rejected)] public void MapNT8OrderState_WithKnownStates_ShouldMapCorrectly(string nt8State, OrderState expected) { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", nt8State, 0, 0.0, 0, null); var status = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(expected, status.State); } [TestMethod] public void SubmitOrder_WithConcurrentCalls_ShouldBeThreadSafe() { var adapter = new NT8ExecutionAdapter(); var tasks = new List(); var count = 50; for (var i = 0; i < count; i++) { var index = i; tasks.Add(Task.Run( delegate { adapter.SubmitOrder(CreateRequest(), string.Format("SDK-{0}", index)); })); } Task.WaitAll(tasks.ToArray()); for (var i = 0; i < count; i++) { var status = adapter.GetOrderStatus(string.Format("SDK-{0}", i)); Assert.IsNotNull(status); } } [TestMethod] public void ProcessExecution_WithMultipleCallsForSameOrder_ShouldAccumulate() { var adapter = new NT8ExecutionAdapter(); adapter.SubmitOrder(CreateRequest(3), "SDK-1"); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "PARTFILLED", 1, 4201.0, 0, null); adapter.ProcessExecution("NT8-1", "EX-1", 4201.0, 1, DateTime.UtcNow); var statusAfterFirst = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.PartiallyFilled, statusAfterFirst.State); adapter.ProcessOrderUpdate("NT8-1", "SDK-1", "FILLED", 3, 4202.0, 0, null); adapter.ProcessExecution("NT8-1", "EX-2", 4202.0, 2, DateTime.UtcNow); var statusAfterSecond = adapter.GetOrderStatus("SDK-1"); Assert.AreEqual(OrderState.Filled, statusAfterSecond.State); Assert.AreEqual(3, statusAfterSecond.FilledQuantity); } private static OrderRequest CreateRequest() { return CreateRequest(1); } private static OrderRequest CreateRequest(int quantity) { var request = new OrderRequest(); request.Symbol = "ES"; request.Side = OrderSide.Buy; request.Type = OrderType.Market; request.Quantity = quantity; request.TimeInForce = TimeInForce.Day; request.ClientOrderId = Guid.NewGuid().ToString(); return request; } } }