Update messages
This commit is contained in:
@@ -83,7 +83,8 @@ public class TradingBot : Bot, ITradingBot
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Scenario object must be provided in TradingBotConfig. ScenarioName alone is not sufficient.");
|
||||
throw new ArgumentException(
|
||||
"Scenario object must be provided in TradingBotConfig. ScenarioName alone is not sufficient.");
|
||||
}
|
||||
|
||||
if (!Config.IsForBacktest)
|
||||
@@ -105,23 +106,51 @@ public class TradingBot : Bot, ITradingBot
|
||||
// This is just a safety check
|
||||
if (Config.Scenario == null || !Indicators.Any())
|
||||
{
|
||||
throw new InvalidOperationException("Scenario or indicators not loaded properly in constructor. This indicates a configuration error.");
|
||||
throw new InvalidOperationException(
|
||||
"Scenario or indicators not loaded properly in constructor. This indicates a configuration error.");
|
||||
}
|
||||
|
||||
PreloadCandles().GetAwaiter().GetResult();
|
||||
CancelAllOrders().GetAwaiter().GetResult();
|
||||
|
||||
// Send startup message only for fresh starts (not reboots)
|
||||
var isReboot = Signals.Any() || Positions.Any();
|
||||
if (!isReboot)
|
||||
{
|
||||
try
|
||||
{
|
||||
// await MessengerService.SendMessage(
|
||||
// $"Hey everyone! I'm about to start {Name}. 🚀\n" +
|
||||
// $"I'll post an update here each time a signal is triggered by the following strategies: {string.Join(", ", Strategies.Select(s => s.Name))}."
|
||||
// );
|
||||
var indicatorNames = Indicators.Select(i => i.Type.ToString()).ToList();
|
||||
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
|
||||
$"📊 **Trading Setup:**\n" +
|
||||
$"🎯 Ticker: `{Config.Ticker}`\n" +
|
||||
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
||||
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
|
||||
$"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
|
||||
$"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
|
||||
$"📈 **Active Indicators:** `{string.Join(", ", indicatorNames)}`\n\n" +
|
||||
$"✅ Ready to monitor signals and execute trades!\n" +
|
||||
$"📢 I'll notify you when signals are triggered.";
|
||||
|
||||
LogInformation(startupMessage).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
LogInformation($"🔄 **Bot Restarted**\n" +
|
||||
$"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
|
||||
$"✅ Ready to continue trading").GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
InitWorker(Run).GetAwaiter().GetResult();
|
||||
}
|
||||
@@ -144,7 +173,6 @@ public class TradingBot : Bot, ITradingBot
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void LoadScenario(Scenario scenario)
|
||||
{
|
||||
if (scenario == null)
|
||||
@@ -1469,22 +1497,154 @@ public class TradingBot : Bot, ITradingBot
|
||||
throw new ArgumentException("Scenario object must be provided in configuration");
|
||||
}
|
||||
|
||||
// Track changes for logging
|
||||
var changes = new List<string>();
|
||||
|
||||
// Check for changes and build change list
|
||||
if (Config.BotTradingBalance != newConfig.BotTradingBalance)
|
||||
{
|
||||
changes.Add($"💰 Balance: ${Config.BotTradingBalance:F2} → ${newConfig.BotTradingBalance:F2}");
|
||||
}
|
||||
|
||||
if (Config.MaxPositionTimeHours != newConfig.MaxPositionTimeHours)
|
||||
{
|
||||
var oldTime = Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled";
|
||||
var newTime = newConfig.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled";
|
||||
changes.Add($"⏱️ Max Time: {oldTime} → {newTime}");
|
||||
}
|
||||
|
||||
if (Config.FlipOnlyWhenInProfit != newConfig.FlipOnlyWhenInProfit)
|
||||
{
|
||||
var oldFlip = Config.FlipOnlyWhenInProfit ? "✅" : "❌";
|
||||
var newFlip = newConfig.FlipOnlyWhenInProfit ? "✅" : "❌";
|
||||
changes.Add($"📈 Flip Only in Profit: {oldFlip} → {newFlip}");
|
||||
}
|
||||
|
||||
if (Config.CooldownPeriod != newConfig.CooldownPeriod)
|
||||
{
|
||||
changes.Add($"⏳ Cooldown: {Config.CooldownPeriod} → {newConfig.CooldownPeriod} candles");
|
||||
}
|
||||
|
||||
if (Config.MaxLossStreak != newConfig.MaxLossStreak)
|
||||
{
|
||||
changes.Add($"📉 Max Loss Streak: {Config.MaxLossStreak} → {newConfig.MaxLossStreak}");
|
||||
}
|
||||
|
||||
if (Config.FlipPosition != newConfig.FlipPosition)
|
||||
{
|
||||
var oldFlipPos = Config.FlipPosition ? "✅" : "❌";
|
||||
var newFlipPos = newConfig.FlipPosition ? "✅" : "❌";
|
||||
changes.Add($"🔄 Flip Position: {oldFlipPos} → {newFlipPos}");
|
||||
}
|
||||
|
||||
if (Config.CloseEarlyWhenProfitable != newConfig.CloseEarlyWhenProfitable)
|
||||
{
|
||||
var oldCloseEarly = Config.CloseEarlyWhenProfitable ? "✅" : "❌";
|
||||
var newCloseEarly = newConfig.CloseEarlyWhenProfitable ? "✅" : "❌";
|
||||
changes.Add($"⏰ Close Early When Profitable: {oldCloseEarly} → {newCloseEarly}");
|
||||
}
|
||||
|
||||
if (Config.UseSynthApi != newConfig.UseSynthApi)
|
||||
{
|
||||
var oldSynth = Config.UseSynthApi ? "✅" : "❌";
|
||||
var newSynth = newConfig.UseSynthApi ? "✅" : "❌";
|
||||
changes.Add($"🔗 Use Synth API: {oldSynth} → {newSynth}");
|
||||
}
|
||||
|
||||
if (Config.UseForPositionSizing != newConfig.UseForPositionSizing)
|
||||
{
|
||||
var oldPositionSizing = Config.UseForPositionSizing ? "✅" : "❌";
|
||||
var newPositionSizing = newConfig.UseForPositionSizing ? "✅" : "❌";
|
||||
changes.Add($"📏 Use Synth for Position Sizing: {oldPositionSizing} → {newPositionSizing}");
|
||||
}
|
||||
|
||||
if (Config.UseForSignalFiltering != newConfig.UseForSignalFiltering)
|
||||
{
|
||||
var oldSignalFiltering = Config.UseForSignalFiltering ? "✅" : "❌";
|
||||
var newSignalFiltering = newConfig.UseForSignalFiltering ? "✅" : "❌";
|
||||
changes.Add($"🔍 Use Synth for Signal Filtering: {oldSignalFiltering} → {newSignalFiltering}");
|
||||
}
|
||||
|
||||
if (Config.UseForDynamicStopLoss != newConfig.UseForDynamicStopLoss)
|
||||
{
|
||||
var oldDynamicStopLoss = Config.UseForDynamicStopLoss ? "✅" : "❌";
|
||||
var newDynamicStopLoss = newConfig.UseForDynamicStopLoss ? "✅" : "❌";
|
||||
changes.Add($"🎯 Use Synth for Dynamic Stop Loss: {oldDynamicStopLoss} → {newDynamicStopLoss}");
|
||||
}
|
||||
|
||||
if (Config.IsForWatchingOnly != newConfig.IsForWatchingOnly)
|
||||
{
|
||||
var oldWatch = Config.IsForWatchingOnly ? "✅" : "❌";
|
||||
var newWatch = newConfig.IsForWatchingOnly ? "✅" : "❌";
|
||||
changes.Add($"👀 Watch Only: {oldWatch} → {newWatch}");
|
||||
}
|
||||
|
||||
if (Config.MoneyManagement?.GetType().Name != newConfig.MoneyManagement?.GetType().Name)
|
||||
{
|
||||
var oldMM = Config.MoneyManagement?.GetType().Name ?? "None";
|
||||
var newMM = newConfig.MoneyManagement?.GetType().Name ?? "None";
|
||||
changes.Add($"💰 Money Management: {oldMM} → {newMM}");
|
||||
}
|
||||
|
||||
if (Config.RiskManagement != newConfig.RiskManagement)
|
||||
{
|
||||
// Compare risk management by serializing (complex object comparison)
|
||||
var oldRiskSerialized = JsonConvert.SerializeObject(Config.RiskManagement, Formatting.None);
|
||||
var newRiskSerialized = JsonConvert.SerializeObject(newConfig.RiskManagement, Formatting.None);
|
||||
|
||||
if (oldRiskSerialized != newRiskSerialized)
|
||||
{
|
||||
changes.Add($"⚠️ Risk Management: Configuration Updated");
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ScenarioName != newConfig.ScenarioName)
|
||||
{
|
||||
changes.Add($"📋 Scenario Name: {Config.ScenarioName ?? "None"} → {newConfig.ScenarioName ?? "None"}");
|
||||
}
|
||||
|
||||
if (allowNameChange && Config.Name != newConfig.Name)
|
||||
{
|
||||
changes.Add($"🏷️ Name: {Config.Name} → {newConfig.Name}");
|
||||
}
|
||||
|
||||
if (Config.AccountName != newConfig.AccountName)
|
||||
{
|
||||
changes.Add($"👤 Account: {Config.AccountName} → {newConfig.AccountName}");
|
||||
}
|
||||
|
||||
if (Config.Ticker != newConfig.Ticker)
|
||||
{
|
||||
changes.Add($"📊 Ticker: {Config.Ticker} → {newConfig.Ticker}");
|
||||
}
|
||||
|
||||
if (Config.Timeframe != newConfig.Timeframe)
|
||||
{
|
||||
changes.Add($"📈 Timeframe: {Config.Timeframe} → {newConfig.Timeframe}");
|
||||
}
|
||||
|
||||
// Capture current indicators before any changes for scenario comparison
|
||||
var oldIndicators = Indicators?.ToList() ?? new List<IIndicator>();
|
||||
|
||||
// Check if the actual Scenario object changed (not just the name)
|
||||
var scenarioChanged = false;
|
||||
if (Config.Scenario != newConfig.Scenario)
|
||||
{
|
||||
var oldScenarioSerialized = JsonConvert.SerializeObject(Config.Scenario, Formatting.None);
|
||||
var newScenarioSerialized = JsonConvert.SerializeObject(newConfig.Scenario, Formatting.None);
|
||||
|
||||
if (oldScenarioSerialized != newScenarioSerialized)
|
||||
{
|
||||
scenarioChanged = true;
|
||||
changes.Add(
|
||||
$"🎯 Scenario: {Config.Scenario?.Name ?? "None"} → {newConfig.Scenario?.Name ?? "None"}");
|
||||
}
|
||||
}
|
||||
|
||||
// Protect critical properties that shouldn't change for running bots
|
||||
var protectedIsForBacktest = Config.IsForBacktest;
|
||||
var protectedName = allowNameChange ? newConfig.Name : Config.Name;
|
||||
|
||||
// Log the configuration update (before changing anything)
|
||||
await LogInformation("⚙️ **Configuration Update**\n" +
|
||||
"📊 **Previous Settings:**\n" +
|
||||
$"💰 Balance: ${Config.BotTradingBalance:F2}\n" +
|
||||
$"⏱️ Max Time: {(Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled")}\n" +
|
||||
$"📈 Flip Only in Profit: {(Config.FlipOnlyWhenInProfit ? "✅" : "❌")}\n" +
|
||||
$"⏳ Cooldown: {Config.CooldownPeriod} candles\n" +
|
||||
$"📉 Max Loss Streak: {Config.MaxLossStreak}" +
|
||||
(allowNameChange && newConfig.Name != Config.Name
|
||||
? $"\n🏷️ Name: {Config.Name} → {newConfig.Name}"
|
||||
: ""));
|
||||
|
||||
// Update the configuration
|
||||
Config = newConfig;
|
||||
|
||||
@@ -1505,14 +1665,21 @@ public class TradingBot : Bot, ITradingBot
|
||||
await LoadAccount();
|
||||
}
|
||||
|
||||
// If scenario changed, reload it
|
||||
var currentScenario = Config.Scenario?.Name;
|
||||
var newScenario = newConfig.Scenario?.Name;
|
||||
if (newScenario != currentScenario)
|
||||
// If scenario changed, reload it and track indicator changes
|
||||
if (scenarioChanged)
|
||||
{
|
||||
if (newConfig.Scenario != null)
|
||||
{
|
||||
LoadScenario(newConfig.Scenario);
|
||||
|
||||
// Compare indicators after scenario change
|
||||
var newIndicators = Indicators?.ToList() ?? new List<IIndicator>();
|
||||
var indicatorChanges = CompareIndicators(oldIndicators, newIndicators);
|
||||
|
||||
if (indicatorChanges.Any())
|
||||
{
|
||||
changes.AddRange(indicatorChanges);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1520,13 +1687,17 @@ public class TradingBot : Bot, ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
await LogInformation("✅ **Configuration Applied**\n" +
|
||||
"🔧 **New Settings:**\n" +
|
||||
$"💰 Balance: ${Config.BotTradingBalance:F2}\n" +
|
||||
$"⏱️ Max Time: {(Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled")}\n" +
|
||||
$"📈 Flip Only in Profit: {(Config.FlipOnlyWhenInProfit ? "✅" : "❌")}\n" +
|
||||
$"⏳ Cooldown: {Config.CooldownPeriod} candles\n" +
|
||||
$"📉 Max Loss Streak: {Config.MaxLossStreak}");
|
||||
// Only log if there are actual changes
|
||||
if (changes.Any())
|
||||
{
|
||||
var changeMessage = "⚙️ **Configuration Updated**\n" + string.Join("\n", changes);
|
||||
await LogInformation(changeMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
"⚙️ **Configuration Update**\n✅ No changes detected - configuration already up to date");
|
||||
}
|
||||
|
||||
// Save the updated configuration as backup
|
||||
if (!Config.IsForBacktest)
|
||||
@@ -1555,6 +1726,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
MoneyManagement = Config.MoneyManagement,
|
||||
Ticker = Config.Ticker,
|
||||
ScenarioName = Config.ScenarioName,
|
||||
Scenario = Config.Scenario,
|
||||
Timeframe = Config.Timeframe,
|
||||
IsForWatchingOnly = Config.IsForWatchingOnly,
|
||||
BotTradingBalance = Config.BotTradingBalance,
|
||||
@@ -1567,8 +1739,70 @@ public class TradingBot : Bot, ITradingBot
|
||||
Name = Config.Name,
|
||||
CloseEarlyWhenProfitable = Config.CloseEarlyWhenProfitable,
|
||||
UseSynthApi = Config.UseSynthApi,
|
||||
UseForPositionSizing = Config.UseForPositionSizing,
|
||||
UseForSignalFiltering = Config.UseForSignalFiltering,
|
||||
UseForDynamicStopLoss = Config.UseForDynamicStopLoss,
|
||||
RiskManagement = Config.RiskManagement,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two lists of indicators and returns a list of changes (added, removed, modified).
|
||||
/// </summary>
|
||||
/// <param name="oldIndicators">The previous list of indicators</param>
|
||||
/// <param name="newIndicators">The new list of indicators</param>
|
||||
/// <returns>A list of change descriptions</returns>
|
||||
private List<string> CompareIndicators(List<IIndicator> oldIndicators, List<IIndicator> newIndicators)
|
||||
{
|
||||
var changes = new List<string>();
|
||||
|
||||
// Create dictionaries for easier comparison using Type as key
|
||||
var oldIndicatorDict = oldIndicators.ToDictionary(i => i.Type, i => i);
|
||||
var newIndicatorDict = newIndicators.ToDictionary(i => i.Type, i => i);
|
||||
|
||||
// Find removed indicators
|
||||
var removedTypes = oldIndicatorDict.Keys.Except(newIndicatorDict.Keys);
|
||||
foreach (var removedType in removedTypes)
|
||||
{
|
||||
var indicator = oldIndicatorDict[removedType];
|
||||
changes.Add($"➖ **Removed Indicator:** {removedType} ({indicator.GetType().Name})");
|
||||
}
|
||||
|
||||
// Find added indicators
|
||||
var addedTypes = newIndicatorDict.Keys.Except(oldIndicatorDict.Keys);
|
||||
foreach (var addedType in addedTypes)
|
||||
{
|
||||
var indicator = newIndicatorDict[addedType];
|
||||
changes.Add($"➕ **Added Indicator:** {addedType} ({indicator.GetType().Name})");
|
||||
}
|
||||
|
||||
// Find modified indicators (same type but potentially different configuration)
|
||||
var commonTypes = oldIndicatorDict.Keys.Intersect(newIndicatorDict.Keys);
|
||||
foreach (var commonType in commonTypes)
|
||||
{
|
||||
var oldIndicator = oldIndicatorDict[commonType];
|
||||
var newIndicator = newIndicatorDict[commonType];
|
||||
|
||||
// Compare indicators by serializing them (simple way to detect configuration changes)
|
||||
var oldSerialized = JsonConvert.SerializeObject(oldIndicator, Formatting.None);
|
||||
var newSerialized = JsonConvert.SerializeObject(newIndicator, Formatting.None);
|
||||
|
||||
if (oldSerialized != newSerialized)
|
||||
{
|
||||
changes.Add($"🔄 **Modified Indicator:** {commonType} ({newIndicator.GetType().Name})");
|
||||
}
|
||||
}
|
||||
|
||||
// Add summary if there are changes
|
||||
if (changes.Any())
|
||||
{
|
||||
var summary =
|
||||
$"📊 **Indicator Changes:** {addedTypes.Count()} added, {removedTypes.Count()} removed, {commonTypes.Count(c => JsonConvert.SerializeObject(oldIndicatorDict[c]) != JsonConvert.SerializeObject(newIndicatorDict[c]))} modified";
|
||||
changes.Insert(0, summary);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public class TradingBotBackup
|
||||
|
||||
Reference in New Issue
Block a user