249 lines
11 KiB
C#
249 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using DesktopBot.Models;
|
|
|
|
namespace DesktopBot.Engine
|
|
{
|
|
/// <summary>
|
|
/// Motore di raccomandazione strategia.
|
|
/// Per ogni combinazione asset-class / volatilità / tipologia di mercato restituisce
|
|
/// la strategia ottimale e i parametri pre-calibrati.
|
|
///
|
|
/// Logica ispirata agli approcci professionali:
|
|
/// Crypto → alta volatilità 24/7 → VOLATILITY_BREAKOUT
|
|
/// Equity → orario di mercato → EMA_CROSSOVER o MACD
|
|
/// ETF → bassa volatilità → KALMAN_MEAN_REVERSION
|
|
/// FX/FX-alike → mean-reverting → RSI
|
|
/// </summary>
|
|
public static class StrategyAdvisor
|
|
{
|
|
// ── Profili per asset class ─────────────────────────────────────────
|
|
|
|
private static readonly Dictionary<string, StrategyProfile[]> _profiles
|
|
= new Dictionary<string, StrategyProfile[]>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
// ── Crypto ──────────────────────────────────────────────────────
|
|
["crypto"] = new[]
|
|
{
|
|
new StrategyProfile(
|
|
TradingStrategy.VOLATILITY_BREAKOUT,
|
|
"Volatility Breakout (1min)",
|
|
"Strategia ad alta frequenza per crypto: breakout su barre da 1 minuto con filtro RVOL e CVD.",
|
|
"🚀", "#FF6D00",
|
|
isRecommended: true,
|
|
cfg =>
|
|
{
|
|
cfg.KeltnerPeriod = 20;
|
|
cfg.KeltnerMultiplier = 2.0m;
|
|
cfg.RvolMinThreshold = 2.5m; // Soglia RVOL più alta per crypto ad alta volatilità
|
|
cfg.AtrStopMultiplier = 1.0m;
|
|
cfg.CheckIntervalSeconds = 60; // Tick ogni 60 secondi
|
|
cfg.AnalysisTimeFrame = Alpaca.Markets.BarTimeFrame.Minute; // Barre da 1 minuto
|
|
cfg.HistoricalBarCount = 200; // 200 barre = 3.3 ore di storico
|
|
cfg.StopLossPercentage = 0.03m; // 3% SL per volatilità crypto
|
|
cfg.TakeProfitPercentage = 0.06m; // 6% TP, ratio 1:2
|
|
}),
|
|
|
|
new StrategyProfile(
|
|
TradingStrategy.KALMAN_MEAN_REVERSION,
|
|
"Kalman Mean Reversion (1min)",
|
|
"Mean-reversion adattiva su crypto range-bound con analisi a 1 minuto.",
|
|
"🔬", "#40C4FF",
|
|
isRecommended: false,
|
|
cfg =>
|
|
{
|
|
cfg.KalmanDelta = 1e-5;
|
|
cfg.KalmanObservationVariance = 1.0;
|
|
cfg.KalmanEntryZScore = 2.0;
|
|
cfg.KalmanExitZScore = 0.25;
|
|
cfg.CheckIntervalSeconds = 120;
|
|
cfg.AnalysisTimeFrame = Alpaca.Markets.BarTimeFrame.Minute;
|
|
cfg.HistoricalBarCount = 300; // 5 ore di storico
|
|
cfg.StopLossPercentage = 0.03m;
|
|
cfg.TakeProfitPercentage = 0.06m;
|
|
}),
|
|
|
|
new StrategyProfile(
|
|
TradingStrategy.EMA_CROSSOVER,
|
|
"EMA Crossover (1min)",
|
|
"Trend-following veloce su barre da 1 minuto — ottimale per BTC/USD in trend forte.",
|
|
"⚡", "#00E676",
|
|
isRecommended: false,
|
|
cfg =>
|
|
{
|
|
cfg.FastEmaPeriod = 5; // EMA veloce 5 periodi (5 minuti)
|
|
cfg.SlowEmaPeriod = 15; // EMA lenta 15 periodi (15 minuti)
|
|
cfg.CheckIntervalSeconds = 60; // Tick ogni 60 secondi
|
|
cfg.AnalysisTimeFrame = Alpaca.Markets.BarTimeFrame.Minute;
|
|
cfg.HistoricalBarCount = 150; // 2.5 ore di storico
|
|
cfg.StopLossPercentage = 0.025m; // 2.5% SL
|
|
cfg.TakeProfitPercentage = 0.05m; // 5% TP
|
|
}),
|
|
},
|
|
|
|
// ── US Equity ────────────────────────────────────────────────────
|
|
["us_equity"] = new[]
|
|
{
|
|
new StrategyProfile(
|
|
TradingStrategy.EMA_CROSSOVER,
|
|
"EMA Crossover",
|
|
"Strategia trend-following classica, ottimale su azioni con trend chiari.",
|
|
"📈", "#00E676",
|
|
isRecommended: true,
|
|
cfg =>
|
|
{
|
|
cfg.FastEmaPeriod = 9;
|
|
cfg.SlowEmaPeriod = 21;
|
|
cfg.CheckIntervalSeconds = 60;
|
|
cfg.StopLossPercentage = 0.02m;
|
|
cfg.TakeProfitPercentage = 0.04m;
|
|
}),
|
|
|
|
new StrategyProfile(
|
|
TradingStrategy.MACD,
|
|
"MACD",
|
|
"Momentum con istogramma MACD — eccellente su mid/large cap con trend.",
|
|
"⚡", "#EA80FC",
|
|
isRecommended: false,
|
|
cfg =>
|
|
{
|
|
cfg.MacdFastPeriod = 12;
|
|
cfg.MacdSlowPeriod = 26;
|
|
cfg.MacdSignalPeriod = 9;
|
|
cfg.CheckIntervalSeconds = 60;
|
|
cfg.StopLossPercentage = 0.02m;
|
|
cfg.TakeProfitPercentage = 0.04m;
|
|
}),
|
|
|
|
new StrategyProfile(
|
|
TradingStrategy.RSI,
|
|
"RSI Reversal",
|
|
"Ottimale per azioni in range o in correzione — compra ipervenduto.",
|
|
"📊", "#FFFF00",
|
|
isRecommended: false,
|
|
cfg =>
|
|
{
|
|
cfg.RsiPeriod = 14;
|
|
cfg.RsiOversoldThreshold = 30m;
|
|
cfg.RsiOverboughtThreshold = 70m;
|
|
cfg.CheckIntervalSeconds = 120;
|
|
cfg.StopLossPercentage = 0.02m;
|
|
cfg.TakeProfitPercentage = 0.035m;
|
|
}),
|
|
},
|
|
|
|
// ── ETF (mappato come us_equity con parametri diversi) ───────────
|
|
["etf"] = new[]
|
|
{
|
|
new StrategyProfile(
|
|
TradingStrategy.KALMAN_MEAN_REVERSION,
|
|
"Kalman Mean Reversion",
|
|
"ETF tendono a mean-revert: il filtro Kalman ne stima il fair value.",
|
|
"🔬", "#40C4FF",
|
|
isRecommended: true,
|
|
cfg =>
|
|
{
|
|
cfg.KalmanDelta = 1e-5;
|
|
cfg.KalmanObservationVariance = 0.8;
|
|
cfg.KalmanEntryZScore = 1.8;
|
|
cfg.KalmanExitZScore = 0.2;
|
|
cfg.CheckIntervalSeconds = 300;
|
|
cfg.StopLossPercentage = 0.015m;
|
|
cfg.TakeProfitPercentage = 0.03m;
|
|
}),
|
|
|
|
new StrategyProfile(
|
|
TradingStrategy.EMA_CROSSOVER,
|
|
"EMA Crossover",
|
|
"Trend-following conservativo su ETF ad alta liquidità.",
|
|
"📈", "#00E676",
|
|
isRecommended: false,
|
|
cfg =>
|
|
{
|
|
cfg.FastEmaPeriod = 12;
|
|
cfg.SlowEmaPeriod = 26;
|
|
cfg.CheckIntervalSeconds = 120;
|
|
cfg.StopLossPercentage = 0.015m;
|
|
cfg.TakeProfitPercentage = 0.03m;
|
|
}),
|
|
},
|
|
};
|
|
|
|
// ── Fallback: usa equity ─────────────────────────────────────────────
|
|
private static StrategyProfile[] GetProfiles(string assetClass)
|
|
{
|
|
if (string.IsNullOrEmpty(assetClass)) return _profiles["us_equity"];
|
|
var key = NormalizeClass(assetClass);
|
|
return _profiles.TryGetValue(key, out var p) ? p : _profiles["us_equity"];
|
|
}
|
|
|
|
/// <summary>Restituisce tutti i profili strategia disponibili per la classe dell'asset.</summary>
|
|
public static StrategyProfile[] GetAvailableProfiles(string assetClass)
|
|
=> GetProfiles(assetClass);
|
|
|
|
/// <summary>Restituisce il profilo raccomandato (primo marcato IsRecommended).</summary>
|
|
public static StrategyProfile GetRecommended(string assetClass)
|
|
{
|
|
var profiles = GetProfiles(assetClass);
|
|
foreach (var p in profiles)
|
|
if (p.IsRecommended) return p;
|
|
return profiles[0];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applica i parametri ottimali del profilo raccomandato alla configurazione.
|
|
/// Mantiene Symbol, Quantity e Name invariati.
|
|
/// </summary>
|
|
public static void ApplyOptimalConfig(BotConfiguration cfg, string assetClass)
|
|
{
|
|
var profile = GetRecommended(assetClass);
|
|
cfg.Strategy = profile.Strategy;
|
|
profile.ApplyParameters(cfg);
|
|
}
|
|
|
|
private static string NormalizeClass(string assetClass)
|
|
{
|
|
// Alpaca restituisce "us_equity", "crypto", ecc.
|
|
var lower = assetClass.ToLowerInvariant().Replace("-", "_");
|
|
if (lower.Contains("crypto")) return "crypto";
|
|
if (lower.Contains("etf")) return "etf";
|
|
return lower;
|
|
}
|
|
}
|
|
|
|
// ── StrategyProfile ──────────────────────────────────────────────────────
|
|
|
|
/// <summary>Descrive una strategia disponibile per una classe di asset, con i parametri pre-calibrati.</summary>
|
|
public sealed class StrategyProfile
|
|
{
|
|
public TradingStrategy Strategy { get; }
|
|
public string DisplayName { get; }
|
|
public string Description { get; }
|
|
public string Icon { get; }
|
|
public string AccentColor { get; }
|
|
public bool IsRecommended { get; }
|
|
|
|
private readonly Action<BotConfiguration> _applyParameters;
|
|
|
|
public StrategyProfile(
|
|
TradingStrategy strategy,
|
|
string displayName,
|
|
string description,
|
|
string icon,
|
|
string accentColor,
|
|
bool isRecommended,
|
|
Action<BotConfiguration> applyParameters)
|
|
{
|
|
Strategy = strategy;
|
|
DisplayName = displayName;
|
|
Description = description;
|
|
Icon = icon;
|
|
AccentColor = accentColor;
|
|
IsRecommended = isRecommended;
|
|
_applyParameters = applyParameters;
|
|
}
|
|
|
|
public void ApplyParameters(BotConfiguration cfg) => _applyParameters(cfg);
|
|
}
|
|
}
|