129 lines
5.5 KiB
C#
129 lines
5.5 KiB
C#
#nullable enable
|
|
using System.ComponentModel.DataAnnotations;
|
|
using Exilion.TradingAtomics;
|
|
using Managing.Domain.Bots;
|
|
using Managing.Domain.Indicators;
|
|
using Managing.Domain.Trades;
|
|
using Managing.Domain.Users;
|
|
|
|
namespace Managing.Domain.Backtests;
|
|
|
|
public class Backtest
|
|
{
|
|
public Backtest(
|
|
TradingBotConfig config,
|
|
Dictionary<Guid, Position> positions,
|
|
Dictionary<string, LightSignal> signals)
|
|
{
|
|
Config = config;
|
|
Positions = positions;
|
|
Signals = signals;
|
|
WalletBalances = new List<KeyValuePair<DateTime, decimal>>();
|
|
}
|
|
|
|
[Required] public string Id { get; set; }
|
|
[Required] public decimal FinalPnl { get; set; }
|
|
[Required] public int WinRate { get; set; }
|
|
[Required] public decimal GrowthPercentage { get; set; }
|
|
[Required] public decimal HodlPercentage { get; set; }
|
|
[Required] public TradingBotConfig Config { get; }
|
|
[Required] public Dictionary<Guid, Position> Positions { get; }
|
|
[Required] public Dictionary<string, LightSignal> Signals { get; }
|
|
[Required] public DateTime StartDate { get; set; }
|
|
[Required] public DateTime EndDate { get; set; }
|
|
[Required] public PerformanceMetrics Statistics { get; set; }
|
|
[Required] public decimal Fees { get; set; }
|
|
[Required] public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
|
|
[Required] public User User { get; set; }
|
|
[Required] public double Score { get; set; }
|
|
public Guid RequestId { get; set; }
|
|
public object? Metadata { get; set; }
|
|
public string ScoreMessage { get; set; } = string.Empty;
|
|
[Required] public decimal InitialBalance { get; set; }
|
|
[Required] public decimal NetPnl { get; set; }
|
|
|
|
/// <summary>
|
|
/// Creates a new TradingBotConfig based on this backtest's configuration for starting a live bot.
|
|
/// This method ensures all properties are properly copied, including nullable values.
|
|
/// </summary>
|
|
/// <param name="accountName">The account name to use for the new bot (can override backtest account)</param>
|
|
/// <param name="botName">The name for the new bot</param>
|
|
/// <param name="initialTradingBalance">The initial trading balance for the new bot</param>
|
|
/// <param name="moneyManagement">Optional money management override (uses backtest's if not provided)</param>
|
|
/// <returns>A new TradingBotConfig ready for bot creation</returns>
|
|
public TradingBotConfig CreateLiveBotConfig(
|
|
string accountName,
|
|
string botName,
|
|
decimal initialTradingBalance,
|
|
LightMoneyManagement moneyManagement = null)
|
|
{
|
|
return new TradingBotConfig
|
|
{
|
|
AccountName = accountName,
|
|
MoneyManagement = moneyManagement ?? Config.MoneyManagement,
|
|
Ticker = Config.Ticker,
|
|
ScenarioName = Config.ScenarioName,
|
|
Timeframe = Config.Timeframe,
|
|
IsForWatchingOnly = false, // Always start as active bot
|
|
BotTradingBalance = initialTradingBalance,
|
|
IsForBacktest = false, // Always false for live bots
|
|
CooldownPeriod = Config.CooldownPeriod,
|
|
MaxLossStreak = Config.MaxLossStreak,
|
|
MaxPositionTimeHours = Config.MaxPositionTimeHours, // Properly copy nullable value
|
|
FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
|
|
FlipPosition = Config.FlipPosition,
|
|
Name = botName
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a copy of the backtest's configuration for a new backtest.
|
|
/// Useful for running similar backtests with modified parameters.
|
|
/// </summary>
|
|
/// <param name="startDate">Start date for the new backtest</param>
|
|
/// <param name="endDate">End date for the new backtest</param>
|
|
/// <param name="balance">Initial balance for the new backtest</param>
|
|
/// <param name="moneyManagement">Optional money management override</param>
|
|
/// <returns>A new TradingBotConfig for backtesting</returns>
|
|
public TradingBotConfig CreateBacktestConfig(
|
|
DateTime startDate,
|
|
DateTime endDate,
|
|
decimal balance,
|
|
LightMoneyManagement moneyManagement = null)
|
|
{
|
|
return new TradingBotConfig
|
|
{
|
|
AccountName = Config.AccountName,
|
|
MoneyManagement = moneyManagement ?? Config.MoneyManagement,
|
|
Ticker = Config.Ticker,
|
|
ScenarioName = Config.ScenarioName,
|
|
Timeframe = Config.Timeframe,
|
|
IsForWatchingOnly = Config.IsForWatchingOnly,
|
|
BotTradingBalance = balance,
|
|
IsForBacktest = true,
|
|
CooldownPeriod = Config.CooldownPeriod,
|
|
MaxLossStreak = Config.MaxLossStreak,
|
|
MaxPositionTimeHours = Config.MaxPositionTimeHours,
|
|
FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
|
|
FlipPosition = Config.FlipPosition,
|
|
Name = Config.Name
|
|
};
|
|
}
|
|
|
|
public string GetStringReport()
|
|
{
|
|
var timeBasedInfo = Config.MaxPositionTimeHours.HasValue
|
|
? $" | MaxTime: {Config.MaxPositionTimeHours}h"
|
|
: " | MaxTime: Disabled";
|
|
|
|
var flipInfo = Config.FlipPosition
|
|
? $" | Flip: {(Config.FlipOnlyWhenInProfit ? "Profit-Only" : "Always")}"
|
|
: "";
|
|
|
|
return
|
|
$"{Config.Ticker} | {Config.Timeframe} | Positions: {Positions.Count} | Winrate: {WinRate}% | " +
|
|
$"Pnl: {FinalPnl:#.##}$ | %Pnl: {GrowthPercentage:#.##}% | %Hodl: {HodlPercentage:#.##}%" +
|
|
$" | Cooldown: {Config.CooldownPeriod} | MaxLoss: {Config.MaxLossStreak}" +
|
|
timeBasedInfo + flipInfo;
|
|
}
|
|
} |