diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs index c684202..baf493f 100644 --- a/src/Managing.Application/Bots/TradingBot.cs +++ b/src/Managing.Application/Bots/TradingBot.cs @@ -334,12 +334,18 @@ public class TradingBot : Bot, ITradingBot if (Config.IsForWatchingOnly || (ExecutionCount < 1 && !Config.IsForBacktest)) signal.Status = SignalStatus.Expired; - var signalText = $"{Config.ScenarioName} trigger a signal. Signal told you " + - $"to {signal.Direction} {Config.Ticker} on {Config.Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}"; + var indicatorNames = Indicators.Select(i => i.Type.ToString()).ToList(); + var signalText = $"šŸŽÆ **New Trading Signal**\n\n" + + $"šŸ“Š **Signal Details:**\n" + + $"šŸ“ˆ Action: `{signal.Direction}` {Config.Ticker}\n" + + $"ā° Timeframe: `{Config.Timeframe}`\n" + + $"šŸŽÆ Confidence: `{signal.Confidence}`\n" + + $"šŸ” Indicators: `{string.Join(", ", indicatorNames)}`\n" + + $"šŸ†” Signal ID: `{signal.Identifier}`"; // Apply Synth-based signal filtering if enabled - if (Config.UseSynthApi) + if (Config.UseSynthApi || !Config.IsForBacktest) { var currentPrice = Config.IsForBacktest ? OptimizedCandles.Last().Close @@ -352,16 +358,41 @@ public class TradingBot : Bot, ITradingBot signalValidationResult.Confidence == Confidence.Low || signalValidationResult.IsBlocked) { - signal.Status = SignalStatus.Expired; - await LogInformation( - $"🚫 **Synth Signal Filter** - Signal {signal.Identifier} blocked by Synth risk assessment. Context : {signalValidationResult.ValidationContext}"); - return; + // TODO : remove this when Synth is stable + // signal.Status = SignalStatus.Expired; + signalText += $"\n\n🚫 *Synth Signal Filter*\n" + + $"Signal `{signal.Identifier}` blocked by Synth risk assessment\n\n" + + $"šŸ“Š *Risk Analysis Details*\n" + + $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" + + $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" + + $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" + + $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" + + $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" + + $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" + + $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" + + $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" + + $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" + + $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" + + $"šŸ“‹ *Context*\n`{signalValidationResult.ValidationContext}`"; + } else { signal.SetConfidence(signalValidationResult.Confidence); - signalText += - $" and Synth risk assessment passed. Context : {signalValidationResult.ValidationContext}"; + signalText += $"\n\nāœ… *Synth Risk Assessment Passed*\n" + + $"Confidence: `{signalValidationResult.Confidence}`\n\n" + + $"šŸ“Š *Risk Analysis Details*\n" + + $"SL Probability: `{signalValidationResult.StopLossProbability:P2}`\n" + + $"TP Probability: `{signalValidationResult.TakeProfitProbability:P2}`\n" + + $"TP/SL Ratio: `{signalValidationResult.TpSlRatio:F2}x`\n" + + $"Win/Loss: `{signalValidationResult.WinLossRatio:F2}:1`\n" + + $"Expected Value: `${signalValidationResult.ExpectedMonetaryValue:F2}`\n" + + $"Expected Utility: `{signalValidationResult.ExpectedUtility:F4}`\n" + + $"Kelly Criterion: `{signalValidationResult.KellyFraction:P2}`\n" + + $"Kelly Capped: `{signalValidationResult.KellyCappedFraction:P2}`\n" + + $"Risk Assessment: `{signalValidationResult.GetUtilityRiskAssessment()}`\n" + + $"Time Horizon: `{signalValidationResult.TimeHorizonSeconds / 3600:F1}h`\n\n" + + $"šŸ“‹ *Context*\n`{signalValidationResult.ValidationContext}`"; } } @@ -1316,19 +1347,22 @@ public class TradingBot : Bot, ITradingBot // Calculate closing fee based on the actual executed trade's price and quantity if (position.StopLoss?.Status == TradeStatus.Filled) { - var stopLossPositionSizeUsd = (position.StopLoss.Price * position.StopLoss.Quantity) * position.StopLoss.Leverage; + var stopLossPositionSizeUsd = + (position.StopLoss.Price * position.StopLoss.Quantity) * position.StopLoss.Leverage; var uiFeeClose = stopLossPositionSizeUsd * uiFeeRate; // Fee paid on closing via StopLoss fees += uiFeeClose; } else if (position.TakeProfit1?.Status == TradeStatus.Filled) { - var takeProfit1PositionSizeUsd = (position.TakeProfit1.Price * position.TakeProfit1.Quantity) * position.TakeProfit1.Leverage; + var takeProfit1PositionSizeUsd = (position.TakeProfit1.Price * position.TakeProfit1.Quantity) * + position.TakeProfit1.Leverage; var uiFeeClose = takeProfit1PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit1 fees += uiFeeClose; } else if (position.TakeProfit2?.Status == TradeStatus.Filled) { - var takeProfit2PositionSizeUsd = (position.TakeProfit2.Price * position.TakeProfit2.Quantity) * position.TakeProfit2.Leverage; + var takeProfit2PositionSizeUsd = (position.TakeProfit2.Price * position.TakeProfit2.Quantity) * + position.TakeProfit2.Leverage; var uiFeeClose = takeProfit2PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit2 fees += uiFeeClose; } diff --git a/src/Managing.Domain/Scenarios/ScenarioHelpers.cs b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs index 5796079..ba73692 100644 --- a/src/Managing.Domain/Scenarios/ScenarioHelpers.cs +++ b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs @@ -183,7 +183,7 @@ public static class ScenarioHelpers IndicatorType.EmaCross => SignalType.Signal, IndicatorType.DualEmaCross => SignalType.Signal, IndicatorType.ThreeWhiteSoldiers => SignalType.Signal, - IndicatorType.SuperTrend => SignalType.Signal, + IndicatorType.SuperTrend => SignalType.Trend, IndicatorType.ChandelierExit => SignalType.Signal, IndicatorType.EmaTrend => SignalType.Trend, IndicatorType.Composite => SignalType.Signal, diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs index 85a61d7..f3c6c98 100644 --- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -58,7 +58,7 @@ public static class TradingBox public static Signal GetSignal(HashSet newCandles, HashSet strategies, HashSet previousSignal, IndicatorComboConfig config, int? loopbackPeriod = 1) { - var signalOnCandles = new HashSet(); + var signalOnCandles = new List(); var limitedCandles = newCandles.ToList().TakeLast(600).ToList(); foreach (var strategy in strategies) @@ -141,6 +141,8 @@ public static class TradingBox return signalOnCandles.Single(); } + signalOnCandles = signalOnCandles.OrderBy(s => s.Date).ToHashSet(); + // Check if all strategies produced signals - this is required for composite signals var strategyNames = strategies.Select(s => s.Name).ToHashSet(); var signalIndicatorNames = signalOnCandles.Select(s => s.IndicatorName).ToHashSet(); diff --git a/src/Managing.Domain/Strategies/Signals/MacdCrossIndicator.cs b/src/Managing.Domain/Strategies/Signals/MacdCrossIndicator.cs index f0c666f..abbe326 100644 --- a/src/Managing.Domain/Strategies/Signals/MacdCrossIndicator.cs +++ b/src/Managing.Domain/Strategies/Signals/MacdCrossIndicator.cs @@ -38,14 +38,20 @@ public class MacdCrossIndicator : Indicator var previousCandle = macdCandle[0]; foreach (var currentCandle in macdCandle.Skip(1)) { - if (previousCandle.Histogram > 0 && currentCandle.Histogram < 0 && currentCandle.Macd < 0) + // Only trigger signals when Signal line is outside -100 to 100 range (extreme conditions) + if (currentCandle.Signal < -200 || currentCandle.Signal > 200) { - AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium); - } + // Check for MACD line crossing below Signal line (bearish cross) + if (previousCandle.Macd > previousCandle.Signal && currentCandle.Macd < currentCandle.Signal) + { + AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium); + } - if (previousCandle.Histogram < 0 && currentCandle.Histogram > 0 && currentCandle.Macd > 0) - { - AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium); + // Check for MACD line crossing above Signal line (bullish cross) + if (previousCandle.Macd < previousCandle.Signal && currentCandle.Macd > currentCandle.Signal) + { + AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium); + } } previousCandle = currentCandle; @@ -84,7 +90,8 @@ public class MacdCrossIndicator : Indicator Ticker = candle.Ticker, Exchange = candle.Exchange, Macd = currentMacd.Macd.Value, - Histogram = currentMacd.Histogram.Value + Histogram = currentMacd.Histogram.Value, + Signal = currentMacd.Signal.Value }); } } diff --git a/src/Managing.Domain/Strategies/Signals/SuperTrendIndicator.cs b/src/Managing.Domain/Strategies/Signals/SuperTrendIndicator.cs index 4654657..f3fa69d 100644 --- a/src/Managing.Domain/Strategies/Signals/SuperTrendIndicator.cs +++ b/src/Managing.Domain/Strategies/Signals/SuperTrendIndicator.cs @@ -38,17 +38,26 @@ public class SuperTrendIndicator : Indicator var previousCandle = superTrendCandle[0]; foreach (var currentCandle in superTrendCandle.Skip(1)) { - // Short - if (currentCandle.Close < previousCandle.SuperTrend && previousCandle.Close > previousCandle.SuperTrend) - { - AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium); - } + // // Short + // if (currentCandle.Close < previousCandle.SuperTrend && previousCandle.Close > previousCandle.SuperTrend) + // { + // AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium); + // } + // + // // Long + // if (currentCandle.Close > previousCandle.SuperTrend && previousCandle.Close < previousCandle.SuperTrend) + // { + // AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium); + // } - // Long - if (currentCandle.Close > previousCandle.SuperTrend && previousCandle.Close < previousCandle.SuperTrend) + if (currentCandle.SuperTrend < currentCandle.Close) { AddSignal(currentCandle, TradeDirection.Long, Confidence.Medium); } + else if (currentCandle.SuperTrend > currentCandle.Close) + { + AddSignal(currentCandle, TradeDirection.Short, Confidence.Medium); + } previousCandle = currentCandle; }