Update messaging
This commit is contained in:
@@ -27,4 +27,5 @@ public interface IMessengerService
|
|||||||
Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate);
|
Task SendFundingRateUpdate(FundingRate oldRate, FundingRate newRate);
|
||||||
Task SendBacktestNotification(Backtest backtest);
|
Task SendBacktestNotification(Backtest backtest);
|
||||||
Task SendGeneticAlgorithmNotification(GeneticRequest request, double bestFitness, object? bestChromosome);
|
Task SendGeneticAlgorithmNotification(GeneticRequest request, double bestFitness, object? bestChromosome);
|
||||||
|
Task SendClosedPosition(Position position, User user);
|
||||||
}
|
}
|
||||||
@@ -859,18 +859,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var message = isSuccess
|
var message = isSuccess
|
||||||
? $"🔄 **Automatic Swap Successful**\n\n" +
|
? $"🔄 Automatic Swap Successful\n\n" +
|
||||||
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
|
$"🎯 Bot: {_tradingBot?.Identifier}\n" +
|
||||||
$"💰 **Amount:** {amount} USDC → ETH\n" +
|
$"💰 Amount: {amount} USDC → ETH\n" +
|
||||||
$"✅ **Status:** Success\n" +
|
$"✅ Status: Success\n" +
|
||||||
$"🔗 **Transaction:** {transactionHash}\n" +
|
$"🔗 Transaction: {transactionHash}\n" +
|
||||||
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
|
$"⏰ Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
|
||||||
: $"❌ **Automatic Swap Failed**\n\n" +
|
: $"❌ Automatic Swap Failed\n\n" +
|
||||||
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
|
$"🎯 Bot: {_tradingBot?.Identifier}\n" +
|
||||||
$"💰 **Amount:** {amount} USDC → ETH\n" +
|
$"💰 Amount: {amount} USDC → ETH\n" +
|
||||||
$"❌ **Status:** Failed\n" +
|
$"❌ Status: Failed\n" +
|
||||||
$"⚠️ **Error:** {errorMessage}\n" +
|
$"⚠️ Error: {errorMessage}\n" +
|
||||||
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
$"⏰ Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
|
||||||
|
|
||||||
// Send notification via webhook service
|
// Send notification via webhook service
|
||||||
await ServiceScopeHelpers.WithScopedService<IWebhookService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IWebhookService>(_scopeFactory,
|
||||||
|
|||||||
@@ -82,16 +82,16 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
case BotStatus.Saved:
|
case BotStatus.Saved:
|
||||||
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
||||||
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
|
var startupMessage = $"🚀 Bot Started Successfully\n\n" +
|
||||||
$"📊 **Trading Setup:**\n" +
|
$"📊 Trading Setup:\n" +
|
||||||
$"🎯 Ticker: `{Config.Ticker}`\n" +
|
$"🎯 Ticker: `{Config.Ticker}`\n" +
|
||||||
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
||||||
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
|
$"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
|
||||||
$"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
|
$"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
|
||||||
$"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
|
$"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
|
||||||
$"📈 **Active Indicators:** `{string.Join(", ", indicatorNames)}`\n\n" +
|
$"📈 Active Indicators: `{string.Join(", ", indicatorNames)}`\n\n" +
|
||||||
$"✅ Ready to monitor signals and execute trades!\n" +
|
$"✅ Ready to monitor signals and execute trades\n" +
|
||||||
$"📢 I'll notify you when signals are triggered.";
|
$"📢 Notifications will be sent when positions are triggered";
|
||||||
|
|
||||||
await LogInformation(startupMessage);
|
await LogInformation(startupMessage);
|
||||||
break;
|
break;
|
||||||
@@ -101,7 +101,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
|
|
||||||
case BotStatus.Stopped:
|
case BotStatus.Stopped:
|
||||||
// If status was Stopped we log a message to inform the user that the bot is restarting
|
// 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" +
|
$"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
|
||||||
$"✅ Ready to continue trading");
|
$"✅ Ready to continue trading");
|
||||||
break;
|
break;
|
||||||
@@ -264,7 +264,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
Signals.Add(recreatedSignal.Identifier, recreatedSignal);
|
Signals.Add(recreatedSignal.Identifier, recreatedSignal);
|
||||||
|
|
||||||
await LogInformation(
|
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;
|
return recreatedSignal;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -283,7 +283,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (signalForPosition == null)
|
if (signalForPosition == null)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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
|
// Recreate the signal based on position information
|
||||||
signalForPosition = await RecreateSignalFromPosition(position);
|
signalForPosition = await RecreateSignalFromPosition(position);
|
||||||
@@ -299,7 +299,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (signalForPosition.Status != SignalStatus.PositionOpen && position.Status != PositionStatus.Finished)
|
if (signalForPosition.Status != SignalStatus.PositionOpen && position.Status != PositionStatus.Finished)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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);
|
SetSignalStatus(signalForPosition.Identifier, SignalStatus.PositionOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (LastCandle != null && signal.Date < LastCandle.Date)
|
if (LastCandle != null && signal.Date < LastCandle.Date)
|
||||||
{
|
{
|
||||||
await LogWarning(
|
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);
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
// Position already exists for this signal, update signal status
|
// Position already exists for this signal, update signal status
|
||||||
await LogInformation(
|
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);
|
SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -342,7 +342,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await LogWarning(
|
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)
|
if (timeSinceRequest.TotalMinutes >= waitTimeMinutes)
|
||||||
{
|
{
|
||||||
await LogWarning(
|
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
|
try
|
||||||
{
|
{
|
||||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||||
@@ -495,7 +495,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
await exchangeService.CancelOrder(Account, Config.Ticker);
|
await exchangeService.CancelOrder(Account, Config.Ticker);
|
||||||
});
|
});
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"✅ **Orders Canceled**\nSuccessfully canceled all orders for: `{Config.Ticker}`");
|
$"✅ Orders Canceled\nSuccessfully canceled all orders for: `{Config.Ticker}`");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -510,7 +510,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
var remainingMinutes = waitTimeMinutes - timeSinceRequest.TotalMinutes;
|
var remainingMinutes = waitTimeMinutes - timeSinceRequest.TotalMinutes;
|
||||||
await LogInformation(
|
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)
|
else if (ordersCount == 2)
|
||||||
@@ -518,7 +518,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
// TODO: This should never happen, but just in case
|
// TODO: This should never happen, but just in case
|
||||||
// Check if position is already open on broker with 2 orders
|
// Check if position is already open on broker with 2 orders
|
||||||
await LogInformation(
|
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;
|
Position brokerPosition = null;
|
||||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||||
@@ -531,7 +531,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (brokerPosition != null)
|
if (brokerPosition != null)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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
|
// Calculate net PnL after fees for broker position
|
||||||
var brokerNetPnL = brokerPosition.GetNetPnL();
|
var brokerNetPnL = brokerPosition.GetNetPnL();
|
||||||
@@ -564,19 +564,19 @@ public class TradingBotBase : ITradingBot
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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
|
else
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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
|
else
|
||||||
{
|
{
|
||||||
await LogWarning(
|
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
|
// 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
|
// 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";
|
var profitStatus = isPositionInProfit ? "in profit" : "at a loss";
|
||||||
|
|
||||||
await LogInformation(
|
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);
|
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -651,7 +651,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
executionPrice, true);
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
@@ -671,7 +671,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
@@ -690,7 +690,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
executionPrice, true);
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
@@ -717,7 +717,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
executionPrice, true);
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
@@ -737,7 +737,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
executionPrice, positionForSignal.TakeProfit2 == null);
|
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
@@ -756,7 +756,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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,
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
executionPrice, true);
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
@@ -845,7 +845,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (openedPosition.OriginDirection == signal.Direction)
|
if (openedPosition.OriginDirection == signal.Direction)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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);
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -863,19 +863,19 @@ public class TradingBotBase : ITradingBot
|
|||||||
: "FlipOnlyWhenInProfit is disabled";
|
: "FlipOnlyWhenInProfit is disabled";
|
||||||
|
|
||||||
await LogInformation(
|
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 CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
|
||||||
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
|
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
|
||||||
var newPosition = await OpenPosition(signal);
|
var newPosition = await OpenPosition(signal);
|
||||||
await LogInformation(
|
await LogInformation(
|
||||||
$"✅ **Position Flipped**\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
|
$"✅ Position Flipped\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
|
||||||
return newPosition;
|
return newPosition;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var currentPnl = openedPosition.ProfitAndLoss?.Realized ?? 0;
|
var currentPnl = openedPosition.ProfitAndLoss?.Realized ?? 0;
|
||||||
await LogInformation(
|
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);
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||||
return null;
|
return null;
|
||||||
@@ -884,7 +884,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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);
|
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -899,9 +899,6 @@ public class TradingBotBase : ITradingBot
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
|
||||||
$"🚀 **Opening Position**\nTime: `{signal.Date:HH:mm:ss}`\nSignal: `{signal.Identifier}`");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var command = new OpenPositionRequest(
|
var command = new OpenPositionRequest(
|
||||||
@@ -936,7 +933,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||||
async messengerService => { await messengerService.SendPosition(position); });
|
async messengerService => { await messengerService.SendClosedPosition(position, Account.User); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation($"Position requested");
|
Logger.LogInformation($"Position requested");
|
||||||
@@ -979,7 +976,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
// TODO : check if its a startup cycle
|
// TODO : check if its a startup cycle
|
||||||
if (!Config.IsForBacktest && ExecutionCount == 0)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,7 +1056,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (lastPosition.OriginDirection == signal.Direction)
|
if (lastPosition.OriginDirection == signal.Direction)
|
||||||
{
|
{
|
||||||
await LogWarning(
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,7 +1101,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1120,7 +1117,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation(
|
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;
|
decimal quantity = 0;
|
||||||
|
|
||||||
@@ -1202,7 +1199,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInformation(
|
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;
|
List<Position> positionHistory = null;
|
||||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||||
@@ -1225,7 +1222,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (gmxPosition != null && gmxPosition.ProfitAndLoss != null)
|
if (gmxPosition != null && gmxPosition.ProfitAndLoss != null)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"✅ **GMX Position History Found**\n" +
|
$"✅ GMX Position History Found\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"GMX Realized PnL (after fees): `${gmxPosition.ProfitAndLoss.Realized:F2}`\n" +
|
$"GMX Realized PnL (after fees): `${gmxPosition.ProfitAndLoss.Realized:F2}`\n" +
|
||||||
$"Bot's UI Fees: `${position.UiFees:F2}`\n" +
|
$"Bot's UI Fees: `${position.UiFees:F2}`\n" +
|
||||||
@@ -1289,7 +1286,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"📊 **Position Reconciliation Complete**\n" +
|
$"📊 Position Reconciliation Complete\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"Closing Price: `${closingPrice:F2}`\n" +
|
$"Closing Price: `${closingPrice:F2}`\n" +
|
||||||
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
||||||
@@ -1302,7 +1299,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogWarning(
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1406,7 +1403,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"🛑 **Stop Loss Execution Confirmed**\n" +
|
$"🛑 Stop Loss Execution Confirmed\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"SL Price: `${closingPrice:F2}` was hit (was `${position.StopLoss.Price:F2}`)\n" +
|
$"SL Price: `${closingPrice:F2}` was hit (was `${position.StopLoss.Price:F2}`)\n" +
|
||||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||||
@@ -1429,7 +1426,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"🎯 **Take Profit Execution Confirmed**\n" +
|
$"🎯 Take Profit Execution Confirmed\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"TP Price: `${closingPrice:F2}` was hit (was `${position.TakeProfit1.Price:F2}`)\n" +
|
$"TP Price: `${closingPrice:F2}` was hit (was `${position.TakeProfit1.Price:F2}`)\n" +
|
||||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||||
@@ -1480,7 +1477,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"✋ **Manual/Exchange Close Detected**\n" +
|
$"✋ Manual/Exchange Close Detected\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"SL: `${position.StopLoss.Price:F2}` | TP: `${position.TakeProfit1.Price:F2}`\n" +
|
$"SL: `${position.StopLoss.Price:F2}` | TP: `${position.TakeProfit1.Price:F2}`\n" +
|
||||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent: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)
|
if (position.Open?.Status == TradeStatus.Filled)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(
|
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)
|
if (position.ProfitAndLoss != null)
|
||||||
{
|
{
|
||||||
Config.BotTradingBalance += position.ProfitAndLoss.Realized;
|
Config.BotTradingBalance += position.ProfitAndLoss.Realized;
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
string.Format("💰 **Balance Updated**\nNew bot trading balance: `${0:F2}`",
|
string.Format("💰 Balance Updated\nNew bot trading balance: `${0:F2}`",
|
||||||
Config.BotTradingBalance));
|
Config.BotTradingBalance));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogInformation(
|
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
|
else
|
||||||
@@ -1574,10 +1571,9 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||||
messengerService =>
|
async messengerService =>
|
||||||
{
|
{
|
||||||
messengerService.SendClosingPosition(position);
|
await messengerService.SendClosedPosition(position, Account.User);
|
||||||
return Task.CompletedTask;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1646,7 +1642,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
Positions.Values.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus;
|
Positions.Values.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus;
|
||||||
await LogInformation(
|
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
|
// Update Open trade status when position becomes Filled
|
||||||
if (positionStatus == PositionStatus.Filled && position.Open != null)
|
if (positionStatus == PositionStatus.Filled && position.Open != null)
|
||||||
@@ -1735,7 +1731,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
|
Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
|
||||||
await LogInformation(
|
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>
|
/// <summary>
|
||||||
@@ -1743,7 +1739,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task StopBot()
|
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)
|
public async Task LogInformation(string message)
|
||||||
@@ -1783,7 +1779,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
var user = Account.User;
|
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,
|
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||||
async messengerService =>
|
async messengerService =>
|
||||||
{
|
{
|
||||||
@@ -1831,7 +1827,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
Positions[position.Identifier] = position;
|
Positions[position.Identifier] = position;
|
||||||
|
|
||||||
Logger.LogInformation(
|
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;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1846,8 +1842,8 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
|
||||||
var signalText = $"🎯 **New Trading Signal**\n\n" +
|
var signalText = $"🎯 New Trading Signal\n\n" +
|
||||||
$"📊 **Signal Details:**\n" +
|
$"📊 Signal Details:\n" +
|
||||||
$"📈 Action: `{signal.Direction}` {Config.Ticker}\n" +
|
$"📈 Action: `{signal.Direction}` {Config.Ticker}\n" +
|
||||||
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
$"⏰ Timeframe: `{Config.Timeframe}`\n" +
|
||||||
$"🎯 Confidence: `{signal.Confidence}`\n" +
|
$"🎯 Confidence: `{signal.Confidence}`\n" +
|
||||||
@@ -2144,13 +2140,13 @@ public class TradingBotBase : ITradingBot
|
|||||||
// Only log if there are actual changes
|
// Only log if there are actual changes
|
||||||
if (changes.Any())
|
if (changes.Any())
|
||||||
{
|
{
|
||||||
var changeMessage = "⚙️ **Configuration Updated**\n" + string.Join("\n", changes);
|
var changeMessage = "⚙️ Configuration Updated\n" + string.Join("\n", changes);
|
||||||
await LogInformation(changeMessage);
|
await LogInformation(changeMessage);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await LogInformation(
|
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;
|
return true;
|
||||||
@@ -2227,7 +2223,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
var remainingTime = cooldownEndTime - LastCandle.Date;
|
var remainingTime = cooldownEndTime - LastCandle.Date;
|
||||||
|
|
||||||
Logger.LogWarning(
|
Logger.LogWarning(
|
||||||
$"⏳ **Cooldown Period Active**\n" +
|
$"⏳ Cooldown Period Active\n" +
|
||||||
$"Cannot open new positions\n" +
|
$"Cannot open new positions\n" +
|
||||||
$"Last position closed: `{LastPositionClosingTime:HH:mm:ss}`\n" +
|
$"Last position closed: `{LastPositionClosingTime:HH:mm:ss}`\n" +
|
||||||
$"Cooldown period: `{Config.CooldownPeriod}` candles\n" +
|
$"Cooldown period: `{Config.CooldownPeriod}` candles\n" +
|
||||||
|
|||||||
@@ -76,11 +76,11 @@ namespace Managing.Application.ManageBot
|
|||||||
await _botRepository.DeleteBot(identifier);
|
await _botRepository.DeleteBot(identifier);
|
||||||
await grain.DeleteAsync();
|
await grain.DeleteAsync();
|
||||||
|
|
||||||
var deleteMessage = $"🗑️ **Bot Deleted**\n\n" +
|
var deleteMessage = $"🗑️ Bot Deleted\n\n" +
|
||||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||||
$"🤖 **Bot Name:** {config.Name}\n" +
|
$"🤖 Bot Name: {config.Name}\n" +
|
||||||
$"⏰ **Deleted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
$"⏰ Deleted At: {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||||
$"⚠️ **Bot has been permanently deleted and all data removed.**";
|
$"⚠️ Bot has been permanently deleted and all data removed";
|
||||||
|
|
||||||
await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
|
await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
|
||||||
return true;
|
return true;
|
||||||
@@ -139,12 +139,12 @@ namespace Managing.Application.ManageBot
|
|||||||
{
|
{
|
||||||
// First time startup
|
// First time startup
|
||||||
await botGrain.StartAsync();
|
await botGrain.StartAsync();
|
||||||
var startupMessage = $"🚀 **Bot Started**\n\n" +
|
var startupMessage = $"🚀 Bot Started\n\n" +
|
||||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||||
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
|
$"🤖 Bot Name: {grainState.Config.Name}\n" +
|
||||||
$"⏰ **Started At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\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" +
|
$"🕐 Startup Time: {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
|
||||||
$"✅ **Bot has been successfully started and is now active.**";
|
$"✅ Bot has been successfully started and is now active";
|
||||||
|
|
||||||
await _messengerService.SendTradeMessage(startupMessage, false, account.User);
|
await _messengerService.SendTradeMessage(startupMessage, false, account.User);
|
||||||
}
|
}
|
||||||
@@ -152,12 +152,12 @@ namespace Managing.Application.ManageBot
|
|||||||
{
|
{
|
||||||
// Restart (bot was previously down)
|
// Restart (bot was previously down)
|
||||||
await botGrain.RestartAsync();
|
await botGrain.RestartAsync();
|
||||||
var restartMessage = $"🔄 **Bot Restarted**\n\n" +
|
var restartMessage = $"🔄 Bot Restarted\n\n" +
|
||||||
$"🎯 **Agent:** {account.User.AgentName}\n" +
|
$"🎯 Agent: {account.User.AgentName}\n" +
|
||||||
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
|
$"🤖 Bot Name: {grainState.Config.Name}\n" +
|
||||||
$"⏰ **Restarted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\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" +
|
$"🕐 New Startup Time: {grainState.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, account.User);
|
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)
|
public async Task SendMessage(string message, string telegramChannel)
|
||||||
{
|
{
|
||||||
await _webhookService.SendMessage(message, telegramChannel);
|
await _webhookService.SendMessage(message, telegramChannel);
|
||||||
@@ -91,27 +107,28 @@ public class MessengerService : IMessengerService
|
|||||||
var direction = position.OriginDirection.ToString();
|
var direction = position.OriginDirection.ToString();
|
||||||
var status = position.Status.ToString();
|
var status = position.Status.ToString();
|
||||||
|
|
||||||
var message = $"🎯 Position {status}\n" +
|
var message = $"🎯 {status} Position \n" +
|
||||||
$"Symbol: {position.Ticker}\n" +
|
$"Symbol: {position.Ticker}\n" +
|
||||||
$"Direction: {direction}\n" +
|
$"Direction: {direction}\n" +
|
||||||
$"Identifier: {position.Identifier}\n" +
|
|
||||||
$"Initiator: {position.Initiator}\n" +
|
|
||||||
$"Date: {position.Date:yyyy-MM-dd HH:mm:ss}";
|
$"Date: {position.Date:yyyy-MM-dd HH:mm:ss}";
|
||||||
|
|
||||||
if (position.Open != null)
|
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)
|
if (position.ProfitAndLoss != null)
|
||||||
{
|
{
|
||||||
var pnlEmoji = position.ProfitAndLoss.Realized >= 0 ? "✅" : "❌";
|
var pnlEmoji = position.ProfitAndLoss.Realized >= 0 ? "✅" : "❌";
|
||||||
message += $"\nPnL: {pnlEmoji} ${position.ProfitAndLoss.Realized:F2}";
|
message += $"\nPnL: {pnlEmoji} ${position.ProfitAndLoss.Realized:F5}";
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(position.SignalIdentifier))
|
|
||||||
{
|
|
||||||
message += $"\nSignal: {position.SignalIdentifier}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
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)
|
private string BuildBacktestMessage(Backtest backtest)
|
||||||
{
|
{
|
||||||
var config = backtest.Config;
|
var config = backtest.Config;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation("🔍 **Synth API** - Fetching leaderboard");
|
_logger.LogInformation("🔍 Synth API - Fetching leaderboard");
|
||||||
|
|
||||||
var response = await _httpClient.GetAsync("/leaderboard/latest");
|
var response = await _httpClient.GetAsync("/leaderboard/latest");
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||||
var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);
|
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>();
|
return miners ?? new List<MinerInfo>();
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
|
|
||||||
var url = $"/leaderboard/historical?start_time={startTimeStr}&end_time={endTimeStr}";
|
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);
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||||
var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);
|
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>();
|
return miners ?? new List<MinerInfo>();
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
var url = $"/prediction/latest?{string.Join("&", queryParams)}";
|
var url = $"/prediction/latest?{string.Join("&", queryParams)}";
|
||||||
|
|
||||||
_logger.LogInformation(
|
_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);
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
|
|
||||||
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
||||||
_logger.LogInformation(
|
_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>();
|
return predictions ?? new List<MinerPrediction>();
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
var url = $"/prediction/historical?{string.Join("&", queryParams)}";
|
var url = $"/prediction/historical?{string.Join("&", queryParams)}";
|
||||||
|
|
||||||
_logger.LogInformation(
|
_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);
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ public class SynthApiClient : ISynthApiClient, IDisposable
|
|||||||
|
|
||||||
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
|
||||||
_logger.LogInformation(
|
_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>();
|
return predictions ?? new List<MinerPrediction>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,14 +123,14 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, false, DateTime.UtcNow);
|
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, false, DateTime.UtcNow);
|
||||||
if (!predictions.Any())
|
if (!predictions.Any())
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"🚫 **Synth Probability** - No predictions available for {asset}");
|
_logger.LogWarning($"🚫 Synth Probability - No predictions available for {asset}");
|
||||||
return 0m;
|
return 0m;
|
||||||
}
|
}
|
||||||
|
|
||||||
var probability = CalculateProbabilityFromPaths(predictions, currentPrice, targetPrice, timeHorizonSeconds,
|
var probability = CalculateProbabilityFromPaths(predictions, currentPrice, targetPrice, timeHorizonSeconds,
|
||||||
isLongPosition);
|
isLongPosition);
|
||||||
|
|
||||||
_logger.LogInformation($"🎯 **Synth Probability** - {asset}\n" +
|
_logger.LogInformation($"🎯 Synth Probability - {asset}\n" +
|
||||||
$"📊 Target: ${targetPrice:F2} | Current: ${currentPrice:F2}\n" +
|
$"📊 Target: ${targetPrice:F2} | Current: ${currentPrice:F2}\n" +
|
||||||
$"⏱️ Horizon: {timeHorizonSeconds / 3600.0:F1}h | Position: {(isLongPosition ? "LONG" : "SHORT")}\n" +
|
$"⏱️ Horizon: {timeHorizonSeconds / 3600.0:F1}h | Position: {(isLongPosition ? "LONG" : "SHORT")}\n" +
|
||||||
$"🎲 Probability: {probability:P2}");
|
$"🎲 Probability: {probability:P2}");
|
||||||
@@ -170,7 +170,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, isBacktest, signalDate);
|
await GetCachedPredictionsAsync(asset, timeHorizonSeconds, config, isBacktest, signalDate);
|
||||||
if (!predictions.Any())
|
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;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
results[threshold.Key] = probability;
|
results[threshold.Key] = probability;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation($"🎯 **Synth Multi-Threshold** - {asset}\n" +
|
_logger.LogInformation($"🎯 Synth Multi-Threshold - {asset}\n" +
|
||||||
$"📊 Calculated {results.Count} threshold probabilities\n" +
|
$"📊 Calculated {results.Count} threshold probabilities\n" +
|
||||||
$"⏱️ Horizon: {timeHorizonSeconds / 3600.0:F1}h | Position: {(isLongPosition ? "LONG" : "SHORT")} {(isBacktest ? "(HISTORICAL)" : "(LIVE)")}");
|
$"⏱️ 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()
|
public async Task ClearCacheAsync()
|
||||||
{
|
{
|
||||||
await _synthRepository.CleanupOldDataAsync(0); // Remove all data
|
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>
|
/// <summary>
|
||||||
@@ -262,7 +262,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
isBacktest, signal.Date);
|
isBacktest, signal.Date);
|
||||||
|
|
||||||
// Debug logging to understand LONG vs SHORT differences
|
// 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" +
|
$"💰 Current Price: ${currentPrice:F2}\n" +
|
||||||
$"📊 Price Targets Requested:\n" +
|
$"📊 Price Targets Requested:\n" +
|
||||||
$" {string.Join("\n ", priceThresholds.Select(kvp => $"{kvp.Key}: ${kvp.Value:F2}"))}\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()}";
|
$"Confidence: {finalConfidence}, Risk: {result.GetUtilityRiskAssessment()}";
|
||||||
|
|
||||||
_logger.LogInformation(
|
_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" +
|
$"📊 Signal: `{signal.Identifier}`\n" +
|
||||||
$"📊 SL Risk: {slProbability:P2} | TP Probability: {tpProbability:P2}\n" +
|
$"📊 SL Risk: {slProbability:P2} | TP Probability: {tpProbability:P2}\n" +
|
||||||
$"🎲 Ratio: {tpSlRatio:F2}x (TP/SL) | Win/Loss: {result.WinLossRatio:F2}:1\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
|
Math.Min(constrainedScore * 0.75m, configLowThreshold + 0.05m); // Small buffer above LOW threshold
|
||||||
|
|
||||||
_logger.LogDebug(
|
_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. " +
|
$"(SL: {result.StopLossProbability:P2}) but has redeeming qualities. " +
|
||||||
$"Constrained score: {constrainedScore:F4} → Penalized: {penalizedScore:F4} → LOW confidence");
|
$"Constrained score: {constrainedScore:F4} → Penalized: {penalizedScore:F4} → LOW confidence");
|
||||||
|
|
||||||
@@ -442,7 +442,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Debug logging to understand scoring breakdown
|
// Debug logging to understand scoring breakdown
|
||||||
_logger.LogDebug($"🧮 **Config-Aware Confidence** [{riskConfig.RiskTolerance}] - " +
|
_logger.LogDebug($"🧮 Config-Aware Confidence [{riskConfig.RiskTolerance}] - " +
|
||||||
$"Composite: {compositeScore:F4} = " +
|
$"Composite: {compositeScore:F4} = " +
|
||||||
$"Config({configScore:F4})*0.5 + " +
|
$"Config({configScore:F4})*0.5 + " +
|
||||||
$"Threshold({thresholdAlignmentScore:F4})*0.3 + " +
|
$"Threshold({thresholdAlignmentScore:F4})*0.3 + " +
|
||||||
@@ -527,7 +527,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
var redeemingPercentage = (decimal)redeemingFactors / totalFactors;
|
var redeemingPercentage = (decimal)redeemingFactors / totalFactors;
|
||||||
|
|
||||||
_logger.LogDebug(
|
_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}), " +
|
$"Kelly: {result.KellyFraction:P2} (>2x{riskConfig.KellyMinimumThreshold:P2}), " +
|
||||||
$"EMV: ${result.ExpectedMonetaryValue:F2} (>$100), " +
|
$"EMV: ${result.ExpectedMonetaryValue:F2} (>$100), " +
|
||||||
$"TP/SL: {result.TpSlRatio:F2}x (>2.0), " +
|
$"TP/SL: {result.TpSlRatio:F2}x (>2.0), " +
|
||||||
@@ -842,7 +842,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
if (liquidationProbability > config.MaxLiquidationProbability)
|
if (liquidationProbability > config.MaxLiquidationProbability)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
$"🚫 **Synth Risk Block**\n" +
|
$"🚫 Synth Risk Block\n" +
|
||||||
$"Liquidation probability too high: {liquidationProbability:P2}\n" +
|
$"Liquidation probability too high: {liquidationProbability:P2}\n" +
|
||||||
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}\n" +
|
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}\n" +
|
||||||
$"💰 Est. liquidation: ${estimatedLiquidationPrice:F2}\n" +
|
$"💰 Est. liquidation: ${estimatedLiquidationPrice:F2}\n" +
|
||||||
@@ -851,7 +851,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
$"✅ **Synth Risk Check**\n" +
|
$"✅ Synth Risk Check\n" +
|
||||||
$"Liquidation probability acceptable: {liquidationProbability:P2}\n" +
|
$"Liquidation probability acceptable: {liquidationProbability:P2}\n" +
|
||||||
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}");
|
$"📊 Max allowed: {config.MaxLiquidationProbability:P2}");
|
||||||
|
|
||||||
@@ -893,7 +893,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
{
|
{
|
||||||
result.ShouldWarn = true;
|
result.ShouldWarn = true;
|
||||||
result.WarningMessage =
|
result.WarningMessage =
|
||||||
$"⚠️ **High Liquidation Risk**\n" +
|
$"⚠️ High Liquidation Risk\n" +
|
||||||
$"Position: `{positionIdentifier}`\n" +
|
$"Position: `{positionIdentifier}`\n" +
|
||||||
$"🎲 Liquidation probability: {liquidationProbability:P2}\n" +
|
$"🎲 Liquidation probability: {liquidationProbability:P2}\n" +
|
||||||
$"💰 Stop loss: ${liquidationPrice:F2}\n" +
|
$"💰 Stop loss: ${liquidationPrice:F2}\n" +
|
||||||
@@ -906,7 +906,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
{
|
{
|
||||||
result.ShouldAutoClose = true;
|
result.ShouldAutoClose = true;
|
||||||
result.EmergencyMessage =
|
result.EmergencyMessage =
|
||||||
$"🚨 **Emergency Close**\n" +
|
$"🚨 Emergency Close\n" +
|
||||||
$"Extremely high liquidation risk: {liquidationProbability:P2}\n" +
|
$"Extremely high liquidation risk: {liquidationProbability:P2}\n" +
|
||||||
$"Auto-closing position for protection";
|
$"Auto-closing position for protection";
|
||||||
}
|
}
|
||||||
@@ -974,7 +974,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
if (cachedLeaderboard != null)
|
if (cachedLeaderboard != null)
|
||||||
{
|
{
|
||||||
leaderboard = cachedLeaderboard.Miners;
|
leaderboard = cachedLeaderboard.Miners;
|
||||||
_logger.LogDebug($"📦 **Synth Cache** - Using cached leaderboard for {asset}");
|
_logger.LogDebug($"📦 Synth Cache - Using cached leaderboard for {asset}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1036,7 +1036,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
if (cachedIndividualPredictions.Count == topMinerUids.Count)
|
if (cachedIndividualPredictions.Count == topMinerUids.Count)
|
||||||
{
|
{
|
||||||
_logger.LogDebug(
|
_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();
|
return cachedIndividualPredictions.Select(p => p.Prediction).ToList();
|
||||||
}
|
}
|
||||||
@@ -1046,7 +1046,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
var missingMinerUids = topMinerUids.Where(uid => !cachedMinerUids.Contains(uid)).ToList();
|
var missingMinerUids = topMinerUids.Where(uid => !cachedMinerUids.Contains(uid)).ToList();
|
||||||
|
|
||||||
_logger.LogInformation(
|
_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
|
// Fetch missing predictions from API
|
||||||
List<MinerPrediction> freshPredictions;
|
List<MinerPrediction> freshPredictions;
|
||||||
@@ -1112,7 +1112,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation(
|
_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;
|
return allPredictions;
|
||||||
}
|
}
|
||||||
@@ -1186,7 +1186,7 @@ public class SynthPredictionService : ISynthPredictionService
|
|||||||
var probability = totalPaths > 0 ? (decimal)pathsCrossingThreshold / totalPaths : 0m;
|
var probability = totalPaths > 0 ? (decimal)pathsCrossingThreshold / totalPaths : 0m;
|
||||||
|
|
||||||
_logger.LogDebug(
|
_logger.LogDebug(
|
||||||
$"🧮 **Probability Calculation** - {pathsCrossingThreshold}/{totalPaths} paths crossed threshold = {probability:P2}");
|
$"🧮 Probability Calculation - {pathsCrossingThreshold}/{totalPaths} paths crossed threshold = {probability:P2}");
|
||||||
|
|
||||||
return probability;
|
return probability;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ namespace Managing.Infrastructure.Messengers.Discord
|
|||||||
{
|
{
|
||||||
return $"Closing : {position.OriginDirection} {position.Open.Ticker} \n" +
|
return $"Closing : {position.OriginDirection} {position.Open.Ticker} \n" +
|
||||||
$"Open Price : {position.Open.Price} \n" +
|
$"Open Price : {position.Open.Price} \n" +
|
||||||
$"Closing Price : {position.Open.Price} \n" +
|
$"Closing Price : {position.Open.Price:F5} \n" +
|
||||||
$"Quantity :{position.Open.Quantity} \n" +
|
$"Quantity :{position.Open.Quantity} \n" +
|
||||||
$"PNL : {position.ProfitAndLoss.Realized} $";
|
$"PNL : {position.ProfitAndLoss.Realized} $";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user