Sviluppo TradingBot
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user