Files
Finlytic/FinlyticApp/lib/features/simulation/utils/strategy_explanations.dart
T

260 lines
16 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../../../core/theme/app_theme.dart';
/// Plain-language explanations of all 10 backtestable strategies (`FinlyticTechnicals/Strategies/CoreStrategies.cs`,
/// shared 1:1 by FinlyticSimulation - "100% code reuse", see that file's own doc comments). Mirrors
/// `FinlyticApp/lib/features/asset_detail/utils/pattern_explanations.dart`'s dictionary shape/detail-sheet
/// pattern rather than inventing a new one. Every description below is a plain-language restatement of that
/// strategy's actual `Evaluate()` logic as written, not a generic template (Rules.md §4). Every strategy fires
/// both a long AND a short setup (mirror-image conditions in the same `Evaluate()` method) - hence
/// `'bias': 'BIDIREKTIONAL'` throughout, rather than a single-direction label.
class StrategyExplanations {
static const Map<String, Map<String, String>> dictionary = {
'TrendPullbackFvg': {
'title': 'Trend Pullback FVG Retracement',
'bias': 'BIDIREKTIONAL',
'description':
'Ein Trend ist bereits im Gange (EMA20/EMA50/EMA200 in Trendreihenfolge) und der Kurs zieht sich kurz '
'in eine Fair-Value-Gap (eine übersprungene Preiszone) zurück. Sobald der Kurs aus dieser Zone '
'wieder in Trendrichtung zu laufen beginnt, gilt das als Bestätigung, dass der Trend weiterläuft. '
'Long bei Aufwärtstrend + bullischer FVG, Short bei Abwärtstrend + bärischer FVG (Spiegelbild).',
'significance':
'Genau das "kurzer Rücksetzer, dann Bestätigung durchs erneute Laufen"-Muster: EMA50 bestätigt den '
'übergeordneten Trend, die Fair-Value-Gap markiert die Rücksetzer-Zone, in der eingestiegen wird.',
'action': 'Einstieg beim erneuten Lauf aus der Fair-Value-Gap-Zone in Trendrichtung, Stop-Loss hinter der Gap bzw. 1,2×ATR vom Entry.',
'reliability': 'Hoch (Quality-Score 88, Top-Pick, Rating A+)',
'target': 'Gestaffelter Ausstieg: TP1 bei 1,5× Risiko (50% Teilverkauf, Stop wandert auf Break-Even), TP2 bei 3,0× Risiko (30%).',
'stop_loss': 'Hinter der Fair-Value-Gap-Grenze bzw. 1,2× ATR vom Einstieg - je nachdem, was enger ist.',
},
'VolatilitySqueeze': {
'title': 'Bollinger/Keltner Squeeze Breakout',
'bias': 'BIDIREKTIONAL',
'description':
'Bollinger-Bänder ziehen sich innerhalb der Keltner-Kanäle zusammen (niedrige Volatilität) und "feuern" '
'dann mit Momentum in eine Richtung - der klassische Ausbruch aus einer ruhigen Konsolidierungsphase. '
'Long bei positivem, Short bei negativem Momentum-Histogramm.',
'significance': 'Eine Phase geringer Schwankung geht typischerweise einer impulsiven Bewegung voraus; das Momentum-Histogramm bestätigt die Richtung.',
'action': 'Einstieg direkt beim Squeeze-Ausbruch in Richtung des Momentums.',
'reliability': 'Hoch (Quality-Score 84, Top-Pick, Rating A)',
'target': 'Festes Ziel bei ±2,0× ATR ab Einstieg (100% Ausstieg).',
'stop_loss': '1,0× ATR gegen die Einstiegsrichtung.',
},
'SmcLiquiditySweep': {
'title': 'Smart Money Liquidity Sweep & CHoCH',
'bias': 'BIDIREKTIONAL',
'description':
'Der Kurs "fegt" kurz über ein bekanntes Hoch oder unter ein bekanntes Tief (Stop-Loss-Jagd durch '
'große Marktteilnehmer) und dreht danach abrupt in die Gegenrichtung - erkennbar an einem '
'Liquidity-Sweep-Muster und/oder einem Wechsel der Marktstruktur (Change of Character). Long nach '
'einem Sweep unter einem Tief, Short nach einem Sweep über einem Hoch.',
'significance': 'Interpretiert als Zeichen, dass institutionelle Marktteilnehmer die durch den Sweep freigesetzte Liquidität aufgenommen haben.',
'action': 'Einstieg nach Bestätigung der Ablehnung des Sweeps.',
'reliability': 'Sehr hoch (Quality-Score 91, Top-Pick, Rating A+)',
'target': 'Gestaffelter Ausstieg: TP1 bei 2,0× Risiko (60%, danach Break-Even), TP2 bei 4,0× Risiko (40%).',
'stop_loss': 'Knapp hinter dem Extrempunkt des Sweeps (0,2% Puffer).',
},
'MeanReversion': {
'title': 'Bollinger 2,5-Sigma Mean Reversion',
'bias': 'BIDIREKTIONAL',
'description':
'Nur in ruhigen/schwankenden Seitwärtsmärkten (nicht im Trend) aktiv: Der Kurs berührt das untere '
'(Long) oder obere (Short) 2,5-Sigma-Bollingerband bei niedrigem ADX (<22, kein starker Trend) und '
'überverkauftem (≤32) bzw. überkauftem (≥68) RSI - eine statistisch übertriebene Bewegung, die '
'zum Durchschnitt zurückkehren sollte.',
'significance': 'Setzt auf die Rückkehr zum VWAP/Mittelwert nach einer überzogenen kurzfristigen Bewegung, nicht auf eine Trendfortsetzung.',
'action': 'Einstieg bei Berührung des äußeren Bandes mit passendem RSI-Extrem.',
'reliability': 'Mittel (Quality-Score 79, kein Top-Pick, Rating B) - bewusst konservativer eingestuft als die trendfolgenden Strategien.',
'target': 'Ziel ist der VWAP bzw. das mittlere Bollingerband (SMA20), 100% Ausstieg.',
'stop_loss': '0,8× ATR hinter dem Extrem der Signalkerze.',
},
'SuperTrendMultiTf': {
'title': 'SuperTrend Multi-Timeframe Alignment',
'bias': 'BIDIREKTIONAL',
'description':
'Zwei Zeitebenen müssen übereinstimmen: Der SuperTrend-Indikator auf 1h UND auf 15m stehen beide auf '
'dieselbe Richtung (bullisch für Long, bärisch für Short). Erst wenn beide Zeitrahmen im Einklang '
'sind, gilt das Setup als bestätigt.',
'significance': 'Reduziert Fehlsignale einzelner Zeitebenen - der übergeordnete (1h) und der feinere (15m) Trend müssen sich decken.',
'action': 'Einstieg bei Bestätigung der Multi-Timeframe-Übereinstimmung.',
'reliability': 'Hoch (Quality-Score 86, Top-Pick, Rating A)',
'target': 'Kein festes Kursziel - reine Trailing-Stop-Strategie entlang der 15m-SuperTrend-Linie, Ausstieg erst bei Trendwechsel.',
'stop_loss': 'Dynamisch an der 15m-SuperTrend-Linie, sofortiger Exit bei Flip der Gegenrichtung.',
},
'MacdCrossover': {
'title': 'MACD Signal Line Crossover',
'bias': 'BIDIREKTIONAL',
'description':
'Klassischer Momentum-Wechsel: Die MACD-Linie kreuzt die Signal-Linie (verglichen mit derselben '
'Berechnung eine Kerze zuvor) und das Histogramm bestätigt die Richtung. Long bei Kreuzung nach '
'oben mit positivem Histogramm, Short bei Kreuzung nach unten mit negativem Histogramm.',
'significance': 'Der MACD-Crossover ist eines der bekanntesten Momentum-Signale der technischen Analyse und markiert einen Wechsel im kurzfristigen Trend.',
'action': 'Einstieg direkt bei bestätigter Kreuzung.',
'reliability': 'Mittel (Quality-Score 80, Rating B)',
'target': 'Festes Ziel bei ±2,0× Risiko (100% Ausstieg).',
'stop_loss': '1,5× ATR gegen die Einstiegsrichtung.',
},
'MovingAverageCrossover': {
'title': 'EMA50/EMA200 Golden & Death Cross',
'bias': 'BIDIREKTIONAL',
'description':
'Der langfristige Trendwechsel-Klassiker: Kreuzt EMA50 von unten nach oben durch EMA200, ist das ein '
'"Golden Cross" (Long). Kreuzt sie von oben nach unten, ist das ein "Death Cross" (Short).',
'significance': 'Einer der ältesten und am weitesten verbreiteten langfristigen Trendindikatoren - signalisiert einen strukturellen Wechsel der Marktrichtung.',
'action': 'Einstieg direkt bei bestätigter Kreuzung.',
'reliability': 'Hoch (Quality-Score 82, Top-Pick, Rating A) - aber selten, da EMA50/EMA200 sich nicht oft kreuzen.',
'target': 'Kein festes Kursziel - Trailing-Stop mit 2,5× ATR-Abstand, für langfristige Trends gedacht.',
'stop_loss': '2,0× ATR gegen die Einstiegsrichtung.',
},
'RsiReversal': {
'title': 'RSI Overbought/Oversold Threshold Cross',
'bias': 'BIDIREKTIONAL',
'description':
'Einfaches, richtungsoffenes Momentum-Reversal: Long, wenn der RSI von unterhalb 30 wieder darüber '
'steigt (Ende der Überverkauftheit). Short, wenn der RSI von oberhalb 70 wieder darunter fällt '
'(Ende der Überkauftheit). Anders als "Bollinger 2,5-Sigma Mean Reversion" wird hier NICHT '
'zusätzlich ein niedriger ADX oder eine Bollinger-Band-Berührung verlangt - rein der RSI-Schwellenwert zählt.',
'significance': 'Der RSI-Schwellenwert-Durchbruch ist eines der einfachsten und am längsten genutzten Reversal-Signale.',
'action': 'Einstieg direkt beim erneuten Über- bzw. Unterschreiten der 30/70-Schwelle.',
'reliability': 'Mittel (Quality-Score 75, Rating B) - bewusst einfacher/häufiger auslösend als die kombinierten Strategien.',
'target': 'Festes Ziel bei ±1,5× Risiko (100% Ausstieg).',
'stop_loss': '1,2× ATR gegen die Einstiegsrichtung.',
},
'DonchianBreakout': {
'title': '20-Perioden Donchian-Kanal-Ausbruch',
'bias': 'BIDIREKTIONAL',
'description':
'Der klassische "Turtle Trading"-Ausbruch: Long, wenn der Schlusskurs über das Hoch der letzten 20 '
'Kerzen (vor der aktuellen) ausbricht. Short, wenn er unter das Tief der letzten 20 Kerzen fällt.',
'significance': 'Einer der ältesten systematischen Breakout-Ansätze - setzt darauf, dass ein Ausbruch aus einer etablierten Handelsspanne eine neue Bewegung einleitet.',
'action': 'Einstieg direkt beim Ausbruch über/unter den 20-Perioden-Kanal.',
'reliability': 'Hoch (Quality-Score 83, Top-Pick, Rating A)',
'target': 'Kein festes Kursziel - Trailing-Stop mit 2,0× ATR-Abstand.',
'stop_loss': 'Am gegenüberliegenden Kanalrand (Tief bei Long, Hoch bei Short).',
},
'VwapBounce': {
'title': 'VWAP Pullback & Bounce Confirmation',
'bias': 'BIDIREKTIONAL',
'description':
'Im Aufwärtstrend (EMA20>EMA50): Der Kurs sinkt kurz zum/unter den VWAP und schließt wieder darüber '
'(Bounce) - Long-Einstieg. Im Abwärtstrend (EMA20<EMA50): Der Kurs steigt kurz zum/über den VWAP '
'und schließt wieder darunter (Ablehnung) - Short-Einstieg.',
'significance': 'Der VWAP wird von vielen institutionellen Marktteilnehmern als fairer Durchschnittspreis behandelt - ein Rücksetzer dorthin mit anschließender Ablehnung gilt als starkes Fortsetzungssignal.',
'action': 'Einstieg bei Bestätigung des Bounces/der Ablehnung am VWAP.',
'reliability': 'Mittel (Quality-Score 81, Rating B)',
'target': 'Festes Ziel bei ±2,0× Risiko (100% Ausstieg).',
'stop_loss': 'Hinter dem Hoch/Tief der Rücksetzer-Kerze bzw. 1,0× ATR - je nachdem, was weiter entfernt ist.',
},
};
static Map<String, String> _infoFor(String strategyKey) {
return dictionary[strategyKey] ??
{
'title': strategyKey,
'bias': 'NEUTRAL',
'description': 'Eine von FinlyticTechnicals bereitgestellte technische Strategie ($strategyKey).',
'significance': 'Details zu dieser Strategie sind noch nicht dokumentiert.',
'action': 'Backtest-Ergebnisse und Score-Aufschlüsselung beachten.',
'reliability': 'Unbekannt',
'target': 'Abhängig vom jeweiligen Setup.',
'stop_loss': 'Siehe individuelles Setup.',
};
}
static void showStrategyDetails(BuildContext context, String strategyKey) {
final info = _infoFor(strategyKey);
final isBullish = info['bias'] == 'BULLISH';
final isBearish = info['bias'] == 'BEARISH';
final biasColor = isBullish ? AppTheme.primaryEmerald : (isBearish ? AppTheme.accentRed : AppTheme.accentCyan);
showDialog(
context: context,
builder: (modalContext) => AlertDialog(
backgroundColor: AppTheme.cardSurface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: AppTheme.glassBorder),
),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
info['title']!,
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: biasColor.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: biasColor),
),
child: Text(
info['bias']!,
style: TextStyle(color: biasColor, fontWeight: FontWeight.bold, fontSize: 11),
),
),
],
),
],
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text('Funktionsweise:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 4),
Text(info['description']!, style: TextStyle(color: AppTheme.textSecondary, fontSize: 13, height: 1.4)),
const SizedBox(height: 14),
const Text('Warum das ein Signal ist:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 4),
Text(info['significance']!, style: const TextStyle(color: Colors.white70, fontSize: 13, height: 1.4)),
const SizedBox(height: 14),
const Text('Einstufung:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 2),
Text(info['reliability']!, style: TextStyle(color: AppTheme.textSecondary, fontSize: 13)),
const SizedBox(height: 14),
const Text('Kursziel:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 4),
Text(info['target']!, style: TextStyle(color: AppTheme.primaryEmerald, fontSize: 13, height: 1.4)),
const SizedBox(height: 14),
const Text('Stop-Loss:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 4),
Text(info['stop_loss']!, style: TextStyle(color: AppTheme.accentRed, fontSize: 13, height: 1.4)),
const SizedBox(height: 18),
const Text('Einstiegsauslöser:', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 13)),
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppTheme.glassSurface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppTheme.glassBorder),
),
child: Text(info['action']!, style: TextStyle(color: AppTheme.accentCyan, fontSize: 12, fontWeight: FontWeight.w600, height: 1.4)),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(modalContext),
child: const Text('Schließen', style: TextStyle(color: Colors.white)),
),
],
),
);
}
}