Refactor BacktestExecutor and TradingBotBase for performance optimizations; remove unused SignalCache and pre-calculation logic; implement caching for open position state and streamline signal access with TryGetValue; enhance logging for detailed timing breakdown during backtest execution.
This commit is contained in:
@@ -47,6 +47,9 @@ public abstract class TradingBotBase : ITradingBot
|
||||
public Guid Identifier { get; set; } = Guid.Empty;
|
||||
public Candle LastCandle { get; set; }
|
||||
public DateTime? LastPositionClosingTime { get; set; }
|
||||
|
||||
// OPTIMIZATION 2: Cache open position state to avoid expensive Positions.Any() calls
|
||||
private bool _hasOpenPosition = false;
|
||||
|
||||
public TradingBotBase(
|
||||
ILogger<TradingBotBase> logger,
|
||||
@@ -264,12 +267,13 @@ public abstract class TradingBotBase : ITradingBot
|
||||
protected virtual async Task UpdateSignalsCore(IReadOnlyList<Candle> candles,
|
||||
Dictionary<IndicatorType, IndicatorsResultBase> preCalculatedIndicatorValues = null)
|
||||
{
|
||||
// OPTIMIZATION 2: Use cached open position state instead of expensive Positions.Any() call
|
||||
// Skip indicator checking if flipping is disabled and there's an open position
|
||||
// This prevents unnecessary indicator calculations when we can't act on signals anyway
|
||||
if (!Config.FlipPosition && Positions.Any(p => p.Value.IsOpen()))
|
||||
if (!Config.FlipPosition && _hasOpenPosition)
|
||||
{
|
||||
Logger.LogDebug(
|
||||
$"Skipping signal update: Position open and flip disabled. Open positions: {Positions.Count(p => p.Value.IsOpen())}");
|
||||
$"Skipping signal update: Position open and flip disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,20 +342,19 @@ public abstract class TradingBotBase : ITradingBot
|
||||
|
||||
protected async Task ManagePositions()
|
||||
{
|
||||
// Early exit optimization - skip if no positions to manage
|
||||
// Optimized: Use for loop to avoid multiple iterations
|
||||
bool hasOpenPositions = false;
|
||||
// OPTIMIZATION 6: Combine early exit checks and collect unfinished positions in one pass
|
||||
// Collect unfinished positions in first iteration to avoid LINQ Where() later
|
||||
var unfinishedPositions = new List<Position>();
|
||||
foreach (var position in Positions.Values)
|
||||
{
|
||||
if (!position.IsFinished())
|
||||
{
|
||||
hasOpenPositions = true;
|
||||
break;
|
||||
unfinishedPositions.Add(position);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasWaitingSignals = false;
|
||||
if (!hasOpenPositions) // Only check signals if no open positions
|
||||
if (unfinishedPositions.Count == 0) // Only check signals if no open positions
|
||||
{
|
||||
foreach (var signal in Signals.Values)
|
||||
{
|
||||
@@ -363,14 +366,14 @@ public abstract class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasOpenPositions && !hasWaitingSignals)
|
||||
if (unfinishedPositions.Count == 0 && !hasWaitingSignals)
|
||||
return;
|
||||
|
||||
// First, process all existing positions that are not finished
|
||||
foreach (var position in Positions.Values.Where(p => !p.IsFinished()))
|
||||
foreach (var position in unfinishedPositions)
|
||||
{
|
||||
var signalForPosition = Signals[position.SignalIdentifier];
|
||||
if (signalForPosition == null)
|
||||
// OPTIMIZATION 3: Use TryGetValue instead of direct dictionary access
|
||||
if (!Signals.TryGetValue(position.SignalIdentifier, out var signalForPosition))
|
||||
{
|
||||
await LogInformation(
|
||||
$"🔍 Signal Recovery\nSignal not found for position `{position.Identifier}`\nRecreating signal from position data...");
|
||||
@@ -873,7 +876,13 @@ public abstract class TradingBotBase : ITradingBot
|
||||
|
||||
if (openedPosition != null)
|
||||
{
|
||||
var previousSignal = Signals[openedPosition.SignalIdentifier];
|
||||
// OPTIMIZATION 3: Use TryGetValue instead of direct dictionary access
|
||||
if (!Signals.TryGetValue(openedPosition.SignalIdentifier, out var previousSignal))
|
||||
{
|
||||
// Signal not found, expire new signal and return
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (openedPosition.OriginDirection == signal.Direction)
|
||||
{
|
||||
@@ -908,6 +917,9 @@ public abstract class TradingBotBase : ITradingBot
|
||||
{
|
||||
// Add position to internal collection before any status updates
|
||||
Positions[position.Identifier] = position;
|
||||
|
||||
// OPTIMIZATION 2: Update cached open position state
|
||||
_hasOpenPosition = true;
|
||||
|
||||
if (position.Open.Status != TradeStatus.Cancelled && position.Status != PositionStatus.Rejected)
|
||||
{
|
||||
@@ -1229,6 +1241,9 @@ public abstract class TradingBotBase : ITradingBot
|
||||
|
||||
SkipCandleBasedCalculation:
|
||||
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);
|
||||
|
||||
// OPTIMIZATION 2: Update cached open position state after closing position
|
||||
_hasOpenPosition = Positions.Values.Any(p => p.IsOpen());
|
||||
|
||||
// Update position in database with all trade changes
|
||||
if (TradingBox.IsLiveTrading(Config.TradingType))
|
||||
@@ -1413,9 +1428,10 @@ public abstract class TradingBotBase : ITradingBot
|
||||
|
||||
protected void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus)
|
||||
{
|
||||
if (Signals.ContainsKey(signalIdentifier) && Signals[signalIdentifier].Status != signalStatus)
|
||||
// OPTIMIZATION 4: Use TryGetValue instead of ContainsKey + direct access (single lookup)
|
||||
if (Signals.TryGetValue(signalIdentifier, out var signal) && signal.Status != signalStatus)
|
||||
{
|
||||
Signals[signalIdentifier].Status = signalStatus;
|
||||
signal.Status = signalStatus;
|
||||
Logger.LogDebug($"Signal {signalIdentifier} is now {signalStatus}");
|
||||
}
|
||||
}
|
||||
@@ -1470,6 +1486,13 @@ public abstract class TradingBotBase : ITradingBot
|
||||
{
|
||||
try
|
||||
{
|
||||
// OPTIMIZATION 1: Early return for backtest - skip all logging and validation
|
||||
if (TradingBox.IsBacktestTrading(Config.TradingType))
|
||||
{
|
||||
Signals.Add(signal.Identifier, signal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set signal status based on configuration
|
||||
if (Config.IsForWatchingOnly || (ExecutionCount < 1 && TradingBox.IsLiveTrading(Config.TradingType)))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user