feat(app): add evaluation history, bot control panel, simulation visualizer, and vendored signalr_core
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
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)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/// Describes one tunable indicator parameter for a strategy - mirrors the `context.GetParameter(StrategyKey, "...", default)`
|
||||
/// calls in `FinlyticTechnicals/Strategies/CoreStrategies.cs`. Keys sent to the backend are always
|
||||
/// `"{strategyKey}.{name}"`, matching `TechnicalContext.ParameterOverrides` exactly.
|
||||
class StrategyParameterDef {
|
||||
final String name;
|
||||
final String label;
|
||||
final double defaultValue;
|
||||
|
||||
/// Short plain-language explanation of what changing this value actually does, shown via an info tooltip
|
||||
/// next to the field (Rules.md-style transparency: a bare abbreviation like "EMA schnell" means nothing to
|
||||
/// someone who didn't just read the strategy's source code).
|
||||
final String hint;
|
||||
|
||||
/// Whether the field should be entered/rounded as a whole number (indicator periods) vs. a decimal
|
||||
/// (multipliers, thresholds).
|
||||
final bool isInteger;
|
||||
|
||||
const StrategyParameterDef({
|
||||
required this.name,
|
||||
required this.label,
|
||||
required this.defaultValue,
|
||||
required this.hint,
|
||||
this.isInteger = false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Per-strategy list of tunable parameters, for the "Erweiterte Parameter" section of the backtest screen.
|
||||
/// Every default value here must match the corresponding hardcoded default in `CoreStrategies.cs` exactly -
|
||||
/// these are the same values the strategy already uses when nothing is overridden (Rules.md §4: never imply a
|
||||
/// different default than what the backend actually falls back to).
|
||||
class StrategyParameterDefinitions {
|
||||
static const Map<String, List<StrategyParameterDef>> byStrategy = {
|
||||
'TrendPullbackFvg': [
|
||||
StrategyParameterDef(
|
||||
name: 'EmaFast',
|
||||
label: 'EMA schnell',
|
||||
defaultValue: 20,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der kurzen EMA. Kleiner = reagiert schneller auf Kursänderungen, aber mehr Fehlsignale.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'EmaMid',
|
||||
label: 'EMA mittel',
|
||||
defaultValue: 50,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der mittleren EMA, bestätigt zusammen mit der langsamen EMA den übergeordneten Trend.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'EmaSlow',
|
||||
label: 'EMA langsam',
|
||||
defaultValue: 200,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der langsamen EMA. Größer = stabilerer, aber träger erkannter Trend.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 1.2,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR). Größer = weiterer Stop, weniger vorzeitige Ausstiege.',
|
||||
),
|
||||
],
|
||||
'VolatilitySqueeze': [
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 1.0,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'TargetAtrMultiplier',
|
||||
label: 'Kursziel (× ATR)',
|
||||
defaultValue: 2.0,
|
||||
hint: 'Abstand des Kursziels vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
],
|
||||
'SmcLiquiditySweep': [
|
||||
StrategyParameterDef(
|
||||
name: 'StopBufferPercent',
|
||||
label: 'Stop-Puffer (%)',
|
||||
defaultValue: 0.2,
|
||||
hint: 'Zusätzlicher Sicherheitsabstand des Stop-Loss hinter dem Sweep-Extrempunkt, in Prozent.',
|
||||
),
|
||||
],
|
||||
'MeanReversion': [
|
||||
StrategyParameterDef(
|
||||
name: 'BollingerMultiplier',
|
||||
label: 'Bollinger-Multiplikator (σ)',
|
||||
defaultValue: 2.5,
|
||||
hint: 'Breite der Bollinger-Bänder in Standardabweichungen. Größer = seltenere, aber extremere Signale.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'AdxThreshold',
|
||||
label: 'ADX-Schwelle (max.)',
|
||||
defaultValue: 22,
|
||||
hint: 'Nur unterhalb dieser ADX-Trendstärke gilt der Markt als "seitwärts" - Voraussetzung für ein Signal.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'RsiOversold',
|
||||
label: 'RSI überverkauft (≤)',
|
||||
defaultValue: 32,
|
||||
hint: 'RSI-Wert, ab dem der Kurs als überverkauft gilt (Long-Signal-Voraussetzung).',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'RsiOverbought',
|
||||
label: 'RSI überkauft (≥)',
|
||||
defaultValue: 68,
|
||||
hint: 'RSI-Wert, ab dem der Kurs als überkauft gilt (Short-Signal-Voraussetzung).',
|
||||
),
|
||||
],
|
||||
'SuperTrendMultiTf': [
|
||||
StrategyParameterDef(
|
||||
name: 'Period',
|
||||
label: 'SuperTrend-Periode',
|
||||
defaultValue: 10,
|
||||
isInteger: true,
|
||||
hint: 'ATR-Periode, die der SuperTrend-Indikator auf beiden Zeitebenen (1h und 15m) zugrunde legt.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'Multiplier',
|
||||
label: 'SuperTrend-Multiplikator',
|
||||
defaultValue: 3.0,
|
||||
hint: 'Wie weit die SuperTrend-Linie von der ATR-Bandbreite entfernt liegt. Größer = trägere, glattere Linie.',
|
||||
),
|
||||
],
|
||||
'MacdCrossover': [
|
||||
StrategyParameterDef(
|
||||
name: 'FastPeriod',
|
||||
label: 'MACD schnell',
|
||||
defaultValue: 12,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der schnellen EMA im MACD.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'SlowPeriod',
|
||||
label: 'MACD langsam',
|
||||
defaultValue: 26,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der langsamen EMA im MACD.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'SignalPeriod',
|
||||
label: 'MACD Signal',
|
||||
defaultValue: 9,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der Signal-Linie (EMA der MACD-Linie), gegen die auf Kreuzung geprüft wird.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 1.5,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
],
|
||||
'MovingAverageCrossover': [
|
||||
StrategyParameterDef(
|
||||
name: 'FastPeriod',
|
||||
label: 'EMA schnell',
|
||||
defaultValue: 50,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der schnellen EMA (kreuzt für ein Golden/Death Cross durch die langsame EMA).',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'SlowPeriod',
|
||||
label: 'EMA langsam',
|
||||
defaultValue: 200,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der langsamen EMA - je größer, desto seltener und langfristiger die Signale.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 2.0,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
],
|
||||
'RsiReversal': [
|
||||
StrategyParameterDef(
|
||||
name: 'Period',
|
||||
label: 'RSI-Periode',
|
||||
defaultValue: 14,
|
||||
isInteger: true,
|
||||
hint: 'Anzahl Kerzen, über die der RSI berechnet wird.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'OversoldThreshold',
|
||||
label: 'Überverkauft-Schwelle',
|
||||
defaultValue: 30,
|
||||
hint: 'RSI-Schwelle, deren erneutes Überschreiten von unten ein Long-Signal auslöst.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'OverboughtThreshold',
|
||||
label: 'Überkauft-Schwelle',
|
||||
defaultValue: 70,
|
||||
hint: 'RSI-Schwelle, deren erneutes Unterschreiten von oben ein Short-Signal auslöst.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 1.2,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
],
|
||||
'DonchianBreakout': [
|
||||
StrategyParameterDef(
|
||||
name: 'Period',
|
||||
label: 'Kanal-Periode',
|
||||
defaultValue: 20,
|
||||
isInteger: true,
|
||||
hint: 'Anzahl vorheriger Kerzen, deren Hoch/Tief den Ausbruchskanal bilden. Größer = seltenere, dafür signifikantere Ausbrüche.',
|
||||
),
|
||||
],
|
||||
'VwapBounce': [
|
||||
StrategyParameterDef(
|
||||
name: 'EmaFast',
|
||||
label: 'EMA schnell',
|
||||
defaultValue: 20,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der schnellen EMA, bestimmt zusammen mit der langsamen EMA die Trendrichtung.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'EmaSlow',
|
||||
label: 'EMA langsam',
|
||||
defaultValue: 50,
|
||||
isInteger: true,
|
||||
hint: 'Perioden der langsamen EMA, bestimmt zusammen mit der schnellen EMA die Trendrichtung.',
|
||||
),
|
||||
StrategyParameterDef(
|
||||
name: 'StopAtrMultiplier',
|
||||
label: 'Stop-Loss (× ATR)',
|
||||
defaultValue: 1.0,
|
||||
hint: 'Abstand des Stop-Loss vom Einstieg, als Vielfaches der aktuellen Volatilität (ATR).',
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
static List<StrategyParameterDef> forStrategy(String strategyKey) => byStrategy[strategyKey] ?? const [];
|
||||
}
|
||||
Reference in New Issue
Block a user