Add startuptime and update creationDate

This commit is contained in:
2025-07-09 18:30:23 +07:00
parent 387948a107
commit 0c1184a22d
11 changed files with 85 additions and 38 deletions

View File

@@ -505,7 +505,9 @@ public class BotController : BaseController
ProfitAndLoss = item.GetProfitAndLoss(), ProfitAndLoss = item.GetProfitAndLoss(),
Identifier = item.Identifier, Identifier = item.Identifier,
AgentName = item.User.AgentName, AgentName = item.User.AgentName,
Config = item.Config // Contains all configuration properties Config = item.Config,
CreateDate = item.CreateDate,
StartupTime = item.StartupTime
}); });
} }

View File

@@ -52,5 +52,15 @@ namespace Managing.Api.Models.Responses
/// The full trading bot configuration /// The full trading bot configuration
/// </summary> /// </summary>
[Required] public TradingBotConfig Config { get; internal set; } [Required] public TradingBotConfig Config { get; internal set; }
/// <summary>
/// The time when the bot was created
/// </summary>
[Required] public DateTime CreateDate { get; internal set; }
/// <summary>
/// The time when the bot was started
/// </summary>
[Required] public DateTime StartupTime { get; internal set; }
} }
} }

View File

@@ -21,6 +21,7 @@ namespace Managing.Application.Abstractions
Dictionary<DateTime, decimal> WalletBalances { get; set; } Dictionary<DateTime, decimal> WalletBalances { get; set; }
Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; } Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; }
DateTime StartupTime { get; set; } DateTime StartupTime { get; set; }
DateTime CreateDate { get; }
DateTime PreloadSince { get; set; } DateTime PreloadSince { get; set; }
int PreloadedCandlesCount { get; set; } int PreloadedCandlesCount { get; set; }
decimal Fee { get; set; } decimal Fee { get; set; }

View File

