246 lines
8.4 KiB
C#
246 lines
8.4 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Unit tests for NT8ExecutionAdapter.
|
|
/// </summary>
|
|
[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<InvalidOperationException>(
|
|
delegate
|
|
{
|
|
adapter.SubmitOrder(CreateRequest(), "SDK-1");
|
|
});
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SubmitOrder_WithNullRequest_ShouldThrowArgumentNullException()
|
|
{
|
|
var adapter = new NT8ExecutionAdapter();
|
|
|
|
Assert.ThrowsException<ArgumentNullException>(
|
|
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<Task>();
|
|
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;
|
|
}
|
|
}
|
|
}
|