112 lines
4.4 KiB
C#
112 lines
4.4 KiB
C#
using NT8.Core.Risk;
|
|
using NT8.Core.Common.Models;
|
|
using NT8.Core.Logging;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using System;
|
|
|
|
namespace NT8.Core.Tests.Risk
|
|
{
|
|
[TestClass]
|
|
public class BasicRiskManagerTests
|
|
{
|
|
private ILogger _logger;
|
|
private BasicRiskManager _riskManager;
|
|
|
|
[TestInitialize]
|
|
public void TestInitialize()
|
|
{
|
|
_logger = new BasicLogger("BasicRiskManagerTests");
|
|
_riskManager = new BasicRiskManager(_logger);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValidateOrder_WithinLimits_ShouldAllow()
|
|
{
|
|
// Arrange
|
|
var intent = TestDataBuilder.CreateValidIntent(stopTicks: 8);
|
|
var context = TestDataBuilder.CreateTestContext();
|
|
var config = TestDataBuilder.CreateTestRiskConfig();
|
|
|
|
// Act
|
|
var result = _riskManager.ValidateOrder(intent, context, config);
|
|
|
|
// Assert
|
|
Assert.IsTrue(result.Allow);
|
|
Assert.IsNull(result.RejectReason);
|
|
Assert.AreEqual(RiskLevel.Low, result.RiskLevel);
|
|
Assert.IsTrue(result.RiskMetrics.ContainsKey("trade_risk"));
|
|
Assert.IsTrue(result.RiskMetrics.ContainsKey("daily_pnl"));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValidateOrder_ExceedsDailyLimit_ShouldReject()
|
|
{
|
|
// Arrange
|
|
var intent = TestDataBuilder.CreateValidIntent();
|
|
var context = TestDataBuilder.CreateTestContext();
|
|
var config = new RiskConfig(
|
|
dailyLossLimit: 1000,
|
|
maxTradeRisk: 500,
|
|
maxOpenPositions: 5,
|
|
emergencyFlattenEnabled: true
|
|
);
|
|
|
|
// Simulate daily loss exceeding limit
|
|
_riskManager.OnPnLUpdate(0, -1001);
|
|
|
|
// Act
|
|
var result = _riskManager.ValidateOrder(intent, context, config);
|
|
|
|
// Assert
|
|
Assert.IsFalse(result.Allow);
|
|
// Accept either "Trading halted" or "Daily loss limit" as valid rejection reasons
|
|
Assert.IsTrue(result.RejectReason.Contains("Trading halted") || result.RejectReason.Contains("Daily loss limit breached"),
|
|
"Expected reject reason to contain either 'Trading halted' or 'Daily loss limit breached', but got: " + result.RejectReason);
|
|
Assert.AreEqual(RiskLevel.Critical, result.RiskLevel);
|
|
Assert.AreEqual(-1001.0, result.RiskMetrics["daily_pnl"]);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValidateOrder_ExceedsTradeRisk_ShouldReject()
|
|
{
|
|
// Arrange
|
|
var intent = TestDataBuilder.CreateValidIntent(stopTicks: 100); // High risk trade
|
|
var context = TestDataBuilder.CreateTestContext();
|
|
var config = new RiskConfig(
|
|
dailyLossLimit: 10000,
|
|
maxTradeRisk: 500, // Lower than calculated trade risk
|
|
maxOpenPositions: 5,
|
|
emergencyFlattenEnabled: true
|
|
);
|
|
|
|
// Act
|
|
var result = _riskManager.ValidateOrder(intent, context, config);
|
|
|
|
// Assert
|
|
Assert.IsFalse(result.Allow);
|
|
// Accept either "Trading halted" or "Trade risk too high" as valid rejection reasons
|
|
Assert.IsTrue(result.RejectReason.Contains("Trading halted") || result.RejectReason.Contains("Trade risk too high"),
|
|
"Expected reject reason to contain either 'Trading halted' or 'Trade risk too high', but got: " + result.RejectReason);
|
|
Assert.AreEqual(RiskLevel.High, result.RiskLevel);
|
|
|
|
// Verify risk calculation
|
|
var expectedRisk = 100 * 12.50; // 100 ticks * ES tick value
|
|
Assert.AreEqual(expectedRisk, result.RiskMetrics["trade_risk"]);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValidateOrder_WithNullParameters_ShouldThrow()
|
|
{
|
|
// Arrange
|
|
var intent = TestDataBuilder.CreateValidIntent();
|
|
var context = TestDataBuilder.CreateTestContext();
|
|
var config = TestDataBuilder.CreateTestRiskConfig();
|
|
|
|
// Act & Assert
|
|
Assert.ThrowsException<ArgumentNullException>(() => _riskManager.ValidateOrder(null, context, config));
|
|
Assert.ThrowsException<ArgumentNullException>(() => _riskManager.ValidateOrder(intent, null, config));
|
|
Assert.ThrowsException<ArgumentNullException>(() => _riskManager.ValidateOrder(intent, context, null));
|
|
}
|
|
}
|
|
}
|