@@ -114,7 +114,12 @@ public class TradingBot : Bot, ITradingBot
CancelAllOrders().GetAwaiter().GetResult(); CancelAllOrders().GetAwaiter().GetResult();
// Send startup message only for fresh starts (not reboots) // Send startup message only for fresh starts (not reboots)
var isReboot = Signals.Any() || Positions.Any(); // Consider it a reboot if the bot was created more than 5 minutes ago
var timeSinceCreation = DateTime.UtcNow - CreateDate;
var isReboot = timeSinceCreation.TotalMinutes > 5;
StartupTime = DateTime.UtcNow;
if (!isReboot) if (!isReboot)
{ {
try try
@@ -361,38 +366,37 @@ public class TradingBot : Bot, ITradingBot
// TODO : remove this when Synth is stable // TODO : remove this when Synth is stable
// signal.Status = SignalStatus.Expired; // signal.Status = SignalStatus.Expired;
signalText += $"\n\n🚫 *Synth Signal Filter*\n" + signalText += $"\n\n🚫 *Synth Signal Filter*\n" +
$"Signal `{signal.Identifier}` blocked by Synth risk assessment\n\n" + $"Signal `{signal.Identifier}` blocked by Synth risk assessment\n\n" +
$"📊 *Risk Analysis Details*\n" + $"📊 *Risk Analysis Details*\n" +
$"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" + $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
$"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" + $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
$"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" + $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
$"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" + $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
$"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" + $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
$"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" + $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
$"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" + $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
$"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" + $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
$"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" + $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
$"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" + $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
$"📋 *Context*\n`{signalValidationResult.ValidationContext}`"; $"📋 *Context*\n`{signalValidationResult.ValidationContext}`";
} }
else else
{ {
signal.SetConfidence(signalValidationResult.Confidence); signal.SetConfidence(signalValidationResult.Confidence);
signalText += $"\n\n✅ *Synth Risk Assessment Passed*\n" + signalText += $"\n\n✅ *Synth Risk Assessment Passed*\n" +
$"Confidence: `{signalValidationResult.Confidence}`\n\n" + $"Confidence: `{signalValidationResult.Confidence}`\n\n" +
$"📊 *Risk Analysis Details*\n" + $"📊 *Risk Analysis Details*\n" +
$"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" + $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
$"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" + $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
$"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" + $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
$"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" + $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
$"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" + $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
$"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" + $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
$"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" + $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
$"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" + $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
$"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" + $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
$"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" + $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
$"📋 *Context*\n`{signalValidationResult.ValidationContext}`"; $"📋 *Context*\n`{signalValidationResult.ValidationContext}`";
} }
} }
@@ -1429,7 +1433,8 @@ public class TradingBot : Bot, ITradingBot
Signals = Signals, Signals = Signals,
Positions = Positions, Positions = Positions,
WalletBalances = WalletBalances, WalletBalances = WalletBalances,
StartupTime = StartupTime StartupTime = StartupTime,
CreateDate = CreateDate
}; };
BotService.SaveOrUpdateBotBackup(User, Identifier, Status, JsonConvert.SerializeObject(data)); BotService.SaveOrUpdateBotBackup(User, Identifier, Status, JsonConvert.SerializeObject(data));
} }
@@ -1449,6 +1454,7 @@ public class TradingBot : Bot, ITradingBot
Positions = data.Positions ?? new List<Position>(); Positions = data.Positions ?? new List<Position>();
WalletBalances = data.WalletBalances ?? new Dictionary<DateTime, decimal>(); WalletBalances = data.WalletBalances ?? new Dictionary<DateTime, decimal>();
PreloadSince = data.StartupTime; PreloadSince = data.StartupTime;
CreateDate = data.CreateDate;
Identifier = backup.Identifier; Identifier = backup.Identifier;
User = backup.User; User = backup.User;
Status = backup.LastStatus; Status = backup.LastStatus;
@@ -1718,7 +1724,6 @@ public class TradingBot : Bot, ITradingBot
if (allowNameChange && !string.IsNullOrEmpty(newConfig.Name)) if (allowNameChange && !string.IsNullOrEmpty(newConfig.Name))
{ {
Name = newConfig.Name; Name = newConfig.Name;
Identifier = newConfig.Name;
} }
// If account changed, reload it // If account changed, reload it
@@ -1893,4 +1898,9 @@ public class TradingBotBackup
/// Runtime state: When the bot was started /// Runtime state: When the bot was started
/// </summary> /// </summary>
public DateTime StartupTime { get; set; } public DateTime StartupTime { get; set; }
/// <summary>
/// Runtime state: When the bot was created
/// </summary>
public DateTime CreateDate { get; set; }
} }

View File

