Update messaging
This commit is contained in:
@@ -859,18 +859,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
try
|
||||
{
|
||||
var message = isSuccess
|
||||
? $"🔄 **Automatic Swap Successful**\n\n" +
|
||||
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
|
||||
$"💰 **Amount:** {amount} USDC → ETH\n" +
|
||||
$"✅ **Status:** Success\n" +
|
||||
$"🔗 **Transaction:** {transactionHash}\n" +
|
||||
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
|
||||
: $"❌ **Automatic Swap Failed**\n\n" +
|
||||
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
|
||||
$"💰 **Amount:** {amount} USDC → ETH\n" +
|
||||
$"❌ **Status:** Failed\n" +
|
||||
$"⚠️ **Error:** {errorMessage}\n" +
|
||||
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
||||
? $"🔄 Automatic Swap Successful\n\n" +
|
||||
$"🎯 Bot: {_tradingBot?.Identifier}\n" +
|
||||
$"💰 Amount: {amount} USDC → ETH\n" +
|
||||
$"✅ Status: Success\n" +
|
||||
$"🔗 Transaction: {transactionHash}\n" +
|
||||
$"⏰ Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
|
||||
: $"❌ Automatic Swap Failed\n\n" +
|
||||
$"🎯 Bot: {_tradingBot?.Identifier}\n" +
|
||||
$"💰 Amount: {amount} USDC → ETH\n" +
|
||||
$"❌ Status: Failed\n" +
|
||||
$"⚠️ Error: {errorMessage}\n" +
|
||||
$"⏰ Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
||||
|
||||
// Send notification via webhook service
|
||||
await ServiceScopeHelpers.WithScopedService<IWebhookService>(_scopeFactory,
|
||||
|
||||
@@ -82,16 +82,16 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
case BotStatus.Saved:
|
||||
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
||||
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
|
||||
$"📊 **Trading Setup:**\n" +
|
||||
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.";
|
||||
$"📈 Active Indicators: `{string.Join(", ", indicatorNames)}`\n\n" +
|
||||
$"✅ Ready to monitor signals and execute trades\n" +
|
||||
$"📢 Notifications will be sent when positions are triggered";
|
||||
|
||||
await LogInformation(startupMessage);
|
||||
break;
|
||||
@@ -101,7 +101,7 @@ public class TradingBotBase : ITradingBot
|
||||
|
||||
case BotStatus.Stopped:
|
||||
// If status was Stopped we log a message to inform the user that the bot is restarting
|
||||
await LogInformation($"🔄 **Bot Restarted**\n" +
|
||||
await LogInformation($"🔄 Bot Restarted\n" +
|
||||
$"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
|
||||
$"✅ Ready to continue trading");
|
||||
break;
|
||||
@@ -264,7 +264,7 @@ public class TradingBotBase : ITradingBot
|
||||
Signals.Add(recreatedSignal.Identifier, recreatedSignal);
|
||||
|
||||
await LogInformation(
|
||||
$"🔍 **Signal Recovery Success**\nRecreated signal: `{recreatedSignal.Identifier}`\nFor position: `{position.Identifier}`");
|
||||
$"🔍 Signal Recovery Success\nRecreated signal: `{recreatedSignal.Identifier}`\nFor position: `{position.Identifier}`");
|
||||
return recreatedSignal;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -283,7 +283,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (signalForPosition == null)
|
||||
{
|
||||
await LogInformation(
|
||||
$"🔍 **Signal Recovery**\nSignal not found for position `{position.Identifier}`\nRecreating signal from position data...");
|
||||
$"🔍 Signal Recovery\nSignal not found for position `{position.Identifier}`\nRecreating signal from position data...");
|
||||
|
||||
// Recreate the signal based on position information
|
||||
signalForPosition = await RecreateSignalFromPosition(position);
|
||||
@@ -299,7 +299,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (signalForPosition.Status != SignalStatus.PositionOpen && position.Status != PositionStatus.Finished)
|
||||
{
|
||||
await LogInformation(
|
||||
$"🔄 **Signal Status Update**\nSignal: `{signalForPosition.Identifier}`\nStatus: `{signalForPosition.Status}` → `PositionOpen`");
|
||||
$"🔄 Signal Status Update\nSignal: `{signalForPosition.Identifier}`\nStatus: `{signalForPosition.Status}` → `PositionOpen`");
|
||||
SetSignalStatus(signalForPosition.Identifier, SignalStatus.PositionOpen);
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (LastCandle != null && signal.Date < LastCandle.Date)
|
||||
{
|
||||
await LogWarning(
|
||||
$"❌ **Signal Expired**\nSignal `{signal.Identifier}` is older than last candle `{LastCandle.Date}`\nStatus: `Expired`");
|
||||
$"❌ Signal Expired\nSignal `{signal.Identifier}` is older than last candle `{LastCandle.Date}`\nStatus: `Expired`");
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||
continue;
|
||||
}
|
||||
@@ -327,7 +327,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
// Position already exists for this signal, update signal status
|
||||
await LogInformation(
|
||||
$"🔄 **Signal Status Update**\nSignal: `{signal.Identifier}`\nStatus: `{signal.Status}` → `PositionOpen`\nPosition already exists: `{existingPosition.Identifier}`");
|
||||
$"🔄 Signal Status Update\nSignal: `{signal.Identifier}`\nStatus: `{signal.Status}` → `PositionOpen`\nPosition already exists: `{existingPosition.Identifier}`");
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen);
|
||||
continue;
|
||||
}
|
||||
@@ -342,7 +342,7 @@ public class TradingBotBase : ITradingBot
|
||||
else
|
||||
{
|
||||
await LogWarning(
|
||||
$"⚠️ **Position Creation Failed**\nSignal: `{signal.Identifier}`\nPosition creation returned null");
|
||||
$"⚠️ Position Creation Failed\nSignal: `{signal.Identifier}`\nPosition creation returned null");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,7 +486,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (timeSinceRequest.TotalMinutes >= waitTimeMinutes)
|
||||
{
|
||||
await LogWarning(
|
||||
$"⚠️ **Order Cleanup**\nToo many open orders: `{orders.Count()}`\nPosition: `{positionForSignal.Identifier}`\nTime elapsed: `{waitTimeMinutes}min`\nCanceling all orders...");
|
||||
$"⚠️ Order Cleanup\nToo many open orders: `{orders.Count()}`\nPosition: `{positionForSignal.Identifier}`\nTime elapsed: `{waitTimeMinutes}min`\nCanceling all orders...");
|
||||
try
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
@@ -495,7 +495,7 @@ public class TradingBotBase : ITradingBot
|
||||
await exchangeService.CancelOrder(Account, Config.Ticker);
|
||||
});
|
||||
await LogInformation(
|
||||
$"✅ **Orders Canceled**\nSuccessfully canceled all orders for: `{Config.Ticker}`");
|
||||
$"✅ Orders Canceled\nSuccessfully canceled all orders for: `{Config.Ticker}`");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -510,7 +510,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
var remainingMinutes = waitTimeMinutes - timeSinceRequest.TotalMinutes;
|
||||
await LogInformation(
|
||||
$"⏳ **Waiting for Orders**\nPosition has `{orders.Count()}` open orders\nElapsed: `{timeSinceRequest.TotalMinutes:F1}min`\nWaiting `{remainingMinutes:F1}min` more before canceling");
|
||||
$"⏳ Waiting for Orders\nPosition has `{orders.Count()}` open orders\nElapsed: `{timeSinceRequest.TotalMinutes:F1}min`\nWaiting `{remainingMinutes:F1}min` more before canceling");
|
||||
}
|
||||
}
|
||||
else if (ordersCount == 2)
|
||||
@@ -518,7 +518,7 @@ public class TradingBotBase : ITradingBot
|
||||
// TODO: This should never happen, but just in case
|
||||
// Check if position is already open on broker with 2 orders
|
||||
await LogInformation(
|
||||
$"🔍 **Checking Broker Position**\nPosition has exactly `{orders.Count()}` open orders\nChecking if position is already open on broker...");
|
||||
$"🔍 Checking Broker Position\nPosition has exactly `{orders.Count()}` open orders\nChecking if position is already open on broker...");
|
||||
|
||||
Position brokerPosition = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
@@ -531,7 +531,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (brokerPosition != null)
|
||||
{
|
||||
await LogInformation(
|
||||
$"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled");
|
||||
$"✅ Position Found on Broker\nPosition is already open on broker\nUpdating position status to Filled");
|
||||
|
||||
// Calculate net PnL after fees for broker position
|
||||
var brokerNetPnL = brokerPosition.GetNetPnL();
|
||||
@@ -564,19 +564,19 @@ public class TradingBotBase : ITradingBot
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
$"⏸️ **Position Pending**\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
|
||||
$"⏸️ Position Pending\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
$"⏸️ **Position Pending**\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
|
||||
$"⏸️ Position Pending\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogWarning(
|
||||
$"❌ **Position Never Filled**\nNo position on exchange and no orders\nSignal: `{signal.Identifier}`\nPosition was never filled and will be marked as canceled.");
|
||||
$"❌ Position Never Filled\nNo position on exchange and no orders\nSignal: `{signal.Identifier}`\nPosition was never filled and will be marked as canceled.");
|
||||
|
||||
// Position was never filled (still in New status), so just mark it as canceled
|
||||
// Don't call HandleClosedPosition as that would incorrectly add volume/PnL
|
||||
@@ -622,7 +622,7 @@ public class TradingBotBase : ITradingBot
|
||||
var profitStatus = isPositionInProfit ? "in profit" : "at a loss";
|
||||
|
||||
await LogInformation(
|
||||
$"⏰ **Time Limit Close**\nClosing position due to time limit: `{Config.MaxPositionTimeHours}h` exceeded\n📈 Position Status: **{profitStatus}**\n💰 Entry: `${positionForSignal.Open.Price}` → Current: `${lastCandle.Close}`\n📊 Realized PNL: `${currentPnl:F2}` (`{pnlPercentage:F2}%`)");
|
||||
$"⏰ Time Limit Close\nClosing position due to time limit: `{Config.MaxPositionTimeHours}h` exceeded\n📈 Position Status: {profitStatus}\n💰 Entry: `${positionForSignal.Open.Price}` → Current: `${lastCandle.Close}`\n📊 Realized PNL: `${currentPnl:F2}` (`{pnlPercentage:F2}%`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||
return;
|
||||
}
|
||||
@@ -651,7 +651,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||
$"🛑 Stop Loss Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||
executionPrice, true);
|
||||
}
|
||||
@@ -671,7 +671,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||
$"🎯 Take Profit 1 Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||
}
|
||||
@@ -690,7 +690,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||
$"🎯 Take Profit 2 Hit\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||
executionPrice, true);
|
||||
}
|
||||
@@ -717,7 +717,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||
$"🛑 Stop Loss Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||
executionPrice, true);
|
||||
}
|
||||
@@ -737,7 +737,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||
$"🎯 Take Profit 1 Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||
}
|
||||
@@ -756,7 +756,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||
$"🎯 Take Profit 2 Hit\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||
executionPrice, true);
|
||||
}
|
||||
@@ -845,7 +845,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (openedPosition.OriginDirection == signal.Direction)
|
||||
{
|
||||
await LogInformation(
|
||||
$"📍 **Same Direction Signal**\nSignal `{signal.Identifier}` tried to open position\nBut `{previousSignal.Identifier}` already open for same direction");
|
||||
$"📍 Same Direction Signal\nSignal `{signal.Identifier}` tried to open position\nBut `{previousSignal.Identifier}` already open for same direction");
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||
return null;
|
||||
}
|
||||
@@ -863,19 +863,19 @@ public class TradingBotBase : ITradingBot
|
||||
: "FlipOnlyWhenInProfit is disabled";
|
||||
|
||||
await LogInformation(
|
||||
$"🔄 **Position Flip Initiated**\nFlipping position due to opposite signal\nReason: {flipReason}");
|
||||
$"🔄 Position Flip Initiated\nFlipping position due to opposite signal\nReason: {flipReason}");
|
||||
await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
|
||||
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
|
||||
var newPosition = await OpenPosition(signal);
|
||||
await LogInformation(
|
||||
$"✅ **Position Flipped**\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
|
||||
$"✅ Position Flipped\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
|
||||
return newPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentPnl = openedPosition.ProfitAndLoss?.Realized ?? 0;
|
||||
await LogInformation(
|
||||
$"💸 **Flip Blocked - Not Profitable**\nPosition `{previousSignal.Identifier}` PnL: `${currentPnl:F2}`\nSignal `{signal.Identifier}` will wait for profitability");
|
||||
$"💸 Flip Blocked - Not Profitable\nPosition `{previousSignal.Identifier}` PnL: `${currentPnl:F2}`\nSignal `{signal.Identifier}` will wait for profitability");
|
||||
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||
return null;
|
||||
@@ -884,7 +884,7 @@ public class TradingBotBase : ITradingBot
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
$"🚫 **Flip Disabled**\nPosition already open for: `{previousSignal.Identifier}`\nFlipping disabled, new signal expired");
|
||||
$"🚫 Flip Disabled\nPosition already open for: `{previousSignal.Identifier}`\nFlipping disabled, new signal expired");
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||
return null;
|
||||
}
|
||||
@@ -899,9 +899,6 @@ public class TradingBotBase : ITradingBot
|
||||
return null;
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🚀 **Opening Position**\nTime: `{signal.Date:HH:mm:ss}`\nSignal: `{signal.Identifier}`");
|
||||
|
||||
try
|
||||
{
|
||||
var command = new OpenPositionRequest(
|
||||
@@ -936,7 +933,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||
async messengerService => { await messengerService.SendPosition(position); });
|
||||
async messengerService => { await messengerService.SendClosedPosition(position, Account.User); });
|
||||
}
|
||||
|
||||
Logger.LogInformation($"Position requested");
|
||||
@@ -979,7 +976,7 @@ public class TradingBotBase : ITradingBot
|
||||
// TODO : check if its a startup cycle
|
||||
if (!Config.IsForBacktest && ExecutionCount == 0)
|
||||
{
|
||||
await LogInformation("⏳ **Bot Not Ready**\nCannot open position\nBot hasn't executed first cycle yet");
|
||||
await LogInformation("⏳ Bot Not Ready\nCannot open position\nBot hasn't executed first cycle yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1059,7 +1056,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (lastPosition.OriginDirection == signal.Direction)
|
||||
{
|
||||
await LogWarning(
|
||||
$"🔥 **Loss Streak Limit**\nCannot open position\nMax loss streak: `{Config.MaxLossStreak}` reached\n📉 Last `{recentPositions.Count}` trades were losses\n🎯 Last position: `{lastPosition.OriginDirection}`\nWaiting for opposite direction signal");
|
||||
$"🔥 Loss Streak Limit\nCannot open position\nMax loss streak: `{Config.MaxLossStreak}` reached\n📉 Last `{recentPositions.Count}` trades were losses\n🎯 Last position: `{lastPosition.OriginDirection}`\nWaiting for opposite direction signal");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1101,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await LogWarning($"❌ **Broker Position Check Failed**\nError checking broker positions\n{ex.Message}");
|
||||
await LogWarning($"❌ Broker Position Check Failed\nError checking broker positions\n{ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1120,7 +1117,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
await LogInformation(
|
||||
$"🔧 **Closing Trade**\nTicker: `{Config.Ticker}`\nPrice: `${lastPrice}`\n📋 Type: `{tradeToClose.TradeType}`\n📊 Quantity: `{tradeToClose.Quantity}`\n🎯 Closing Position: `{(tradeClosingPosition ? "Yes" : "No")}`");
|
||||
$"🔧 Closing Trade\nTicker: `{Config.Ticker}`\nPrice: `${lastPrice}`\n📋 Type: `{tradeToClose.TradeType}`\n📊 Quantity: `{tradeToClose.Quantity:F5}`");
|
||||
|
||||
decimal quantity = 0;
|
||||
|
||||
@@ -1202,7 +1199,7 @@ public class TradingBotBase : ITradingBot
|
||||
try
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"🔍 **Fetching Position History from GMX**\nPosition: `{position.Identifier}`\nTicker: `{Config.Ticker}`");
|
||||
$"🔍 Fetching Position History from GMX\nPosition: `{position.Identifier}`\nTicker: `{Config.Ticker}`");
|
||||
|
||||
List<Position> positionHistory = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
@@ -1225,7 +1222,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (gmxPosition != null && gmxPosition.ProfitAndLoss != null)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"✅ **GMX Position History Found**\n" +
|
||||
$"✅ GMX Position History Found\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"GMX Realized PnL (after fees): `${gmxPosition.ProfitAndLoss.Realized:F2}`\n" +
|
||||
$"Bot's UI Fees: `${position.UiFees:F2}`\n" +
|
||||
@@ -1289,7 +1286,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
Logger.LogInformation(
|
||||
$"📊 **Position Reconciliation Complete**\n" +
|
||||
$"📊 Position Reconciliation Complete\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Closing Price: `${closingPrice:F2}`\n" +
|
||||
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
||||
@@ -1302,7 +1299,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
Logger.LogWarning(
|
||||
$"⚠️ **No GMX Position History Found**\nPosition: `{position.Identifier}`\nFalling back to candle-based calculation");
|
||||
$"⚠️ No GMX Position History Found\nPosition: `{position.Identifier}`\nFalling back to candle-based calculation");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1406,7 +1403,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
Logger.LogInformation(
|
||||
$"🛑 **Stop Loss Execution Confirmed**\n" +
|
||||
$"🛑 Stop Loss Execution Confirmed\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"SL Price: `${closingPrice:F2}` was hit (was `${position.StopLoss.Price:F2}`)\n" +
|
||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||
@@ -1429,7 +1426,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
Logger.LogInformation(
|
||||
$"🎯 **Take Profit Execution Confirmed**\n" +
|
||||
$"🎯 Take Profit Execution Confirmed\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"TP Price: `${closingPrice:F2}` was hit (was `${position.TakeProfit1.Price:F2}`)\n" +
|
||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||
@@ -1480,7 +1477,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
Logger.LogInformation(
|
||||
$"✋ **Manual/Exchange Close Detected**\n" +
|
||||
$"✋ Manual/Exchange Close Detected\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"SL: `${position.StopLoss.Price:F2}` | TP: `${position.TakeProfit1.Price:F2}`\n" +
|
||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`\n" +
|
||||
@@ -1549,21 +1546,21 @@ public class TradingBotBase : ITradingBot
|
||||
if (position.Open?.Status == TradeStatus.Filled)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"✅ **Position Closed Successfully**\nPosition: `{position.SignalIdentifier}`\nPnL: `${position.ProfitAndLoss?.Realized:F2}`");
|
||||
$"✅ Position Closed Successfully\nPosition: `{position.SignalIdentifier}`\nPnL: `${position.ProfitAndLoss?.Realized:F2}`");
|
||||
|
||||
if (position.ProfitAndLoss != null)
|
||||
{
|
||||
Config.BotTradingBalance += position.ProfitAndLoss.Realized;
|
||||
|
||||
Logger.LogInformation(
|
||||
string.Format("💰 **Balance Updated**\nNew bot trading balance: `${0:F2}`",
|
||||
string.Format("💰 Balance Updated\nNew bot trading balance: `${0:F2}`",
|
||||
Config.BotTradingBalance));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation(
|
||||
$"✅ **Position Cleanup**\nPosition: `{position.SignalIdentifier}` was never filled - no balance or PnL changes");
|
||||
$"✅ Position Cleanup\nPosition: `{position.SignalIdentifier}` was never filled - no balance or PnL changes");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1574,10 +1571,9 @@ public class TradingBotBase : ITradingBot
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||
messengerService =>
|
||||
async messengerService =>
|
||||
{
|
||||
messengerService.SendClosingPosition(position);
|
||||
return Task.CompletedTask;
|
||||
await messengerService.SendClosedPosition(position, Account.User);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1646,7 +1642,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
Positions.Values.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus;
|
||||
await LogInformation(
|
||||
$"📊 **Position Status Change**\nPosition: `{signalIdentifier}`\nStatus: `{position.Status}` → `{positionStatus}`");
|
||||
$"📊 Position Status Change\nPosition: `{position.Ticker}`\nDirection: `{position.OriginDirection}`\nNew Status: `{positionStatus}`");
|
||||
|
||||
// Update Open trade status when position becomes Filled
|
||||
if (positionStatus == PositionStatus.Filled && position.Open != null)
|
||||
@@ -1735,7 +1731,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
|
||||
await LogInformation(
|
||||
$"🔄 **Watch Mode Toggle**\nBot: `{Config.Name}`\nWatch Only: `{(Config.IsForWatchingOnly ? "ON" : "OFF")}`");
|
||||
$"🔄 Watch Mode Toggle\nBot: `{Config.Name}`\nWatch Only: `{(Config.IsForWatchingOnly ? "ON" : "OFF")}`");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1743,7 +1739,7 @@ public class TradingBotBase : ITradingBot
|
||||
/// </summary>
|
||||
public async Task StopBot()
|
||||
{
|
||||
await LogInformation($"🛑 **Bot Stopped**\nBot: `{Config.Name}`\nTicker: `{Config.Ticker}`");
|
||||
await LogInformation($"🛑 Bot Stopped\nBot: `{Config.Name}`\nTicker: `{Config.Ticker}`");
|
||||
}
|
||||
|
||||
public async Task LogInformation(string message)
|
||||
@@ -1783,7 +1779,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
var user = Account.User;
|
||||
var messageWithBotName = $"🤖 **{user.AgentName} - {Config.Name}**\n{message}";
|
||||
var messageWithBotName = $"🤖 {user.AgentName} - {Config.Name}\n{message}";
|
||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||
async messengerService =>
|
||||
{
|
||||
@@ -1831,7 +1827,7 @@ public class TradingBotBase : ITradingBot
|
||||
Positions[position.Identifier] = position;
|
||||
|
||||
Logger.LogInformation(
|
||||
$"👤 **Manual Position Opened**\nPosition: `{position.Identifier}`\nSignal: `{signal.Identifier}`\nAdded to positions list");
|
||||
$"👤 Manual Position Opened\nPosition: `{position.Identifier}`\nSignal: `{signal.Identifier}`\nAdded to positions list");
|
||||
return position;
|
||||
}
|
||||
|
||||
@@ -1846,8 +1842,8 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
||||
var signalText = $"🎯 **New Trading Signal**\n\n" +
|
||||
$"📊 **Signal Details:**\n" +
|
||||
var signalText = $"🎯 New Trading Signal\n\n" +
|
||||
$"📊 Signal Details:\n" +
|
||||
$"📈 Action: `{signal.Direction}` {Config.Ticker}\n" +
|
||||
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
||||
$"🎯 Confidence: `{signal.Confidence}`\n" +
|
||||
@@ -2144,13 +2140,13 @@ public class TradingBotBase : ITradingBot
|
||||
// Only log if there are actual changes
|
||||
if (changes.Any())
|
||||
{
|
||||
var changeMessage = "⚙️ **Configuration Updated**\n" + string.Join("\n", changes);
|
||||
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");
|
||||
"⚙️ Configuration Update\n✅ No changes detected - configuration already up to date");
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -2227,7 +2223,7 @@ public class TradingBotBase : ITradingBot
|
||||
var remainingTime = cooldownEndTime - LastCandle.Date;
|
||||
|
||||
Logger.LogWarning(
|
||||
$"⏳ **Cooldown Period Active**\n" +
|
||||
$"⏳ Cooldown Period Active\n" +
|
||||
$"Cannot open new positions\n" +
|
||||
$"Last position closed: `{LastPositionClosingTime:HH:mm:ss}`\n" +
|
||||
$"Cooldown period: `{Config.CooldownPeriod}` candles\n" +
|
||||
|
||||
@@ -76,11 +76,11 @@ namespace Managing.Application.ManageBot
|
||||
await _botRepository.DeleteBot(identifier);
|
||||
await grain.DeleteAsync();
|
||||
|
||||
var deleteMessage = $"🗑️ **Bot Deleted**\n\n" +
|
||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
||||
$"🤖 **Bot Name:** {config.Name}\n" +
|
||||
$"⏰ **Deleted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"⚠️ **Bot has been permanently deleted and all data removed.**";
|
||||
var deleteMessage = $"🗑️ Bot Deleted\n\n" +
|
||||
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||
$"🤖 Bot Name: {config.Name}\n" +
|
||||
$"⏰ Deleted At: {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"⚠️ Bot has been permanently deleted and all data removed";
|
||||
|
||||
await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
|
||||
return true;
|
||||
@@ -139,12 +139,12 @@ namespace Managing.Application.ManageBot
|
||||
{
|
||||
// First time startup
|
||||
await botGrain.StartAsync();
|
||||
var startupMessage = $"🚀 **Bot Started**\n\n" +
|
||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
||||
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
|
||||
$"⏰ **Started At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
|
||||
$"🕐 **Startup Time:** {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"✅ **Bot has been successfully started and is now active.**";
|
||||
var startupMessage = $"🚀 Bot Started\n\n" +
|
||||
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||
$"🤖 Bot Name: {grainState.Config.Name}\n" +
|
||||
$"⏰ Started At: {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
|
||||
$"🕐 Startup Time: {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"✅ Bot has been successfully started and is now active";
|
||||
|
||||
await _messengerService.SendTradeMessage(startupMessage, false, account.User);
|
||||
}
|
||||
@@ -152,12 +152,12 @@ namespace Managing.Application.ManageBot
|
||||
{
|
||||
// Restart (bot was previously down)
|
||||
await botGrain.RestartAsync();
|
||||
var restartMessage = $"🔄 **Bot Restarted**\n\n" +
|
||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
||||
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
|
||||
$"⏰ **Restarted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
|
||||
$"🕐 **New Startup Time:** {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"🚀 **Bot has been successfully restarted and is now active.**";
|
||||
var restartMessage = $"🔄 Bot Restarted\n\n" +
|
||||
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||
$"🤖 Bot Name: {grainState.Config.Name}\n" +
|
||||
$"⏰ Restarted At: {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
|
||||
$"🕐 New Startup Time: {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||
$"🚀 Bot has been successfully restarted and is now active";
|
||||
|
||||
await _messengerService.SendTradeMessage(restartMessage, false, account.User);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,22 @@ public class MessengerService : IMessengerService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendClosedPosition(Position position, User user)
|
||||
{
|
||||
var message = BuildClosePositionMessage(position);
|
||||
await _discordService.SendMessage(message);
|
||||
await _webhookService.SendMessage(message, user.TelegramChannel);
|
||||
}
|
||||
|
||||
private string BuildClosePositionMessage(Position position)
|
||||
{
|
||||
return $"Closing : {position.OriginDirection} {position.Open.Ticker} \n" +
|
||||
$"Open Price : {position.Open.Price} \n" +
|
||||
$"Closing Price : {position.Open.Price} \n" +
|
||||
$"Quantity :{position.Open.Quantity} \n" +
|
||||
$"PNL : {position.ProfitAndLoss.Realized} $";
|
||||
}
|
||||
|
||||
public async Task SendMessage(string message, string telegramChannel)
|
||||
{
|
||||
await _webhookService.SendMessage(message, telegramChannel);
|
||||
@@ -91,27 +107,28 @@ public class MessengerService : IMessengerService
|
||||
var direction = position.OriginDirection.ToString();
|
||||
var status = position.Status.ToString();
|
||||
|
||||
var message = $"🎯 Position {status}\n" +
|
||||
var message = $"🎯 {status} Position \n" +
|
||||
$"Symbol: {position.Ticker}\n" +
|
||||
$"Direction: {direction}\n" +
|
||||
$"Identifier: {position.Identifier}\n" +
|
||||
$"Initiator: {position.Initiator}\n" +
|
||||
$"Date: {position.Date:yyyy-MM-dd HH:mm:ss}";
|
||||
|
||||
if (position.Open != null)
|
||||
{
|
||||
message += $"\nOpen Trade: {position.Open.Quantity} @ {position.Open.Price:F4}";
|
||||
message += $"\nOpen Trade: {position.Open.Quantity:F5} @ {position.Open.Price:F5}";
|
||||
}
|
||||
|
||||
if (position.StopLoss != null){
|
||||
message += $"\nStop Loss: {position.StopLoss.Quantity:F5} @ {position.StopLoss.Price:F5}";
|
||||
}
|
||||
|
||||
if (position.TakeProfit1 != null){
|
||||
message += $"\nTake Profit 1: {position.TakeProfit1.Quantity:F5} @ {position.TakeProfit1.Price:F5}";
|
||||
}
|
||||
|
||||
if (position.ProfitAndLoss != null)
|
||||
{
|
||||
var pnlEmoji = position.ProfitAndLoss.Realized >= 0 ? "✅" : "❌";
|
||||
message += $"\nPnL: {pnlEmoji} ${position.ProfitAndLoss.Realized:F2}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(position.SignalIdentifier))
|
||||
{
|
||||
message += $"\nSignal: {position.SignalIdentifier}";
|
||||
message += $"\nPnL: {pnlEmoji} ${position.ProfitAndLoss.Realized:F5}";
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -194,47 +211,6 @@ public class MessengerService : IMessengerService
|
||||
}
|
||||
}
|
||||
|
||||
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 * 100) ?? 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;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("🔍 **Synth API** - Fetching leaderboard");
|
||||
_logger.LogInformation("🔍 Synth API - Fetching leaderboard");
|
||||
|
||||
var response = await _httpClient.GetAsync("/leaderboard/latest");
|
||||
|
||||
@@ -79,7 +79,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||
var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);
|
||||
|
||||
_logger.LogInformation($"📊 **Synth API** - Retrieved {miners?.Count ?? 0} miners from leaderboard");
|
||||
_logger.LogInformation($"📊 Synth API - Retrieved {miners?.Count ?? 0} miners from leaderboard");
|
||||
|
||||
return miners ?? new List<MinerInfo>();
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
|
||||
var url = $"/leaderboard/historical?start_time={startTimeStr}&end_time={endTimeStr}";
|
||||
|
||||
_logger.LogInformation($"🔍 **Synth API** - Fetching historical leaderboard from {startTime:yyyy-MM-dd HH:mm} to {endTime:yyyy-MM-dd HH:mm}");
|
||||
_logger.LogInformation($"🔍 Synth API - Fetching historical leaderboard from {startTime:yyyy-MM-dd HH:mm} to {endTime:yyyy-MM-dd HH:mm}");
|
||||
|
||||
var response = await _httpClient.GetAsync(url);
|
||||
|
||||
@@ -132,7 +132,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||
var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);
|
||||
|
||||
_logger.LogInformation($"📊 **Synth API** - Retrieved {miners?.Count ?? 0} miners from historical leaderboard");
|
||||
_logger.LogInformation($"📊 Synth API - Retrieved {miners?.Count ?? 0} miners from historical leaderboard");
|
||||
|
||||
return miners ?? new List<MinerInfo>();
|
||||
}
|
||||
@@ -193,7 +193,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
var url = $"/prediction/latest?{string.Join("&", queryParams)}";
|
||||
|
||||
_logger.LogInformation(
|
||||
$"🔮 **Synth API** - Fetching predictions for {minerUids.Count} miners, asset: {asset}, time: {timeLength}s");
|
||||
$"🔮 Synth API - Fetching predictions for {minerUids.Count} miners, asset: {asset}, time: {timeLength}s");
|
||||
|
||||
var response = await _httpClient.GetAsync(url);
|
||||
|
||||
@@ -209,7 +209,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
|
||||
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
||||
_logger.LogInformation(
|
||||
$"📈 **Synth API** - Retrieved {predictions?.Count ?? 0} predictions with {totalPaths} total simulation paths");
|
||||
$"📈 Synth API - Retrieved {predictions?.Count ?? 0} predictions with {totalPaths} total simulation paths");
|
||||
|
||||
return predictions ?? new List<MinerPrediction>();
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
var url = $"/prediction/historical?{string.Join("&", queryParams)}";
|
||||
|
||||
_logger.LogInformation(
|
||||
$"🔮 **Synth API** - Fetching historical predictions for {minerUids.Count} miners, asset: {asset}, time: {startTime:yyyy-MM-dd HH:mm}, duration: {timeLength}s");
|
||||
$"🔮 Synth API - Fetching historical predictions for {minerUids.Count} miners, asset: {asset}, time: {startTime:yyyy-MM-dd HH:mm}, duration: {timeLength}s");
|
||||
|
||||
var response = await _httpClient.GetAsync(url);
|
||||
|
||||
@@ -291,7 +291,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
||||
|
||||
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
||||
_logger.LogInformation(
|
||||
$"📈 **Synth API** - Retrieved {predictions?.Count ?? 0} historical predictions with {totalPaths} total simulation paths");
|
||||
$"📈 Synth API - Retrieved {predictions?.Count ?? 0} historical predictions with {totalPaths} total simulation paths");
|
||||
|
||||
return predictions ?? new List<MinerPrediction>();
|
||||
}
|
||||
|
||||
@@ -123,14 +123,14 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, false, DateTime.UtcNow);
|
||||
if (!predictions.Any())
|
||||
{
|
||||
_logger.LogWarning($"🚫 **Synth Probability** - No predictions available for {asset}");
|
||||
_logger.LogWarning($"🚫 Synth Probability - No predictions available for {asset}");
|
||||
return 0m;
|
||||
}
|
||||
|
||||
var probability = CalculateProbabilityFromPaths(predictions, currentPrice, targetPrice, timeHorizonSeconds,
|
||||
isLongPosition);
|
||||
|
||||
_logger.LogInformation($"🎯 **Synth Probability** - {asset}\n" +
|
||||
_logger.LogInformation($"🎯 Synth Probability - {asset}\n" +
|
||||
$"📊 Target: ${targetPrice:F2} | Current: ${currentPrice:F2}\n" +
|
||||
$"⏱️ Horizon: {timeHorizonSeconds / 3600.0:F1}h | Position: {(isLongPosition ? "LONG" : "SHORT")}\n" +
|
||||
$"🎲 Probability: {probability:P2}");
|
||||
@@ -170,7 +170,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, isBacktest, signalDate);
|
||||
if (!predictions.Any())
|
||||
{
|
||||
_logger.LogWarning($"🚫 **Synth Multi-Threshold** - No predictions available for {asset}");
|
||||
_logger.LogWarning($"🚫 Synth Multi-Threshold - No predictions available for {asset}");
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
results[threshold.Key] = probability;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"🎯 **Synth Multi-Threshold** - {asset}\n" +
|
||||
_logger.LogInformation($"🎯 Synth Multi-Threshold - {asset}\n" +
|
||||
$"📊 Calculated {results.Count} threshold probabilities\n" +
|
||||
$"⏱️ Horizon: {timeHorizonSeconds / 3600.0:F1}h | Position: {(isLongPosition ? "LONG" : "SHORT")} {(isBacktest ? "(HISTORICAL)" : "(LIVE)")}");
|
||||
|
||||
@@ -200,7 +200,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
public async Task ClearCacheAsync()
|
||||
{
|
||||
await _synthRepository.CleanupOldDataAsync(0); // Remove all data
|
||||
_logger.LogInformation("🧹 **Synth Cache** - Cleared all cached individual predictions from MongoDB");
|
||||
_logger.LogInformation("🧹 Synth Cache - Cleared all cached individual predictions from MongoDB");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -262,7 +262,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
isBacktest, signal.Date);
|
||||
|
||||
// Debug logging to understand LONG vs SHORT differences
|
||||
_logger.LogInformation($"🔍 **Debug Signal Direction Analysis** - {signal.Direction} {signal.Ticker}\n" +
|
||||
_logger.LogInformation($"🔍 Debug Signal Direction Analysis - {signal.Direction} {signal.Ticker}\n" +
|
||||
$"💰 Current Price: ${currentPrice:F2}\n" +
|
||||
$"📊 Price Targets Requested:\n" +
|
||||
$" {string.Join("\n ", priceThresholds.Select(kvp => $"{kvp.Key}: ${kvp.Value:F2}"))}\n" +
|
||||
@@ -355,7 +355,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
$"Confidence: {finalConfidence}, Risk: {result.GetUtilityRiskAssessment()}";
|
||||
|
||||
_logger.LogInformation(
|
||||
$"🎯 **Synth Signal Analysis** - {signal.Direction} {signal.Ticker} {(isBacktest ? "(BACKTEST)" : "(LIVE)")}\n" +
|
||||
$"🎯 Synth Signal Analysis - {signal.Direction} {signal.Ticker} {(isBacktest ? "(BACKTEST)" : "(LIVE)")}\n" +
|
||||
$"📊 Signal: `{signal.Identifier}`\n" +
|
||||
$"📊 SL Risk: {slProbability:P2} | TP Probability: {tpProbability:P2}\n" +
|
||||
$"🎲 Ratio: {tpSlRatio:F2}x (TP/SL) | Win/Loss: {result.WinLossRatio:F2}:1\n" +
|
||||
@@ -412,7 +412,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
Math.Min(constrainedScore * 0.75m, configLowThreshold + 0.05m); // Small buffer above LOW threshold
|
||||
|
||||
_logger.LogDebug(
|
||||
$"🚨 **Conditional Pass-Through** - Signal exceeded {result.AdverseProbabilityThreshold:P1} threshold " +
|
||||
$"🚨 Conditional Pass-Through - Signal exceeded {result.AdverseProbabilityThreshold:P1} threshold " +
|
||||
$"(SL: {result.StopLossProbability:P2}) but has redeeming qualities. " +
|
||||
$"Constrained score: {constrainedScore:F4} → Penalized: {penalizedScore:F4} → LOW confidence");
|
||||
|
||||
@@ -442,7 +442,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
};
|
||||
|
||||
// Debug logging to understand scoring breakdown
|
||||
_logger.LogDebug($"🧮 **Config-Aware Confidence** [{riskConfig.RiskTolerance}] - " +
|
||||
_logger.LogDebug($"🧮 Config-Aware Confidence [{riskConfig.RiskTolerance}] - " +
|
||||
$"Composite: {compositeScore:F4} = " +
|
||||
$"Config({configScore:F4})*0.5 + " +
|
||||
$"Threshold({thresholdAlignmentScore:F4})*0.3 + " +
|
||||
@@ -527,7 +527,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
var redeemingPercentage = (decimal)redeemingFactors / totalFactors;
|
||||
|
||||
_logger.LogDebug(
|
||||
$"🔍 **Redeeming Qualities Check** - {redeemingFactors}/{totalFactors} factors positive ({redeemingPercentage:P0}). " +
|
||||
$"🔍 Redeeming Qualities Check - {redeemingFactors}/{totalFactors} factors positive ({redeemingPercentage:P0}). " +
|
||||
$"Kelly: {result.KellyFraction:P2} (>2x{riskConfig.KellyMinimumThreshold:P2}), " +
|
||||
$"EMV: ${result.ExpectedMonetaryValue:F2} (>$100), " +
|
||||
$"TP/SL: {result.TpSlRatio:F2}x (>2.0), " +
|
||||
@@ -842,7 +842,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
if (liquidationProbability > config.MaxLiquidationProbability)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
$"🚫 **Synth Risk Block**\n" +
|
||||
$"🚫 Synth Risk Block\n" +
|
||||
$"Liquidation probability too high: {liquidationProbability:P2}\n" +
|
||||
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}\n" +
|
||||
$"💰 Est. liquidation: ${estimatedLiquidationPrice:F2}\n" +
|
||||
@@ -851,7 +851,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
$"✅ **Synth Risk Check**\n" +
|
||||
$"✅ Synth Risk Check\n" +
|
||||
$"Liquidation probability acceptable: {liquidationProbability:P2}\n" +
|
||||
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}");
|
||||
|
||||
@@ -893,7 +893,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
{
|
||||
result.ShouldWarn = true;
|
||||
result.WarningMessage =
|
||||
$"⚠️ **High Liquidation Risk**\n" +
|
||||
$"⚠️ High Liquidation Risk\n" +
|
||||
$"Position: `{positionIdentifier}`\n" +
|
||||
$"🎲 Liquidation probability: {liquidationProbability:P2}\n" +
|
||||
$"💰 Stop loss: ${liquidationPrice:F2}\n" +
|
||||
@@ -906,7 +906,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
{
|
||||
result.ShouldAutoClose = true;
|
||||
result.EmergencyMessage =
|
||||
$"🚨 **Emergency Close**\n" +
|
||||
$"🚨 Emergency Close\n" +
|
||||
$"Extremely high liquidation risk: {liquidationProbability:P2}\n" +
|
||||
$"Auto-closing position for protection";
|
||||
}
|
||||
@@ -974,7 +974,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
if (cachedLeaderboard != null)
|
||||
{
|
||||
leaderboard = cachedLeaderboard.Miners;
|
||||
_logger.LogDebug($"📦 **Synth Cache** - Using cached leaderboard for {asset}");
|
||||
_logger.LogDebug($"📦 Synth Cache - Using cached leaderboard for {asset}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1036,7 +1036,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
if (cachedIndividualPredictions.Count == topMinerUids.Count)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
$"📦 **Synth Individual Cache** - Using all cached individual predictions for {asset} {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
$"📦 Synth Individual Cache - Using all cached individual predictions for {asset} {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
|
||||
return cachedIndividualPredictions.Select(p => p.Prediction).ToList();
|
||||
}
|
||||
@@ -1046,7 +1046,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
var missingMinerUids = topMinerUids.Where(uid => !cachedMinerUids.Contains(uid)).ToList();
|
||||
|
||||
_logger.LogInformation(
|
||||
$"🔄 **Synth Individual Cache** - Partial cache hit for {asset}: {cachedIndividualPredictions.Count}/{topMinerUids.Count} cached, fetching {missingMinerUids.Count} fresh predictions {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
$"🔄 Synth Individual Cache - Partial cache hit for {asset}: {cachedIndividualPredictions.Count}/{topMinerUids.Count} cached, fetching {missingMinerUids.Count} fresh predictions {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
|
||||
// Fetch missing predictions from API
|
||||
List<MinerPrediction> freshPredictions;
|
||||
@@ -1112,7 +1112,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
$"📈 **Synth Individual Cache** - Total predictions assembled: {allPredictions.Count} for {asset} {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
$"📈 Synth Individual Cache - Total predictions assembled: {allPredictions.Count} for {asset} {(isBacktest ? "HISTORICAL" : "LIVE")}");
|
||||
|
||||
return allPredictions;
|
||||
}
|
||||
@@ -1186,7 +1186,7 @@ public class SynthPredictionService : ISynthPredictionService
|
||||
var probability = totalPaths > 0 ? (decimal)pathsCrossingThreshold / totalPaths : 0m;
|
||||
|
||||
_logger.LogDebug(
|
||||
$"🧮 **Probability Calculation** - {pathsCrossingThreshold}/{totalPaths} paths crossed threshold = {probability:P2}");
|
||||
$"🧮 Probability Calculation - {pathsCrossingThreshold}/{totalPaths} paths crossed threshold = {probability:P2}");
|
||||
|
||||
return probability;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user