More fix
This commit is contained in:
@@ -176,18 +176,11 @@ public class BacktestController : BaseController
|
|||||||
switch (request.Config.BotType)
|
switch (request.Config.BotType)
|
||||||
{
|
{
|
||||||
case BotType.SimpleBot:
|
case BotType.SimpleBot:
|
||||||
|
// SimpleBot backtest not implemented yet
|
||||||
break;
|
break;
|
||||||
case BotType.ScalpingBot:
|
case BotType.ScalpingBot:
|
||||||
backtestResult = await _backtester.RunScalpingBotBacktest(
|
|
||||||
backtestConfig,
|
|
||||||
request.StartDate,
|
|
||||||
request.EndDate,
|
|
||||||
user,
|
|
||||||
request.Save,
|
|
||||||
null);
|
|
||||||
break;
|
|
||||||
case BotType.FlippingBot:
|
case BotType.FlippingBot:
|
||||||
backtestResult = await _backtester.RunFlippingBotBacktest(
|
backtestResult = await _backtester.RunTradingBotBacktest(
|
||||||
backtestConfig,
|
backtestConfig,
|
||||||
request.StartDate,
|
request.StartDate,
|
||||||
request.EndDate,
|
request.EndDate,
|
||||||
|
|||||||
@@ -7,6 +7,39 @@ namespace Managing.Application.Abstractions.Services
|
|||||||
{
|
{
|
||||||
public interface IBacktester
|
public interface IBacktester
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a unified trading bot backtest with the specified configuration and date range.
|
||||||
|
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <param name="startDate">The start date for the backtest</param>
|
||||||
|
/// <param name="endDate">The end date for the backtest</param>
|
||||||
|
/// <param name="user">The user running the backtest</param>
|
||||||
|
/// <param name="save">Whether to save the backtest results</param>
|
||||||
|
/// <param name="initialCandles">Optional pre-loaded candles</param>
|
||||||
|
/// <returns>The backtest results</returns>
|
||||||
|
Task<Backtest> RunTradingBotBacktest(
|
||||||
|
TradingBotConfig config,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
User user = null,
|
||||||
|
bool save = false,
|
||||||
|
List<Candle>? initialCandles = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a unified trading bot backtest with pre-loaded candles.
|
||||||
|
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <param name="candles">The candles to use for backtesting</param>
|
||||||
|
/// <param name="user">The user running the backtest</param>
|
||||||
|
/// <returns>The backtest results</returns>
|
||||||
|
Task<Backtest> RunTradingBotBacktest(
|
||||||
|
TradingBotConfig config,
|
||||||
|
List<Candle> candles,
|
||||||
|
User user = null);
|
||||||
|
|
||||||
|
// Legacy methods - maintained for backward compatibility
|
||||||
Task<Backtest> RunScalpingBotBacktest(
|
Task<Backtest> RunScalpingBotBacktest(
|
||||||
TradingBotConfig config,
|
TradingBotConfig config,
|
||||||
DateTime startDate,
|
DateTime startDate,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Managing.Application.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Bots;
|
|
||||||
using Managing.Domain.Workflows;
|
using Managing.Domain.Workflows;
|
||||||
|
|
||||||
namespace Managing.Application.Abstractions
|
namespace Managing.Application.Abstractions
|
||||||
@@ -7,6 +6,22 @@ namespace Managing.Application.Abstractions
|
|||||||
public interface IBotFactory
|
public interface IBotFactory
|
||||||
{
|
{
|
||||||
IBot CreateSimpleBot(string botName, Workflow workflow);
|
IBot CreateSimpleBot(string botName, Workflow workflow);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a trading bot using the unified TradingBot class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <returns>ITradingBot instance</returns>
|
||||||
|
ITradingBot CreateTradingBot(TradingBotConfig config);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a trading bot for backtesting using the unified TradingBot class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <returns>ITradingBot instance configured for backtesting</returns>
|
||||||
|
ITradingBot CreateBacktestTradingBot(TradingBotConfig config);
|
||||||
|
|
||||||
|
// Legacy methods - these will use TradingBot internally but maintain backward compatibility
|
||||||
ITradingBot CreateScalpingBot(TradingBotConfig config);
|
ITradingBot CreateScalpingBot(TradingBotConfig config);
|
||||||
ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
|
ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
|
||||||
ITradingBot CreateFlippingBot(TradingBotConfig config);
|
ITradingBot CreateFlippingBot(TradingBotConfig config);
|
||||||
|
|||||||
@@ -15,6 +15,21 @@ public interface IBotService
|
|||||||
void StartBotFromBackup(BotBackup backupBot);
|
void StartBotFromBackup(BotBackup backupBot);
|
||||||
BotBackup GetBotBackup(string identifier);
|
BotBackup GetBotBackup(string identifier);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a trading bot using the unified TradingBot class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <returns>ITradingBot instance</returns>
|
||||||
|
ITradingBot CreateTradingBot(TradingBotConfig config);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a trading bot for backtesting using the unified TradingBot class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <returns>ITradingBot instance configured for backtesting</returns>
|
||||||
|
ITradingBot CreateBacktestTradingBot(TradingBotConfig config);
|
||||||
|
|
||||||
|
// Legacy methods - these will use TradingBot internally but maintain backward compatibility
|
||||||
ITradingBot CreateScalpingBot(TradingBotConfig config);
|
ITradingBot CreateScalpingBot(TradingBotConfig config);
|
||||||
ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
|
ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
|
||||||
ITradingBot CreateFlippingBot(TradingBotConfig config);
|
ITradingBot CreateFlippingBot(TradingBotConfig config);
|
||||||
|
|||||||
@@ -55,7 +55,18 @@ namespace Managing.Application.Backtesting
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Backtest> RunScalpingBotBacktest(
|
/// <summary>
|
||||||
|
/// Runs a unified trading bot backtest with the specified configuration and date range.
|
||||||
|
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <param name="startDate">The start date for the backtest</param>
|
||||||
|
/// <param name="endDate">The end date for the backtest</param>
|
||||||
|
/// <param name="user">The user running the backtest</param>
|
||||||
|
/// <param name="save">Whether to save the backtest results</param>
|
||||||
|
/// <param name="initialCandles">Optional pre-loaded candles</param>
|
||||||
|
/// <returns>The backtest results</returns>
|
||||||
|
public async Task<Backtest> RunTradingBotBacktest(
|
||||||
TradingBotConfig config,
|
TradingBotConfig config,
|
||||||
DateTime startDate,
|
DateTime startDate,
|
||||||
DateTime endDate,
|
DateTime endDate,
|
||||||
@@ -64,12 +75,17 @@ namespace Managing.Application.Backtesting
|
|||||||
List<Candle>? initialCandles = null)
|
List<Candle>? initialCandles = null)
|
||||||
{
|
{
|
||||||
var account = await GetAccountFromConfig(config);
|
var account = await GetAccountFromConfig(config);
|
||||||
var scalpingBot = _botFactory.CreateBacktestScalpingBot(config);
|
|
||||||
scalpingBot.LoadScenario(config.ScenarioName);
|
// Set FlipPosition based on BotType
|
||||||
scalpingBot.User = user;
|
config.FlipPosition = config.BotType == BotType.FlippingBot;
|
||||||
await scalpingBot.LoadAccount();
|
|
||||||
|
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||||
|
tradingBot.LoadScenario(config.ScenarioName);
|
||||||
|
tradingBot.User = user;
|
||||||
|
await tradingBot.LoadAccount();
|
||||||
|
|
||||||
var candles = initialCandles ?? GetCandles(account, config.Ticker, config.Timeframe, startDate, endDate);
|
var candles = initialCandles ?? GetCandles(account, config.Ticker, config.Timeframe, startDate, endDate);
|
||||||
var result = GetBacktestingResult(config, scalpingBot, candles);
|
var result = GetBacktestingResult(config, tradingBot, candles);
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
@@ -88,6 +104,52 @@ namespace Managing.Application.Backtesting
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs a unified trading bot backtest with pre-loaded candles.
|
||||||
|
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The trading bot configuration</param>
|
||||||
|
/// <param name="candles">The candles to use for backtesting</param>
|
||||||
|
/// <param name="user">The user running the backtest</param>
|
||||||
|
/// <returns>The backtest results</returns>
|
||||||
|
public async Task<Backtest> RunTradingBotBacktest(
|
||||||
|
TradingBotConfig config,
|
||||||
|
List<Candle> candles,
|
||||||
|
User user = null)
|
||||||
|
{
|
||||||
|
var account = await GetAccountFromConfig(config);
|
||||||
|
|
||||||
|
// Set FlipPosition based on BotType
|
||||||
|
config.FlipPosition = config.BotType == BotType.FlippingBot;
|
||||||
|
|
||||||
|
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||||
|
tradingBot.LoadScenario(config.ScenarioName);
|
||||||
|
tradingBot.User = user;
|
||||||
|
await tradingBot.LoadAccount();
|
||||||
|
|
||||||
|
var result = GetBacktestingResult(config, tradingBot, candles);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
result.User = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy methods - maintained for backward compatibility
|
||||||
|
public async Task<Backtest> RunScalpingBotBacktest(
|
||||||
|
TradingBotConfig config,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
User user = null,
|
||||||
|
bool save = false,
|
||||||
|
List<Candle>? initialCandles = null)
|
||||||
|
{
|
||||||
|
config.BotType = BotType.ScalpingBot; // Ensure correct type
|
||||||
|
return await RunTradingBotBacktest(config, startDate, endDate, user, save, initialCandles);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Backtest> RunFlippingBotBacktest(
|
public async Task<Backtest> RunFlippingBotBacktest(
|
||||||
TradingBotConfig config,
|
TradingBotConfig config,
|
||||||
DateTime startDate,
|
DateTime startDate,
|
||||||
@@ -96,30 +158,8 @@ namespace Managing.Application.Backtesting
|
|||||||
bool save = false,
|
bool save = false,
|
||||||
List<Candle>? initialCandles = null)
|
List<Candle>? initialCandles = null)
|
||||||
{
|
{
|
||||||
var account = await GetAccountFromConfig(config);
|
config.BotType = BotType.FlippingBot; // Ensure correct type
|
||||||
var flippingBot = _botFactory.CreateBacktestFlippingBot(config);
|
return await RunTradingBotBacktest(config, startDate, endDate, user, save, initialCandles);
|
||||||
flippingBot.LoadScenario(config.ScenarioName);
|
|
||||||
flippingBot.User = user;
|
|
||||||
await flippingBot.LoadAccount();
|
|
||||||
|
|
||||||
var candles = initialCandles ?? GetCandles(account, config.Ticker, config.Timeframe, startDate, endDate);
|
|
||||||
var result = GetBacktestingResult(config, flippingBot, candles);
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
result.User = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set start and end dates
|
|
||||||
result.StartDate = startDate;
|
|
||||||
result.EndDate = endDate;
|
|
||||||
|
|
||||||
if (save)
|
|
||||||
{
|
|
||||||
_backtestRepository.InsertBacktestForUser(user, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Backtest> RunScalpingBotBacktest(
|
public async Task<Backtest> RunScalpingBotBacktest(
|
||||||
@@ -127,20 +167,8 @@ namespace Managing.Application.Backtesting
|
|||||||
List<Candle> candles,
|
List<Candle> candles,
|
||||||
User user = null)
|
User user = null)
|
||||||
{
|
{
|
||||||
var account = await GetAccountFromConfig(config);
|
config.BotType = BotType.ScalpingBot; // Ensure correct type
|
||||||
var bot = _botFactory.CreateBacktestScalpingBot(config);
|
return await RunTradingBotBacktest(config, candles, user);
|
||||||
bot.LoadScenario(config.ScenarioName);
|
|
||||||
bot.User = user;
|
|
||||||
await bot.LoadAccount();
|
|
||||||
|
|
||||||
var result = GetBacktestingResult(config, bot, candles);
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
result.User = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Backtest> RunFlippingBotBacktest(
|
public async Task<Backtest> RunFlippingBotBacktest(
|
||||||
@@ -148,20 +176,8 @@ namespace Managing.Application.Backtesting
|
|||||||
List<Candle> candles,
|
List<Candle> candles,
|
||||||
User user = null)
|
User user = null)
|
||||||
{
|
{
|
||||||
var account = await GetAccountFromConfig(config);
|
config.BotType = BotType.FlippingBot; // Ensure correct type
|
||||||
var bot = _botFactory.CreateBacktestFlippingBot(config);
|
return await RunTradingBotBacktest(config, candles, user);
|
||||||
bot.LoadScenario(config.ScenarioName);
|
|
||||||
bot.User = user;
|
|
||||||
await bot.LoadAccount();
|
|
||||||
|
|
||||||
var result = GetBacktestingResult(config, bot, candles);
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
result.User = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Account> GetAccountFromConfig(TradingBotConfig config)
|
private async Task<Account> GetAccountFromConfig(TradingBotConfig config)
|
||||||
|
|||||||
@@ -37,10 +37,37 @@ namespace Managing.Application.Bots.Base
|
|||||||
return new SimpleBot(botName, _tradingBotLogger, workflow, _botService);
|
return new SimpleBot(botName, _tradingBotLogger, workflow, _botService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ITradingBot IBotFactory.CreateTradingBot(TradingBotConfig config)
|
||||||
|
{
|
||||||
|
return new TradingBot(
|
||||||
|
_exchangeService,
|
||||||
|
_tradingBotLogger,
|
||||||
|
_tradingService,
|
||||||
|
_accountService,
|
||||||
|
_messengerService,
|
||||||
|
_botService,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
|
ITradingBot IBotFactory.CreateBacktestTradingBot(TradingBotConfig config)
|
||||||
|
{
|
||||||
|
config.IsForBacktest = true;
|
||||||
|
return new TradingBot(
|
||||||
|
_exchangeService,
|
||||||
|
_tradingBotLogger,
|
||||||
|
_tradingService,
|
||||||
|
_accountService,
|
||||||
|
_messengerService,
|
||||||
|
_botService,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy methods for backward compatibility - will be deprecated
|
||||||
ITradingBot IBotFactory.CreateScalpingBot(TradingBotConfig config)
|
ITradingBot IBotFactory.CreateScalpingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
config.BotType = BotType.ScalpingBot;
|
config.BotType = BotType.ScalpingBot;
|
||||||
return new ScalpingBot(
|
config.FlipPosition = false;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -54,7 +81,8 @@ namespace Managing.Application.Bots.Base
|
|||||||
{
|
{
|
||||||
config.BotType = BotType.ScalpingBot;
|
config.BotType = BotType.ScalpingBot;
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
return new ScalpingBot(
|
config.FlipPosition = false;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -67,7 +95,8 @@ namespace Managing.Application.Bots.Base
|
|||||||
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
config.BotType = BotType.FlippingBot;
|
config.BotType = BotType.FlippingBot;
|
||||||
return new FlippingBot(
|
config.FlipPosition = true;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -81,7 +110,8 @@ namespace Managing.Application.Bots.Base
|
|||||||
{
|
{
|
||||||
config.BotType = BotType.FlippingBot;
|
config.BotType = BotType.FlippingBot;
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
return new FlippingBot(
|
config.FlipPosition = true;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
using Managing.Application.Abstractions;
|
|
||||||
using Managing.Application.Abstractions.Services;
|
|
||||||
using Managing.Domain.Bots;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Application.Bots
|
|
||||||
{
|
|
||||||
public class FlippingBot : TradingBot
|
|
||||||
{
|
|
||||||
public FlippingBot(
|
|
||||||
IExchangeService exchangeService,
|
|
||||||
ILogger<TradingBot> logger,
|
|
||||||
ITradingService tradingService,
|
|
||||||
IAccountService accountService,
|
|
||||||
IMessengerService messengerService,
|
|
||||||
IBotService botService,
|
|
||||||
TradingBotConfig config)
|
|
||||||
: base(exchangeService, logger, tradingService, accountService, messengerService, botService, config)
|
|
||||||
{
|
|
||||||
Config.BotType = BotType.FlippingBot;
|
|
||||||
Config.FlipPosition = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed override void Start()
|
|
||||||
{
|
|
||||||
Logger.LogInformation($"{Name} - Bot Started");
|
|
||||||
base.Start();
|
|
||||||
Logger.LogInformation($"Starting {Name} bot - Status : {Status}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using Managing.Application.Abstractions;
|
|
||||||
using Managing.Application.Abstractions.Services;
|
|
||||||
using Managing.Domain.Bots;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Application.Bots
|
|
||||||
{
|
|
||||||
public class ScalpingBot : TradingBot
|
|
||||||
{
|
|
||||||
public ScalpingBot(
|
|
||||||
IExchangeService exchangeService,
|
|
||||||
ILogger<TradingBot> logger,
|
|
||||||
ITradingService tradingService,
|
|
||||||
IAccountService accountService,
|
|
||||||
IMessengerService messengerService,
|
|
||||||
IBotService botService,
|
|
||||||
TradingBotConfig config)
|
|
||||||
: base(exchangeService, logger, tradingService, accountService, messengerService, botService, config)
|
|
||||||
{
|
|
||||||
Config.BotType = BotType.ScalpingBot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed override void Start()
|
|
||||||
{
|
|
||||||
Logger.LogInformation($"{Name} - Bot Started");
|
|
||||||
base.Start();
|
|
||||||
Logger.LogInformation($"Starting {Name} bot - Status : {Status}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -471,51 +471,74 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
// Check time-based position management (only if MaxPositionTimeHours is set)
|
// Check time-based position management (only if MaxPositionTimeHours is set)
|
||||||
if (Config.MaxPositionTimeHours.HasValue)
|
if (Config.MaxPositionTimeHours.HasValue)
|
||||||
{
|
{
|
||||||
var isPositionInProfit = await IsPositionInProfit(positionForSignal, lastCandle.Close);
|
|
||||||
var isAtBreakeven = Math.Abs(lastCandle.Close - positionForSignal.Open.Price) < 0.01m; // Small tolerance for breakeven
|
|
||||||
var hasExceededTimeLimit = HasPositionExceededTimeLimit(positionForSignal, currentTime);
|
var hasExceededTimeLimit = HasPositionExceededTimeLimit(positionForSignal, currentTime);
|
||||||
|
|
||||||
// Calculate current unrealized PNL for logging
|
// Calculate current unrealized PNL for logging
|
||||||
var currentPnl = CalculateUnrealizedPnl(positionForSignal, lastCandle.Close);
|
var currentPnl = CalculateUnrealizedPnl(positionForSignal, lastCandle.Close);
|
||||||
var pnlPercentage = Math.Round((currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100, 2);
|
var pnlPercentage =
|
||||||
|
Math.Round(
|
||||||
|
(currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100, 2);
|
||||||
|
|
||||||
// Early closure logic when CloseEarlyWhenProfitable is enabled
|
// Early closure logic when CloseEarlyWhenProfitable is enabled
|
||||||
if (Config.CloseEarlyWhenProfitable && (isPositionInProfit || isAtBreakeven))
|
if (Config.CloseEarlyWhenProfitable)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
var isPositionInProfit = await IsPositionInProfit(positionForSignal, lastCandle.Close);
|
||||||
$"Closing position early due to profitability - Position opened at {positionForSignal.Open.Date}, " +
|
var isAtBreakeven =
|
||||||
$"current time {currentTime}. Position is {(isPositionInProfit ? "in profit" : "at breakeven")} " +
|
Math.Abs(lastCandle.Close - positionForSignal.Open.Price) <
|
||||||
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
0.01m; // Small tolerance for breakeven
|
||||||
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
|
||||||
$"CloseEarlyWhenProfitable is enabled.");
|
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time limit exceeded logic
|
if (isPositionInProfit || isAtBreakeven)
|
||||||
if (hasExceededTimeLimit)
|
|
||||||
{
|
|
||||||
if (Config.CloseEarlyWhenProfitable || isPositionInProfit || isAtBreakeven)
|
|
||||||
{
|
{
|
||||||
// Close when time limit is reached if:
|
|
||||||
// 1. CloseEarlyWhenProfitable is enabled (safety net), OR
|
|
||||||
// 2. Position is in profit/breakeven (when CloseEarlyWhenProfitable is disabled)
|
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"Closing position due to time limit - Position opened at {positionForSignal.Open.Date}, " +
|
$"Closing position early due to profitability - Position opened at {positionForSignal.Open.Date}, " +
|
||||||
$"current time {currentTime}, max time limit {Config.MaxPositionTimeHours} hours. " +
|
$"current time {currentTime}. Position is {(isPositionInProfit ? "in profit" : "at breakeven")} " +
|
||||||
$"Position is {(isPositionInProfit ? "in profit" : isAtBreakeven ? "at breakeven" : "at a loss")} " +
|
|
||||||
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
||||||
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%)");
|
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
||||||
|
$"CloseEarlyWhenProfitable is enabled.");
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// Time limit exceeded logic when CloseEarlyWhenProfitable is enabled
|
||||||
|
if (hasExceededTimeLimit && (isPositionInProfit || isAtBreakeven))
|
||||||
|
{
|
||||||
|
await LogInformation(
|
||||||
|
$"Closing position due to time limit - Position opened at {positionForSignal.Open.Date}, " +
|
||||||
|
$"current time {currentTime}, max time limit {Config.MaxPositionTimeHours} hours. " +
|
||||||
|
$"Position is {(isPositionInProfit ? "in profit" : "at breakeven")} " +
|
||||||
|
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
||||||
|
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
||||||
|
$"CloseEarlyWhenProfitable is enabled.");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (hasExceededTimeLimit)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"Position has exceeded time limit ({Config.MaxPositionTimeHours} hours) but is at a loss " +
|
$"Position has exceeded time limit ({Config.MaxPositionTimeHours} hours) but is at a loss " +
|
||||||
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
||||||
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
||||||
$"CloseEarlyWhenProfitable is disabled - waiting for profit or breakeven before closing.");
|
$"CloseEarlyWhenProfitable is enabled - waiting for profit or breakeven before closing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Time limit exceeded logic when CloseEarlyWhenProfitable is disabled
|
||||||
|
if (hasExceededTimeLimit)
|
||||||
|
{
|
||||||
|
var isPositionInProfit = await IsPositionInProfit(positionForSignal, lastCandle.Close);
|
||||||
|
var profitStatus = isPositionInProfit ? "in profit" :
|
||||||
|
Math.Abs(lastCandle.Close - positionForSignal.Open.Price) < 0.01m ? "at breakeven" : "at a loss";
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"Closing position due to time limit - Position opened at {positionForSignal.Open.Date}, " +
|
||||||
|
$"current time {currentTime}, max time limit {Config.MaxPositionTimeHours} hours. " +
|
||||||
|
$"Position is {profitStatus} " +
|
||||||
|
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
||||||
|
$"Current PNL: ${currentPnl:F2} ({pnlPercentage:F2}%). " +
|
||||||
|
$"CloseEarlyWhenProfitable is disabled - closing regardless of profit status.");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -665,7 +688,6 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
$"Position {previousSignal.Identifier} is not in profit (entry: {openedPosition.Open.Price}, current: {lastPrice}). " +
|
$"Position {previousSignal.Identifier} is not in profit (entry: {openedPosition.Open.Price}, current: {lastPrice}). " +
|
||||||
$"Signal {signal.Identifier} will wait for position to become profitable before flipping.");
|
$"Signal {signal.Identifier} will wait for position to become profitable before flipping.");
|
||||||
|
|
||||||
// Keep signal in waiting status to check again on next execution
|
|
||||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1046,7 +1068,7 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
private void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus)
|
private void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus)
|
||||||
{
|
{
|
||||||
if (Signals.Any(s => s.Identifier == signalIdentifier))
|
if (Signals.Any(s => s.Identifier == signalIdentifier && s.Status != signalStatus))
|
||||||
{
|
{
|
||||||
Signals.First(s => s.Identifier == signalIdentifier).Status = signalStatus;
|
Signals.First(s => s.Identifier == signalIdentifier).Status = signalStatus;
|
||||||
Logger.LogInformation($"Signal {signalIdentifier} is now {signalStatus}");
|
Logger.LogInformation($"Signal {signalIdentifier} is now {signalStatus}");
|
||||||
@@ -1360,11 +1382,11 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
// Log the configuration update
|
// Log the configuration update
|
||||||
await LogInformation($"Updating bot configuration. Previous config: " +
|
await LogInformation($"Updating bot configuration. Previous config: " +
|
||||||
$"Balance: {Config.BotTradingBalance}, " +
|
$"Balance: {Config.BotTradingBalance}, " +
|
||||||
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||||
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
||||||
$"Cooldown: {Config.CooldownPeriod}, " +
|
$"Cooldown: {Config.CooldownPeriod}, " +
|
||||||
$"MaxLoss: {Config.MaxLossStreak}");
|
$"MaxLoss: {Config.MaxLossStreak}");
|
||||||
|
|
||||||
// Update the configuration
|
// Update the configuration
|
||||||
Config = newConfig;
|
Config = newConfig;
|
||||||
@@ -1388,11 +1410,11 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation($"Bot configuration updated successfully. New config: " +
|
await LogInformation($"Bot configuration updated successfully. New config: " +
|
||||||
$"Balance: {Config.BotTradingBalance}, " +
|
$"Balance: {Config.BotTradingBalance}, " +
|
||||||
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||||
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
||||||
$"Cooldown: {Config.CooldownPeriod}, " +
|
$"Cooldown: {Config.CooldownPeriod}, " +
|
||||||
$"MaxLoss: {Config.MaxLossStreak}");
|
$"MaxLoss: {Config.MaxLossStreak}");
|
||||||
|
|
||||||
// Save the updated configuration as backup
|
// Save the updated configuration as backup
|
||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace Managing.Application.ManageBot
|
|||||||
CloseEarlyWhenProfitable = scalpingBotData.CloseEarlyWhenProfitable
|
CloseEarlyWhenProfitable = scalpingBotData.CloseEarlyWhenProfitable
|
||||||
};
|
};
|
||||||
|
|
||||||
bot = CreateScalpingBot(scalpingConfig);
|
bot = CreateTradingBot(scalpingConfig);
|
||||||
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ namespace Managing.Application.ManageBot
|
|||||||
CloseEarlyWhenProfitable = flippingBotData.CloseEarlyWhenProfitable
|
CloseEarlyWhenProfitable = flippingBotData.CloseEarlyWhenProfitable
|
||||||
};
|
};
|
||||||
|
|
||||||
bot = CreateFlippingBot(flippingConfig);
|
bot = CreateTradingBot(flippingConfig);
|
||||||
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -282,9 +282,35 @@ namespace Managing.Application.ManageBot
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ITradingBot CreateTradingBot(TradingBotConfig config)
|
||||||
|
{
|
||||||
|
return new TradingBot(
|
||||||
|
_exchangeService,
|
||||||
|
_tradingBotLogger,
|
||||||
|
_tradingService,
|
||||||
|
_accountService,
|
||||||
|
_messengerService,
|
||||||
|
this,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITradingBot CreateBacktestTradingBot(TradingBotConfig config)
|
||||||
|
{
|
||||||
|
config.IsForBacktest = true;
|
||||||
|
return new TradingBot(
|
||||||
|
_exchangeService,
|
||||||
|
_tradingBotLogger,
|
||||||
|
_tradingService,
|
||||||
|
_accountService,
|
||||||
|
_messengerService,
|
||||||
|
this,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
public ITradingBot CreateScalpingBot(TradingBotConfig config)
|
public ITradingBot CreateScalpingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
return new ScalpingBot(
|
config.FlipPosition = false;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -297,7 +323,8 @@ namespace Managing.Application.ManageBot
|
|||||||
public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config)
|
public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
return new ScalpingBot(
|
config.FlipPosition = false;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -309,7 +336,8 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
return new FlippingBot(
|
config.FlipPosition = true;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
@@ -322,7 +350,8 @@ namespace Managing.Application.ManageBot
|
|||||||
public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
|
public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
return new FlippingBot(
|
config.FlipPosition = true;
|
||||||
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
_tradingService,
|
_tradingService,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace Managing.Application.ManageBot
|
|||||||
MaxLossStreak = request.Config.MaxLossStreak,
|
MaxLossStreak = request.Config.MaxLossStreak,
|
||||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours, // Properly handle nullable value
|
MaxPositionTimeHours = request.Config.MaxPositionTimeHours, // Properly handle nullable value
|
||||||
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
|
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
|
||||||
FlipPosition = request.Config.FlipPosition,
|
FlipPosition = request.Config.BotType == BotType.FlippingBot, // Set FlipPosition based on BotType
|
||||||
Name = request.Config.Name ?? request.Name,
|
Name = request.Config.Name ?? request.Name,
|
||||||
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
|
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
|
||||||
};
|
};
|
||||||
@@ -87,24 +87,15 @@ namespace Managing.Application.ManageBot
|
|||||||
return bot.GetStatus();
|
return bot.GetStatus();
|
||||||
|
|
||||||
case BotType.ScalpingBot:
|
case BotType.ScalpingBot:
|
||||||
var sBot = _botFactory.CreateScalpingBot(configToUse);
|
|
||||||
sBot.User = request.User;
|
|
||||||
|
|
||||||
// Log the configuration being used
|
|
||||||
await LogBotConfigurationAsync(sBot, "ScalpingBot created");
|
|
||||||
|
|
||||||
_botService.AddTradingBotToCache(sBot);
|
|
||||||
return sBot.GetStatus();
|
|
||||||
|
|
||||||
case BotType.FlippingBot:
|
case BotType.FlippingBot:
|
||||||
var fBot = _botFactory.CreateFlippingBot(configToUse);
|
var tradingBot = _botFactory.CreateTradingBot(configToUse);
|
||||||
fBot.User = request.User;
|
tradingBot.User = request.User;
|
||||||
|
|
||||||
// Log the configuration being used
|
// Log the configuration being used
|
||||||
await LogBotConfigurationAsync(fBot, "FlippingBot created");
|
await LogBotConfigurationAsync(tradingBot, $"{configToUse.BotType} created");
|
||||||
|
|
||||||
_botService.AddTradingBotToCache(fBot);
|
_botService.AddTradingBotToCache(tradingBot);
|
||||||
return fBot.GetStatus();
|
return tradingBot.GetStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
return botStatus.ToString();
|
return botStatus.ToString();
|
||||||
@@ -127,6 +118,7 @@ namespace Managing.Application.ManageBot
|
|||||||
$"Balance: {config.BotTradingBalance}, " +
|
$"Balance: {config.BotTradingBalance}, " +
|
||||||
$"MaxTime: {config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
$"MaxTime: {config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||||
$"FlipOnlyProfit: {config.FlipOnlyWhenInProfit}, " +
|
$"FlipOnlyProfit: {config.FlipOnlyWhenInProfit}, " +
|
||||||
|
$"FlipPosition: {config.FlipPosition}, " +
|
||||||
$"Cooldown: {config.CooldownPeriod}, " +
|
$"Cooldown: {config.CooldownPeriod}, " +
|
||||||
$"MaxLoss: {config.MaxLossStreak}";
|
$"MaxLoss: {config.MaxLossStreak}";
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ public static class WorkersBootstrap
|
|||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
services.AddTransient<ICandleRepository, CandleRepository>();
|
services.AddTransient<ICandleRepository, CandleRepository>();
|
||||||
|
services.AddTransient<IAgentBalanceRepository, AgentBalanceRepository>();
|
||||||
services.AddTransient<IWorkerRepository, WorkerRepository>();
|
services.AddTransient<IWorkerRepository, WorkerRepository>();
|
||||||
services.AddTransient<IStatisticRepository, StatisticRepository>();
|
services.AddTransient<IStatisticRepository, StatisticRepository>();
|
||||||
services.AddTransient<ICandleRepository, CandleRepository>();
|
services.AddTransient<ICandleRepository, CandleRepository>();
|
||||||
|
|||||||
Reference in New Issue
Block a user