Clean tradingbot and spot and future

This commit is contained in:
2025-12-11 14:10:38 +07:00
parent 292a48d108
commit df8c199cce
5 changed files with 32 additions and 61 deletions

View File

@@ -36,12 +36,12 @@ public class BacktestFuturesBot : TradingBotBase, ITradingBot
// Backtest mode: Skip account loading and broker initialization // Backtest mode: Skip account loading and broker initialization
// Just log basic startup info // Just log basic startup info
await LogInformation($"🔬 Backtest Bot Started\n" + await LogInformation($"🔬 Backtest Bot Started\n" +
$"📊 Testing Setup:\n" + $"📊 Testing Setup:\n" +
$"🎯 Ticker: `{Config.Ticker}`\n" + $"🎯 Ticker: `{Config.Ticker}`\n" +
$"⏰ Timeframe: `{Config.Timeframe}`\n" + $"⏰ Timeframe: `{Config.Timeframe}`\n" +
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" + $"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
$"💰 Initial Balance: `${Config.BotTradingBalance:F2}`\n" + $"💰 Initial Balance: `${Config.BotTradingBalance:F2}`\n" +
$"✅ Ready to run backtest simulation"); $"✅ Ready to run backtest simulation");
} }
public override async Task Run() public override async Task Run()
@@ -68,12 +68,6 @@ public class BacktestFuturesBot : TradingBotBase, ITradingBot
return position; return position;
} }
protected override async Task<List<Position>> GetBrokerPositionsForUpdate(Account account)
{
// In backtest mode, return empty list (no broker positions to check)
return new List<Position>();
}
protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions) protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions)
{ {
// In backtest mode, skip broker synchronization // In backtest mode, skip broker synchronization
@@ -289,7 +283,8 @@ public class BacktestFuturesBot : TradingBotBase, ITradingBot
_scopeFactory, async (exchangeService, accountService, tradingService) => _scopeFactory, async (exchangeService, accountService, tradingService) =>
{ {
closedPosition = closedPosition =
await new CloseBacktestFuturesPositionCommandHandler(exchangeService, accountService, tradingService, await new CloseBacktestFuturesPositionCommandHandler(exchangeService, accountService,
tradingService,
_scopeFactory) _scopeFactory)
.Handle(command); .Handle(command);
}); });

View File

@@ -36,12 +36,12 @@ public class BacktestSpotBot : TradingBotBase, ITradingBot
// Backtest mode: Skip account loading and broker initialization // Backtest mode: Skip account loading and broker initialization
// Just log basic startup info // Just log basic startup info
await LogInformation($"🔬 Backtest Spot Bot Started\n" + await LogInformation($"🔬 Backtest Spot Bot Started\n" +
$"📊 Testing Setup:\n" + $"📊 Testing Setup:\n" +
$"🎯 Ticker: `{Config.Ticker}`\n" + $"🎯 Ticker: `{Config.Ticker}`\n" +
$"⏰ Timeframe: `{Config.Timeframe}`\n" + $"⏰ Timeframe: `{Config.Timeframe}`\n" +
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" + $"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
$"💰 Initial Balance: `${Config.BotTradingBalance:F2}`\n" + $"💰 Initial Balance: `${Config.BotTradingBalance:F2}`\n" +
$"✅ Ready to run spot backtest simulation"); $"✅ Ready to run spot backtest simulation");
} }
public override async Task Run() public override async Task Run()
@@ -67,12 +67,6 @@ public class BacktestSpotBot : TradingBotBase, ITradingBot
return position; return position;
} }
protected override async Task<List<Position>> GetBrokerPositionsForUpdate(Account account)
{
// In backtest mode, return empty list (no broker positions to check)
return new List<Position>();
}
protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions) protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions)
{ {
// In backtest mode, skip broker synchronization // In backtest mode, skip broker synchronization
@@ -247,7 +241,8 @@ public class BacktestSpotBot : TradingBotBase, ITradingBot
// Only LONG signals should reach here (SHORT signals are filtered out earlier) // Only LONG signals should reach here (SHORT signals are filtered out earlier)
if (signal.Direction != TradeDirection.Long) if (signal.Direction != TradeDirection.Long)
{ {
throw new InvalidOperationException($"Only LONG signals can open positions in spot trading. Received: {signal.Direction}"); throw new InvalidOperationException(
$"Only LONG signals can open positions in spot trading. Received: {signal.Direction}");
} }
if (Account == null || Account.User == null) if (Account == null || Account.User == null)
@@ -330,5 +325,4 @@ public class BacktestSpotBot : TradingBotBase, ITradingBot
} }
} }
} }
} }

View File

