Send notif on good backtest
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Backtests;
|
||||||
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
using static Managing.Common.Enums;
|
using static Managing.Common.Enums;
|
||||||
@@ -22,4 +23,5 @@ public interface IMessengerService
|
|||||||
Task SendDowngradedFundingRate(FundingRate oldRate);
|
Task SendDowngradedFundingRate(FundingRate oldRate);
|
||||||
Task SendNewTopFundingRate(FundingRate newRate);
|
Task SendNewTopFundingRate(FundingRate newRate);
|
||||||
Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate);
|
Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate);
|
||||||
|
Task SendBacktestNotification(Backtest backtest);
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,7 @@ namespace Managing.Application.Backtesting
|
|||||||
private readonly IBotFactory _botFactory;
|
private readonly IBotFactory _botFactory;
|
||||||
private readonly IScenarioService _scenarioService;
|
private readonly IScenarioService _scenarioService;
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
|
private readonly IMessengerService _messengerService;
|
||||||
|
|
||||||
public Backtester(
|
public Backtester(
|
||||||
IExchangeService exchangeService,
|
IExchangeService exchangeService,
|
||||||
@@ -33,7 +34,8 @@ namespace Managing.Application.Backtesting
|
|||||||
IBacktestRepository backtestRepository,
|
IBacktestRepository backtestRepository,
|
||||||
ILogger<Backtester> logger,
|
ILogger<Backtester> logger,
|
||||||
IScenarioService scenarioService,
|
IScenarioService scenarioService,
|
||||||
IAccountService accountService)
|
IAccountService accountService,
|
||||||
|
IMessengerService messengerService)
|
||||||
{
|
{
|
||||||
_exchangeService = exchangeService;
|
_exchangeService = exchangeService;
|
||||||
_botFactory = botFactory;
|
_botFactory = botFactory;
|
||||||
@@ -41,6 +43,7 @@ namespace Managing.Application.Backtesting
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_scenarioService = scenarioService;
|
_scenarioService = scenarioService;
|
||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
|
_messengerService = messengerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false)
|
public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false)
|
||||||
@@ -133,7 +136,7 @@ namespace Managing.Application.Backtesting
|
|||||||
tradingBot.User = user;
|
tradingBot.User = user;
|
||||||
await tradingBot.LoadAccount();
|
await tradingBot.LoadAccount();
|
||||||
|
|
||||||
var result = GetBacktestingResult(config, tradingBot, candles, withCandles);
|
var result = await GetBacktestingResult(config, tradingBot, candles, user, withCandles);
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
@@ -170,10 +173,11 @@ namespace Managing.Application.Backtesting
|
|||||||
return candles;
|
return candles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Backtest GetBacktestingResult(
|
private async Task<Backtest> GetBacktestingResult(
|
||||||
TradingBotConfig config,
|
TradingBotConfig config,
|
||||||
ITradingBot bot,
|
ITradingBot bot,
|
||||||
List<Candle> candles,
|
List<Candle> candles,
|
||||||
|
User user = null,
|
||||||
bool withCandles = false)
|
bool withCandles = false)
|
||||||
{
|
{
|
||||||
if (candles == null || candles.Count == 0)
|
if (candles == null || candles.Count == 0)
|
||||||
@@ -248,7 +252,8 @@ namespace Managing.Application.Backtesting
|
|||||||
var score = BacktestScorer.CalculateTotalScore(scoringParams);
|
var score = BacktestScorer.CalculateTotalScore(scoringParams);
|
||||||
|
|
||||||
// Create backtest result with conditional candles and indicators values
|
// Create backtest result with conditional candles and indicators values
|
||||||
var result = new Backtest(config, bot.Positions, bot.Signals.ToList(), withCandles ? candles : new List<Candle>())
|
var result = new Backtest(config, bot.Positions, bot.Signals.ToList(),
|
||||||
|
withCandles ? candles : new List<Candle>())
|
||||||
{
|
{
|
||||||
FinalPnl = finalPnl,
|
FinalPnl = finalPnl,
|
||||||
WinRate = winRate,
|
WinRate = winRate,
|
||||||
@@ -258,14 +263,52 @@ namespace Managing.Application.Backtesting
|
|||||||
WalletBalances = bot.WalletBalances.ToList(),
|
WalletBalances = bot.WalletBalances.ToList(),
|
||||||
Statistics = stats,
|
Statistics = stats,
|
||||||
OptimizedMoneyManagement = optimizedMoneyManagement,
|
OptimizedMoneyManagement = optimizedMoneyManagement,
|
||||||
IndicatorsValues = withCandles ? AggregateValues(indicatorsValues, bot.IndicatorsValues) : new Dictionary<IndicatorType, IndicatorsResultBase>(),
|
IndicatorsValues = withCandles
|
||||||
|
? AggregateValues(indicatorsValues, bot.IndicatorsValues)
|
||||||
|
: new Dictionary<IndicatorType, IndicatorsResultBase>(),
|
||||||
Score = score,
|
Score = score,
|
||||||
Id = Guid.NewGuid().ToString()
|
Id = Guid.NewGuid().ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Send notification if backtest meets criteria
|
||||||
|
await SendBacktestNotificationIfCriteriaMet(result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SendBacktestNotificationIfCriteriaMet(Backtest backtest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if backtest meets criteria: score > 85, at least 5 positions, winrate > 65%, risk-reward >= 1.5:1
|
||||||
|
var score = backtest.Score;
|
||||||
|
var tradeCount = backtest.Positions?.Count ?? 0;
|
||||||
|
var winRate = backtest.WinRate;
|
||||||
|
|
||||||
|
// Calculate risk-reward ratio from money management settings
|
||||||
|
var riskRewardRatio = 0.0;
|
||||||
|
if (backtest.Config.MoneyManagement != null)
|
||||||
|
{
|
||||||
|
var stopLoss = (double)backtest.Config.MoneyManagement.StopLoss;
|
||||||
|
var takeProfit = (double)backtest.Config.MoneyManagement.TakeProfit;
|
||||||
|
|
||||||
|
if (stopLoss > 0 && takeProfit > 0)
|
||||||
|
{
|
||||||
|
riskRewardRatio = takeProfit / stopLoss;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score > 85 && tradeCount >= 5 && winRate > 65 && riskRewardRatio >= 1.5)
|
||||||
|
{
|
||||||
|
await _messengerService.SendBacktestNotification(backtest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to send backtest notification for backtest {Id}", backtest.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<IndicatorType, IndicatorsResultBase> AggregateValues(
|
private Dictionary<IndicatorType, IndicatorsResultBase> AggregateValues(
|
||||||
Dictionary<IndicatorType, IndicatorsResultBase> indicatorsValues,
|
Dictionary<IndicatorType, IndicatorsResultBase> indicatorsValues,
|
||||||
Dictionary<IndicatorType, IndicatorsResultBase> botStrategiesValues)
|
Dictionary<IndicatorType, IndicatorsResultBase> botStrategiesValues)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Common;
|
using Managing.Common;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -155,4 +156,111 @@ public class MessengerService : IMessengerService
|
|||||||
{
|
{
|
||||||
await _discordService.SendFundingRateUpdate(oldRate, newRate);
|
await _discordService.SendFundingRateUpdate(oldRate, newRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendBacktestNotification(Backtest backtest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configMessage = BuildBacktestConfigMessage(backtest);
|
||||||
|
var resultsMessage = BuildBacktestResultsMessage(backtest);
|
||||||
|
|
||||||
|
// Send configuration message first
|
||||||
|
await _webhookService.SendMessage(configMessage, "2775292276");
|
||||||
|
|
||||||
|
// Send results message second
|
||||||
|
await _webhookService.SendMessage(resultsMessage, "2775292276");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to send backtest notification: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildBacktestConfigMessage(Backtest backtest)
|
||||||
|
{
|
||||||
|
var config = backtest.Config;
|
||||||
|
|
||||||
|
// Get indicators list as comma-separated string
|
||||||
|
var indicators = config.Scenario?.Indicators != null && config.Scenario.Indicators.Any()
|
||||||
|
? string.Join(", ", config.Scenario.Indicators.Select(i => i.Type.ToString()))
|
||||||
|
: "N/A";
|
||||||
|
|
||||||
|
// MoneyManagement summary
|
||||||
|
var mmSl = config.MoneyManagement != null ? (config.MoneyManagement.StopLoss * 100).ToString("F2") : "N/A";
|
||||||
|
var mmTp = config.MoneyManagement != null ? (config.MoneyManagement.TakeProfit * 100).ToString("F2") : "N/A";
|
||||||
|
var mmLev = config.MoneyManagement != null ? config.MoneyManagement.Leverage.ToString("F2") : "N/A";
|
||||||
|
|
||||||
|
return $"🚀 Excellent Backtest Results! 🚀\n\n" +
|
||||||
|
$"🔹 {config.Ticker} | ⏱️ {config.Timeframe}\n" +
|
||||||
|
$"💰 {config.BotTradingBalance:C}\n" +
|
||||||
|
$"🛡️ SL: {mmSl}% | 🎯 TP: {mmTp}% | 📈 Lev: {mmLev}x\n" +
|
||||||
|
$"🧩 {indicators}\n" +
|
||||||
|
$"📅 {backtest.StartDate:yyyy-MM-dd} to {backtest.EndDate:yyyy-MM-dd}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildBacktestResultsMessage(Backtest backtest)
|
||||||
|
{
|
||||||
|
var config = backtest.Config;
|
||||||
|
var score = backtest.Score;
|
||||||
|
var winRate = backtest.WinRate;
|
||||||
|
var tradeCount = backtest.Positions?.Count ?? 0;
|
||||||
|
var finalPnl = backtest.FinalPnl;
|
||||||
|
var growthPercentage = backtest.GrowthPercentage;
|
||||||
|
var maxDrawdown = backtest.Statistics?.MaxDrawdownPc ?? 0;
|
||||||
|
var sharpeRatio = backtest.Statistics?.SharpeRatio ?? 0;
|
||||||
|
|
||||||
|
return $"📈 Performance Metrics:\n" +
|
||||||
|
$"⭐ Score: {score:F1}/100 | 🏆 Win Rate: {winRate:F1}%\n" +
|
||||||
|
$"📊 Trades: {tradeCount} | 💰 PnL: ${finalPnl:F2}\n" +
|
||||||
|
$"📈 Growth: {growthPercentage:F1}% | 📉 DD: {maxDrawdown:F1}%\n" +
|
||||||
|
$"📊 Sharpe: {sharpeRatio:F2}\n\n" +
|
||||||
|
$"🆔 ID: {backtest.Id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildBacktestMessage(Backtest backtest)
|
||||||
|
{
|
||||||
|
var config = backtest.Config;
|
||||||
|
var score = backtest.Score;
|
||||||
|
var winRate = backtest.WinRate;
|
||||||
|
var tradeCount = backtest.Positions?.Count ?? 0;
|
||||||
|
var finalPnl = backtest.FinalPnl;
|
||||||
|
var growthPercentage = backtest.GrowthPercentage;
|
||||||
|
var maxDrawdown = backtest.Statistics?.MaxDrawdownPc ?? 0;
|
||||||
|
var sharpeRatio = backtest.Statistics?.SharpeRatio ?? 0;
|
||||||
|
|
||||||
|
// Get indicators list as comma-separated string
|
||||||
|
var indicators = config.Scenario?.Indicators != null && config.Scenario.Indicators.Any()
|
||||||
|
? string.Join(", ", config.Scenario.Indicators.Select(i => i.Name ?? i.Type.ToString()))
|
||||||
|
: "N/A";
|
||||||
|
|
||||||
|
// MoneyManagement summary
|
||||||
|
var mmSl = config.MoneyManagement != null ? config.MoneyManagement.StopLoss.ToString("F2") : "N/A";
|
||||||
|
var mmTp = config.MoneyManagement != null ? config.MoneyManagement.TakeProfit.ToString("F2") : "N/A";
|
||||||
|
var mmLev = config.MoneyManagement != null ? config.MoneyManagement.Leverage.ToString("F2") : "N/A";
|
||||||
|
|
||||||
|
var message = $"🚀 Excellent Backtest Results! 🚀\n\n" +
|
||||||
|
$"📊 Configuration:\n" +
|
||||||
|
$"🔹 Symbol: {config.Ticker}\n" +
|
||||||
|
$"⏱️ Timeframe: {config.Timeframe}\n" +
|
||||||
|
$"👤 Account: {config.AccountName}\n" +
|
||||||
|
$"💼 Money Management: 🛡️ SL: {mmSl}% | 🎯 TP: {mmTp}% | 📈 Lev: {mmLev}x\n" +
|
||||||
|
$"💰 Balance: {config.BotTradingBalance}\n" +
|
||||||
|
$"🧩 Indicators: {indicators}\n" +
|
||||||
|
$"📅 Period: {backtest.StartDate:yyyy-MM-dd} to {backtest.EndDate:yyyy-MM-dd}\n" +
|
||||||
|
$"⏳ Cooldown: {config.CooldownPeriod} | 🔥 Max Loss Streak: {config.MaxLossStreak}\n" +
|
||||||
|
$"🔄 Flip Position: {(config.FlipPosition ? "Yes" : "No")} | 🔒 Flip Only When In Profit: {(config.FlipOnlyWhenInProfit ? "Yes" : "No")}\n" +
|
||||||
|
$"👀 Watching Only: {(config.IsForWatchingOnly ? "Yes" : "No")} | 🧪 Backtest Mode: {(config.IsForBacktest ? "Yes" : "No")}\n" +
|
||||||
|
$"⏰ Max Position Time (hrs): {(config.MaxPositionTimeHours?.ToString() ?? "N/A")} | 🏁 Close Early When Profitable: {(config.CloseEarlyWhenProfitable ? "Yes" : "No")}\n" +
|
||||||
|
$"📈 Performance Metrics:\n" +
|
||||||
|
$"⭐ Score: {score:F1}/100\n" +
|
||||||
|
$"🏆 Win Rate: {winRate:F1}%\n" +
|
||||||
|
$"📊 Total Trades: {tradeCount}\n" +
|
||||||
|
$"💰 Final PnL: ${finalPnl:F2}\n" +
|
||||||
|
$"📈 Growth: {growthPercentage:F1}%\n" +
|
||||||
|
$"📉 Max Drawdown: {maxDrawdown:F1}%\n" +
|
||||||
|
$"📊 Sharpe Ratio: {sharpeRatio:F2}\n\n" +
|
||||||
|
$"🆔 Backtest ID: {backtest.Id}";
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user