Add synthApi (#27)
* Add synthApi * Put confidence for Synth proba * Update the code * Update readme * Fix bootstraping * fix github build * Update the endpoints for scenario * Add scenario and update backtest modal * Update bot modal * Update interfaces for synth * add synth to backtest * Add Kelly criterion and better signal * Update signal confidence * update doc * save leaderboard and prediction * Update nswag to generate ApiClient in the correct path * Unify the trading modal * Save miner and prediction * Update messaging and block new signal until position not close when flipping off * Rename strategies to indicators * Update doc * Update chart + add signal name * Fix signal direction * Update docker webui * remove crypto npm * Clean
This commit is contained in:
@@ -108,15 +108,21 @@ public static class TradingBox
|
||||
}
|
||||
}
|
||||
|
||||
// Keep only the latest signal per indicator to avoid count mismatch
|
||||
var latestSignalsPerIndicator = signalOnCandles
|
||||
.GroupBy(s => s.IndicatorName)
|
||||
.Select(g => g.OrderByDescending(s => s.Date).First())
|
||||
.ToHashSet();
|
||||
|
||||
// Remove the restrictive requirement that ALL strategies must produce signals
|
||||
// Instead, let ComputeSignals handle the logic based on what we have
|
||||
if (!signalOnCandles.Any())
|
||||
if (!latestSignalsPerIndicator.Any())
|
||||
{
|
||||
return null; // No signals from any strategy
|
||||
}
|
||||
|
||||
var data = newCandles.First();
|
||||
return ComputeSignals(strategies, signalOnCandles, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
|
||||
return ComputeSignals(strategies, latestSignalsPerIndicator, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
|
||||
data.Timeframe, config);
|
||||
}
|
||||
|
||||
@@ -136,51 +142,88 @@ public static class TradingBox
|
||||
}
|
||||
|
||||
// Check if all strategies produced signals - this is required for composite signals
|
||||
if (signalOnCandles.Count != strategies.Count)
|
||||
var strategyNames = strategies.Select(s => s.Name).ToHashSet();
|
||||
var signalIndicatorNames = signalOnCandles.Select(s => s.IndicatorName).ToHashSet();
|
||||
|
||||
if (!strategyNames.SetEquals(signalIndicatorNames))
|
||||
{
|
||||
// Not all strategies produced signals - composite signal requires all strategies to contribute
|
||||
return null;
|
||||
}
|
||||
|
||||
// Group signals by type for analysis
|
||||
var signalStrategies = signalOnCandles.Where(s => s.SignalType == SignalType.Signal).ToList();
|
||||
var trendStrategies = signalOnCandles.Where(s => s.SignalType == SignalType.Trend).ToList();
|
||||
var contextStrategies = signalOnCandles.Where(s => s.SignalType == SignalType.Context).ToList();
|
||||
var signals = signalOnCandles.Where(s => s.SignalType == SignalType.Signal).ToList();
|
||||
var trendSignals = signalOnCandles.Where(s => s.SignalType == SignalType.Trend).ToList();
|
||||
var contextSignals = signalOnCandles.Where(s => s.SignalType == SignalType.Context).ToList();
|
||||
|
||||
// Context validation - evaluates market conditions based on confidence levels
|
||||
if (!ValidateContextStrategies(strategies, contextStrategies, config))
|
||||
if (!ValidateContextStrategies(strategies, contextSignals, config))
|
||||
{
|
||||
return null; // Context strategies are blocking the trade
|
||||
}
|
||||
|
||||
// Trend analysis - evaluate overall market direction
|
||||
var trendDirection = EvaluateTrendDirection(trendStrategies, config);
|
||||
// Check for 100% agreement across ALL signals (no threshold voting)
|
||||
var allDirectionalSignals = signalOnCandles
|
||||
.Where(s => s.Direction != TradeDirection.None && s.SignalType != SignalType.Context).ToList();
|
||||
|
||||
// Signal analysis - evaluate entry signals
|
||||
var signalDirection = EvaluateSignalDirection(signalStrategies, config);
|
||||
if (!allDirectionalSignals.Any())
|
||||
{
|
||||
return null; // No directional signals available
|
||||
}
|
||||
|
||||
// Determine final direction and confidence
|
||||
var (finalDirection, confidence) =
|
||||
DetermineFinalSignal(signalDirection, trendDirection, signalStrategies, trendStrategies, config);
|
||||
// Require 100% agreement - all signals must have the same direction
|
||||
var lastSignalDirection = allDirectionalSignals.Last().Direction;
|
||||
if (!allDirectionalSignals.All(s => s.Direction == lastSignalDirection))
|
||||
{
|
||||
return null; // Signals are not in complete agreement
|
||||
}
|
||||
|
||||
if (finalDirection == TradeDirection.None || confidence < config.MinimumConfidence)
|
||||
var finalDirection = lastSignalDirection;
|
||||
|
||||
// Calculate confidence based on the average confidence of all signals
|
||||
var averageConfidence = CalculateAverageConfidence(allDirectionalSignals);
|
||||
|
||||
if (finalDirection == TradeDirection.None || averageConfidence < config.MinimumConfidence)
|
||||
{
|
||||
return null; // No valid signal or below minimum confidence
|
||||
}
|
||||
|
||||
// Create composite signal
|
||||
var lastSignal = signalStrategies.LastOrDefault() ??
|
||||
trendStrategies.LastOrDefault() ?? contextStrategies.LastOrDefault();
|
||||
var lastSignal = signals.LastOrDefault() ??
|
||||
trendSignals.LastOrDefault() ?? contextSignals.LastOrDefault();
|
||||
|
||||
return new Signal(
|
||||
ticker,
|
||||
finalDirection,
|
||||
confidence,
|
||||
averageConfidence,
|
||||
lastSignal?.Candle,
|
||||
lastSignal?.Date ?? DateTime.UtcNow,
|
||||
lastSignal?.Exchange ?? config.DefaultExchange,
|
||||
IndicatorType.Composite,
|
||||
SignalType.Signal);
|
||||
SignalType.Signal, "Aggregated");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the average confidence level from a list of signals
|
||||
/// </summary>
|
||||
private static Confidence CalculateAverageConfidence(List<Signal> signals)
|
||||
{
|
||||
if (!signals.Any())
|
||||
{
|
||||
return Confidence.None;
|
||||
}
|
||||
|
||||
// Convert confidence enum to numeric values for averaging
|
||||
var confidenceValues = signals.Select(s => (int)s.Confidence).ToList();
|
||||
var averageValue = confidenceValues.Average();
|
||||
|
||||
// Round to nearest confidence level
|
||||
var roundedValue = Math.Round(averageValue);
|
||||
|
||||
// Ensure the value is within valid confidence enum range
|
||||
roundedValue = Math.Max(0, Math.Min(3, roundedValue));
|
||||
|
||||
return (Confidence)(int)roundedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user