diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs
index c81aa26..d3cabe1 100644
--- a/src/Managing.Api/Controllers/BotController.cs
+++ b/src/Managing.Api/Controllers/BotController.cs
@@ -505,7 +505,9 @@ public class BotController : BaseController
ProfitAndLoss = item.GetProfitAndLoss(),
Identifier = item.Identifier,
AgentName = item.User.AgentName,
- Config = item.Config // Contains all configuration properties
+ Config = item.Config,
+ CreateDate = item.CreateDate,
+ StartupTime = item.StartupTime
});
}
diff --git a/src/Managing.Api/Models/Responses/TradingBotResponse.cs b/src/Managing.Api/Models/Responses/TradingBotResponse.cs
index c13f742..73a1956 100644
--- a/src/Managing.Api/Models/Responses/TradingBotResponse.cs
+++ b/src/Managing.Api/Models/Responses/TradingBotResponse.cs
@@ -52,5 +52,15 @@ namespace Managing.Api.Models.Responses
/// The full trading bot configuration
///
[Required] public TradingBotConfig Config { get; internal set; }
+
+ ///
+ /// The time when the bot was created
+ ///
+ [Required] public DateTime CreateDate { get; internal set; }
+
+ ///
+ /// The time when the bot was started
+ ///
+ [Required] public DateTime StartupTime { get; internal set; }
}
}
\ No newline at end of file
diff --git a/src/Managing.Application/Abstractions/ITradingBot.cs b/src/Managing.Application/Abstractions/ITradingBot.cs
index 78344f6..f862d5d 100644
--- a/src/Managing.Application/Abstractions/ITradingBot.cs
+++ b/src/Managing.Application/Abstractions/ITradingBot.cs
@@ -21,6 +21,7 @@ namespace Managing.Application.Abstractions
Dictionary WalletBalances { get; set; }
Dictionary IndicatorsValues { get; set; }
DateTime StartupTime { get; set; }
+ DateTime CreateDate { get; }
DateTime PreloadSince { get; set; }
int PreloadedCandlesCount { get; set; }
decimal Fee { get; set; }
diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs
index 4022fd7..21f86e7 100644
--- a/src/Managing.Application/Bots/TradingBot.cs
+++ b/src/Managing.Application/Bots/TradingBot.cs
@@ -114,7 +114,12 @@ public class TradingBot : Bot, ITradingBot
CancelAllOrders().GetAwaiter().GetResult();
// 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)
{
try
@@ -361,38 +366,37 @@ public class TradingBot : Bot, ITradingBot
// TODO : remove this when Synth is stable
// signal.Status = SignalStatus.Expired;
signalText += $"\n\nš« *Synth Signal Filter*\n" +
- $"Signal `{signal.Identifier}` blocked by Synth risk assessment\n\n" +
- $"š *Risk Analysis Details*\n" +
- $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
- $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
- $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
- $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
- $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
- $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
- $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
- $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
- $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
- $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
- $"š *Context*\n`{signalValidationResult.ValidationContext}`";
-
+ $"Signal `{signal.Identifier}` blocked by Synth risk assessment\n\n" +
+ $"š *Risk Analysis Details*\n" +
+ $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
+ $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
+ $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
+ $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
+ $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
+ $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
+ $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
+ $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
+ $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
+ $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
+ $"š *Context*\n`{signalValidationResult.ValidationContext}`";
}
else
{
signal.SetConfidence(signalValidationResult.Confidence);
signalText += $"\n\nā
*Synth Risk Assessment Passed*\n" +
- $"Confidence: `{signalValidationResult.Confidence}`\n\n" +
- $"š *Risk Analysis Details*\n" +
- $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
- $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
- $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
- $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
- $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
- $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
- $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
- $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
- $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
- $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
- $"š *Context*\n`{signalValidationResult.ValidationContext}`";
+ $"Confidence: `{signalValidationResult.Confidence}`\n\n" +
+ $"š *Risk Analysis Details*\n" +
+ $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" +
+ $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" +
+ $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" +
+ $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" +
+ $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" +
+ $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" +
+ $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" +
+ $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" +
+ $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" +
+ $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" +
+ $"š *Context*\n`{signalValidationResult.ValidationContext}`";
}
}
@@ -1429,7 +1433,8 @@ public class TradingBot : Bot, ITradingBot
Signals = Signals,
Positions = Positions,
WalletBalances = WalletBalances,
- StartupTime = StartupTime
+ StartupTime = StartupTime,
+ CreateDate = CreateDate
};
BotService.SaveOrUpdateBotBackup(User, Identifier, Status, JsonConvert.SerializeObject(data));
}
@@ -1449,6 +1454,7 @@ public class TradingBot : Bot, ITradingBot
Positions = data.Positions ?? new List();
WalletBalances = data.WalletBalances ?? new Dictionary();
PreloadSince = data.StartupTime;
+ CreateDate = data.CreateDate;
Identifier = backup.Identifier;
User = backup.User;
Status = backup.LastStatus;
@@ -1718,7 +1724,6 @@ public class TradingBot : Bot, ITradingBot
if (allowNameChange && !string.IsNullOrEmpty(newConfig.Name))
{
Name = newConfig.Name;
- Identifier = newConfig.Name;
}
// If account changed, reload it
@@ -1893,4 +1898,9 @@ public class TradingBotBackup
/// Runtime state: When the bot was started
///
public DateTime StartupTime { get; set; }
+
+ ///
+ /// Runtime state: When the bot was created
+ ///
+ public DateTime CreateDate { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs
index 5690e22..cee6b69 100644
--- a/src/Managing.Application/ManageBot/BotService.cs
+++ b/src/Managing.Application/ManageBot/BotService.cs
@@ -244,12 +244,20 @@ namespace Managing.Application.ManageBot
{
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();
var restartMessage = $"š **Bot Restarted**\n\n" +
$"šÆ **Agent:** {bot.User.AgentName}\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.**";
await _messengerService.SendTradeMessage(restartMessage, false, bot.User);
diff --git a/src/Managing.Application/Shared/MessengerService.cs b/src/Managing.Application/Shared/MessengerService.cs
index c43fe5a..6e2c60b 100644
--- a/src/Managing.Application/Shared/MessengerService.cs
+++ b/src/Managing.Application/Shared/MessengerService.cs
@@ -207,7 +207,7 @@ public class MessengerService : IMessengerService
var finalPnl = backtest.FinalPnl;
var growthPercentage = backtest.GrowthPercentage;
var maxDrawdown = backtest.Statistics?.MaxDrawdownPc ?? 0;
- var sharpeRatio = backtest.Statistics?.SharpeRatio ?? 0;
+ var sharpeRatio = (backtest.Statistics?.SharpeRatio * 100) ?? 0;
return $"š Performance Metrics:\n" +
$"ā Score: {score:F1}/100 | š Win Rate: {winRate:F1}%\n" +
diff --git a/src/Managing.Domain/Bots/Bot.cs b/src/Managing.Domain/Bots/Bot.cs
index 18c3f8f..7f5194f 100644
--- a/src/Managing.Domain/Bots/Bot.cs
+++ b/src/Managing.Domain/Bots/Bot.cs
@@ -17,9 +17,14 @@ namespace Managing.Domain.Bots
public User User { get; set; }
///
- /// The time when the bot was started
+ /// The time when the bot was first started (creation date)
///
- public DateTime StartupTime { get; private set; }
+ public DateTime StartupTime { get; protected set; }
+
+ ///
+ /// The time when the bot was created
+ ///
+ public DateTime CreateDate { get; protected set; }
private CancellationTokenSource CancellationToken { get; set; }
@@ -31,13 +36,14 @@ namespace Managing.Domain.Bots
CancellationToken = new CancellationTokenSource();
ExecutionCount = 0;
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()
{
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 action)
@@ -105,7 +111,7 @@ namespace Managing.Domain.Bots
/// TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running
public TimeSpan GetRuntime()
{
- if (Status != BotStatus.Up || StartupTime == DateTime.MinValue)
+ if (Status != BotStatus.Up)
return TimeSpan.Zero;
return DateTime.UtcNow - StartupTime;
diff --git a/src/Managing.Domain/Bots/BotBackup.cs b/src/Managing.Domain/Bots/BotBackup.cs
index 8b19f78..1f56f14 100644
--- a/src/Managing.Domain/Bots/BotBackup.cs
+++ b/src/Managing.Domain/Bots/BotBackup.cs
@@ -9,4 +9,5 @@ public class BotBackup
public User User { get; set; }
public string Data { get; set; }
public BotStatus LastStatus { get; set; }
+ public DateTime CreateDate { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Domain/Bots/IBot.cs b/src/Managing.Domain/Bots/IBot.cs
index d821d0d..0ef3c11 100644
--- a/src/Managing.Domain/Bots/IBot.cs
+++ b/src/Managing.Domain/Bots/IBot.cs
@@ -18,6 +18,11 @@ namespace Managing.Domain.Bots
/// TimeSpan representing the runtime, or TimeSpan.Zero if the bot is not running
TimeSpan GetRuntime();
+ ///
+ /// The time when the bot was first started (creation date)
+ ///
+ DateTime StartupTime { get; }
+
string Identifier { get; set; }
void SaveBackup();
void LoadBackup(BotBackup backup);
diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts
index a494417..ea16f34 100644
--- a/src/Managing.WebApp/src/generated/ManagingApi.ts
+++ b/src/Managing.WebApp/src/generated/ManagingApi.ts
@@ -3533,6 +3533,8 @@ export interface TradingBotResponse {
identifier: string;
agentName: string;
config: TradingBotConfig;
+ createDate: Date;
+ startupTime: Date;
}
export interface OpenPositionManuallyRequest {
diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
index a9ddae1..b08726f 100644
--- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
+++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
@@ -673,6 +673,8 @@ export interface TradingBotResponse {
identifier: string;
agentName: string;
config: TradingBotConfig;
+ createDate: Date;
+ startupTime: Date;
}
export interface OpenPositionManuallyRequest {