@@ -244,12 +244,20 @@ namespace Managing.Application.ManageBot
{ {
if (botWrapper.BotInstance is IBot bot) if (botWrapper.BotInstance is IBot bot)
{ {
// Stop the bot first to ensure clean state
bot.Stop();
// Small delay to ensure stop is complete
await Task.Delay(100);
// Restart the bot (this will update StartupTime)
bot.Restart(); bot.Restart();
var restartMessage = $"🔄 **Bot Restarted**\n\n" + var restartMessage = $"🔄 **Bot Restarted**\n\n" +
$"🎯 **Agent:** {bot.User.AgentName}\n" + $"🎯 **Agent:** {bot.User.AgentName}\n" +
$"🤖 **Bot Name:** {bot.Name}\n" + $"🤖 **Bot Name:** {bot.Name}\n" +
$"⏰ **Restarted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" + $"⏰ **Restarted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
$"🕐 **New Startup Time:** {bot.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
$"🚀 **Bot has been successfully restarted and is now active.**"; $"🚀 **Bot has been successfully restarted and is now active.**";
await _messengerService.SendTradeMessage(restartMessage, false, bot.User); await _messengerService.SendTradeMessage(restartMessage, false, bot.User);

View File

@@ -207,7 +207,7 @@ public class MessengerService : IMessengerService
var finalPnl = backtest.FinalPnl; var finalPnl = backtest.FinalPnl;
var growthPercentage = backtest.GrowthPercentage; var growthPercentage = backtest.GrowthPercentage;
var maxDrawdown = backtest.Statistics?.MaxDrawdownPc ?? 0; var maxDrawdown = backtest.Statistics?.MaxDrawdownPc ?? 0;
var sharpeRatio = backtest.Statistics?.SharpeRatio ?? 0; var sharpeRatio = (backtest.Statistics?.SharpeRatio * 100) ?? 0;
return $"📈 Performance Metrics:\n" + return $"📈 Performance Metrics:\n" +
$"⭐ Score: {score:F1}/100 | 🏆 Win Rate: {winRate:F1}%\n" + $"⭐ Score: {score:F1}/100 | 🏆 Win Rate: {winRate:F1}%\n" +

View File

@@ -17,9 +17,14 @@ namespace Managing.Domain.Bots
public User User { get; set; } public User User { get; set; }
/// <summary> /// <summary>
/// The time when the bot was started /// The time when the bot was first started (creation date)
/// </summary> /// </summary>
public DateTime StartupTime { get; private set; } public DateTime StartupTime { get; protected set; }
/// <summary>
/// The time when the bot was created
/// </summary>
public DateTime CreateDate { get; protected set; }
private CancellationTokenSource CancellationToken { get; set; } private CancellationTokenSource CancellationToken { get; set; }
@@ -31,13 +36,14 @@ namespace Managing.Domain.Bots
CancellationToken = new CancellationTokenSource(); CancellationToken = new CancellationTokenSource();
ExecutionCount = 0; ExecutionCount = 0;
Interval = 3000; Interval = 3000;
StartupTime = DateTime.MinValue; // Initialize with minimum value to indicate it hasn't been started yet CreateDate = DateTime.UtcNow; // Set the creation time when the bot is instantiated
StartupTime = DateTime.UtcNow; // Set the startup time to creation date initially
} }
public virtual void Start() public virtual void Start()
{ {
Status = BotStatus.Up; Status = BotStatus.Up;
StartupTime = DateTime.UtcNow; // Record the startup time when the bot is started // StartupTime remains unchanged on first start (it's already set to creation date)
} }
public async Task InitWorker(Func<Task> action) public async Task InitWorker(Func<Task> action)
@@ -105,7 +111,7 @@ namespace Managing.Domain.Bots
/// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns> /// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns>
public TimeSpan GetRuntime() public TimeSpan GetRuntime()
{ {
if (Status != BotStatus.Up || StartupTime == DateTime.MinValue) if (Status != BotStatus.Up)
return TimeSpan.Zero; return TimeSpan.Zero;
return DateTime.UtcNow - StartupTime; return DateTime.UtcNow - StartupTime;

View File

@@ -9,4 +9,5 @@ public class BotBackup
public User User { get; set; } public User User { get; set; }
public string Data { get; set; } public string Data { get; set; }
public BotStatus LastStatus { get; set; } public BotStatus LastStatus { get; set; }
public DateTime CreateDate { get; set; }
} }

View File

@@ -18,6 +18,11 @@ namespace Managing.Domain.Bots
/// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns> /// <returns>TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running</returns>
TimeSpan GetRuntime(); TimeSpan GetRuntime();
/// <summary>
/// The time when the bot was first started (creation date)
/// </summary>
DateTime StartupTime { get; }
string Identifier { get; set; } string Identifier { get; set; }
void SaveBackup(); void SaveBackup();
void LoadBackup(BotBackup backup); void LoadBackup(BotBackup backup);

View File

@@ -3533,6 +3533,8 @@ export interface TradingBotResponse {
identifier: string; identifier: string;
agentName: string; agentName: string;
config: TradingBotConfig; config: TradingBotConfig;
createDate: Date;
startupTime: Date;
} }
export interface OpenPositionManuallyRequest { export interface OpenPositionManuallyRequest {

View File

@@ -673,6 +673,8 @@ export interface TradingBotResponse {
identifier: string; identifier: string;
agentName: string; agentName: string;
config: TradingBotConfig; config: TradingBotConfig;
createDate: Date;
startupTime: Date;
} }
export interface OpenPositionManuallyRequest { export interface OpenPositionManuallyRequest {