@@ -75,14 +75,6 @@ public class FuturesBot : TradingBotBase, ITradingBot
async tradingService => { return await tradingService.GetPositionByIdentifierAsync(position.Identifier); }); async tradingService => { return await tradingService.GetPositionByIdentifierAsync(position.Identifier); });
} }
protected override async Task<List<Position>> GetBrokerPositionsForUpdate(Account account)
{
// For live trading, get real broker positions
return await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(
_scopeFactory,
async exchangeService => { return [.. await exchangeService.GetBrokerPositions(Account)]; });
}
protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions) protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions)
{ {
// Live trading broker position synchronization logic is handled in the base UpdatePosition method // Live trading broker position synchronization logic is handled in the base UpdatePosition method
@@ -210,11 +202,16 @@ public class FuturesBot : TradingBotBase, ITradingBot
} }
protected override async Task SynchronizeWithBrokerPositions(Position internalPosition, Position positionForSignal, protected override async Task SynchronizeWithBrokerPositions(
List<Position> brokerPositions) Position internalPosition,
Position positionForSignal)
{ {
var liveBrokerPositions = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(
_scopeFactory,
async exchangeService => [.. await exchangeService.GetBrokerPositions(Account)]);
// Improved broker position matching with more robust logic // Improved broker position matching with more robust logic
var brokerPosition = brokerPositions var brokerPosition = liveBrokerPositions
.Where(p => p.Ticker == Config.Ticker) .Where(p => p.Ticker == Config.Ticker)
.OrderByDescending(p => p.Open?.Date ?? DateTime.MinValue) .OrderByDescending(p => p.Open?.Date ?? DateTime.MinValue)
.FirstOrDefault(p => p.OriginDirection == positionForSignal.OriginDirection); .FirstOrDefault(p => p.OriginDirection == positionForSignal.OriginDirection);
@@ -284,7 +281,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
Logger.LogWarning( Logger.LogWarning(
$"⚠️ Position Sync Issue Detected\n" + $"⚠️ Position Sync Issue Detected\n" +
$"Internal position {internalPosition.Identifier} shows Filled\n" + $"Internal position {internalPosition.Identifier} shows Filled\n" +
$"But not found in broker positions list (Count: {brokerPositions.Count})\n" + $"But not found in broker positions list (Count: {liveBrokerPositions.Count})\n" +
$"Checking position history before marking as closed..."); $"Checking position history before marking as closed...");
// Verify in exchange history before assuming it's closed // Verify in exchange history before assuming it's closed

View File

@@ -1,3 +1,4 @@
#nullable enable
using Managing.Application.Abstractions.Grains; using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands; using Managing.Application.Trading.Commands;
@@ -68,15 +69,7 @@ public class SpotBot : TradingBotBase
// For live trading, get position from database via trading service // For live trading, get position from database via trading service
return await ServiceScopeHelpers.WithScopedService<ITradingService, Position>( return await ServiceScopeHelpers.WithScopedService<ITradingService, Position>(
_scopeFactory, _scopeFactory,
async tradingService => { return await tradingService.GetPositionByIdentifierAsync(position.Identifier); }); async tradingService => await tradingService.GetPositionByIdentifierAsync(position.Identifier));
}
protected override async Task<List<Position>> GetBrokerPositionsForUpdate(Account account)
{
// For live spot trading, we don't have broker positions like futures
// Positions are verified via token balances in UpdatePositionWithBrokerData and SynchronizeWithBrokerPositions
// Return empty list - actual verification happens in those methods
return new List<Position>();
} }
protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions) protected override async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions)
@@ -88,7 +81,7 @@ public class SpotBot : TradingBotBase
_scopeFactory, _scopeFactory,
async exchangeService => await exchangeService.GetBalance(Account, Config.Ticker)); async exchangeService => await exchangeService.GetBalance(Account, Config.Ticker));
if (tokenBalance == null || tokenBalance.Amount <= 0) if (tokenBalance is not { Amount: > 0 })
{ {
// No token balance found - position might be closed // No token balance found - position might be closed
return; return;
@@ -225,8 +218,7 @@ public class SpotBot : TradingBotBase
} }
protected override async Task SynchronizeWithBrokerPositions(Position internalPosition, Position positionForSignal, protected override async Task SynchronizeWithBrokerPositions(Position internalPosition, Position positionForSignal)
List<Position> brokerPositions)
{ {
// For spot trading, fetch token balance directly and verify/match with internal position // For spot trading, fetch token balance directly and verify/match with internal position
try try

View File

@@ -462,10 +462,9 @@ public abstract class TradingBotBase : ITradingBot
} }
Position internalPosition = await GetInternalPositionForUpdate(positionForSignal); Position internalPosition = await GetInternalPositionForUpdate(positionForSignal);
var brokerPositions = await GetBrokerPositionsForUpdate(Account);
// Handle broker position synchronization (futures-specific logic) // Handle broker position synchronization (futures-specific logic)
await SynchronizeWithBrokerPositions(internalPosition, positionForSignal, brokerPositions); await SynchronizeWithBrokerPositions(internalPosition, positionForSignal);
// Handle order management and position status (futures-specific logic) // Handle order management and position status (futures-specific logic)
await HandleOrderManagementAndPositionStatus(signal, internalPosition, positionForSignal); await HandleOrderManagementAndPositionStatus(signal, internalPosition, positionForSignal);
@@ -650,8 +649,7 @@ public abstract class TradingBotBase : ITradingBot
} }
// Virtual methods for trading mode-specific behavior // Virtual methods for trading mode-specific behavior
protected virtual async Task SynchronizeWithBrokerPositions(Position internalPosition, Position positionForSignal, protected virtual async Task SynchronizeWithBrokerPositions(Position internalPosition, Position positionForSignal)
List<Position> brokerPositions)
{ {
// Default implementation: do nothing (for backtest) // Default implementation: do nothing (for backtest)
} }
@@ -1999,11 +1997,6 @@ public abstract class TradingBotBase : ITradingBot
return position; // Default implementation for backtest return position; // Default implementation for backtest
} }
protected virtual async Task<List<Position>> GetBrokerPositionsForUpdate(Account account)
{
return new List<Position>(); // Default implementation for backtest
}
protected virtual async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions) protected virtual async Task UpdatePositionWithBrokerData(Position position, List<Position> brokerPositions)
{ {
// Default: do nothing for backtest // Default: do nothing for backtest