S2-B3/S3-05: PortfolioRiskManager, connection loss recovery, long-only lock
Some checks failed
Build and Test / build (push) Has been cancelled
Some checks failed
Build and Test / build (push) Has been cancelled
This commit is contained in:
@@ -53,6 +53,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
private int _ordersSubmittedToday;
|
||||
private DateTime _lastBarTime;
|
||||
private bool _killSwitchTriggered;
|
||||
private bool _connectionLost;
|
||||
private ExecutionCircuitBreaker _circuitBreaker;
|
||||
private System.IO.StreamWriter _fileLog;
|
||||
private readonly object _fileLock = new object();
|
||||
@@ -194,6 +195,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
EnableLongTrades = true;
|
||||
EnableShortTrades = true;
|
||||
_killSwitchTriggered = false;
|
||||
_connectionLost = false;
|
||||
}
|
||||
else if (State == State.DataLoaded)
|
||||
{
|
||||
@@ -220,6 +222,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
}
|
||||
else if (State == State.Terminated)
|
||||
{
|
||||
PortfolioRiskManager.Instance.UnregisterStrategy(Name);
|
||||
WriteSessionFooter();
|
||||
}
|
||||
}
|
||||
@@ -261,6 +264,14 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection loss guard — do not submit new orders if broker is disconnected
|
||||
if (_connectionLost)
|
||||
{
|
||||
if (EnableVerboseLogging)
|
||||
Print(string.Format("[NT8-SDK] Bar skipped — connection lost: {0}", Time[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
// Log first processable bar and every 100th bar.
|
||||
if (CurrentBar == BarsRequiredToTrade || CurrentBar % 100 == 0)
|
||||
{
|
||||
@@ -357,9 +368,54 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
execution.Price,
|
||||
execution.OrderId));
|
||||
|
||||
var fill = new NT8.Core.Common.Models.OrderFill(
|
||||
orderId,
|
||||
execution.Order != null ? execution.Order.Instrument.MasterInstrument.Name : string.Empty,
|
||||
execution.Quantity,
|
||||
execution.Price,
|
||||
time,
|
||||
0.0,
|
||||
executionId);
|
||||
PortfolioRiskManager.Instance.ReportFill(Name, fill);
|
||||
|
||||
_executionAdapter.ProcessExecution(orderId, executionId, price, quantity, time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles broker connection status changes. Halts new orders on disconnect,
|
||||
/// logs reconnect, and resets the connection flag when restored.
|
||||
/// </summary>
|
||||
protected override void OnConnectionStatusUpdate(
|
||||
Connection connection,
|
||||
ConnectionStatus status,
|
||||
DateTime time)
|
||||
{
|
||||
if (connection == null) return;
|
||||
|
||||
if (status == ConnectionStatus.Connected)
|
||||
{
|
||||
if (_connectionLost)
|
||||
{
|
||||
_connectionLost = false;
|
||||
Print(string.Format("[NT8-SDK] Connection RESTORED at {0} — trading resumed.",
|
||||
time.ToString("HH:mm:ss")));
|
||||
FileLog(string.Format("CONNECTION RESTORED at {0}", time.ToString("HH:mm:ss")));
|
||||
}
|
||||
}
|
||||
else if (status == ConnectionStatus.Disconnected ||
|
||||
status == ConnectionStatus.ConnectionLost)
|
||||
{
|
||||
if (!_connectionLost)
|
||||
{
|
||||
_connectionLost = true;
|
||||
Print(string.Format("[NT8-SDK] Connection LOST at {0} — halting new orders. Status={1}",
|
||||
time.ToString("HH:mm:ss"),
|
||||
status));
|
||||
FileLog(string.Format("CONNECTION LOST at {0} Status={1}", time.ToString("HH:mm:ss"), status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitFileLog()
|
||||
{
|
||||
if (!EnableFileLogging)
|
||||
@@ -418,6 +474,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
RiskPerTrade));
|
||||
FileLog(string.Format("Sizing : MinContracts={0} MaxContracts={1}", MinContracts, MaxContracts));
|
||||
FileLog(string.Format("VerboseLog : {0} FileLog: {1}", EnableVerboseLogging, EnableFileLogging));
|
||||
FileLog(string.Format("ConnectionLost : {0}", _connectionLost));
|
||||
FileLog("---");
|
||||
}
|
||||
|
||||
@@ -481,6 +538,8 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
|
||||
_sdkStrategy.Initialize(_strategyConfig, null, _logger);
|
||||
ConfigureStrategyParameters();
|
||||
PortfolioRiskManager.Instance.RegisterStrategy(Name, _riskConfig);
|
||||
Print(string.Format("[NT8-SDK] Registered with PortfolioRiskManager: {0}", PortfolioRiskManager.Instance.GetStatusSnapshot()));
|
||||
|
||||
_ordersSubmittedToday = 0;
|
||||
_lastBarTime = DateTime.MinValue;
|
||||
@@ -590,6 +649,16 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
|
||||
private void ProcessStrategyIntent(StrategyIntent intent, StrategyContext context)
|
||||
{
|
||||
// Portfolio-level risk check — runs before per-strategy risk validation
|
||||
var portfolioDecision = PortfolioRiskManager.Instance.ValidatePortfolioRisk(Name, intent);
|
||||
if (!portfolioDecision.Allow)
|
||||
{
|
||||
Print(string.Format("[SDK] Portfolio blocked: {0}", portfolioDecision.RejectReason));
|
||||
if (_logger != null)
|
||||
_logger.LogWarning("Portfolio risk blocked order: {0}", portfolioDecision.RejectReason);
|
||||
return;
|
||||
}
|
||||
|
||||
// Direction filter — checked before risk to avoid unnecessary processing
|
||||
if (intent.Side == SdkOrderSide.Buy && !EnableLongTrades)
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
||||
Calculate = Calculate.OnBarClose;
|
||||
BarsRequiredToTrade = 50;
|
||||
EnableLongTrades = true;
|
||||
// Long-only: short trades permanently disabled pending backtest confirmation
|
||||
EnableShortTrades = false;
|
||||
}
|
||||
else if (State == State.Configure)
|
||||
|
||||
Reference in New Issue
Block a user