import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/widgets/glass_container.dart'; import '../../../../core/widgets/shimmer_loading.dart'; import '../../../../core/widgets/status_badge.dart'; import 'package:finlytic_app/features/trades/models/trade_model.dart'; import 'package:finlytic_app/features/asset_detail/models/manual_analysis_request_dto.dart'; import 'package:finlytic_app/features/trades/widgets/trade_execution_dialog.dart'; import '../../bloc/trades/asset_trades_bloc.dart'; import '../../bloc/trades/asset_trades_event.dart'; import '../../bloc/trades/asset_trades_state.dart'; class TradesTab extends StatefulWidget { final String symbol; const TradesTab({super.key, required this.symbol}); @override State createState() => _TradesTabState(); } class _TradesTabState extends State { bool _justTriggeredAnalysis = false; // Settings State double _defaultPositionSize = 2500.0; double _defaultLeverage = 5.0; double _defaultRiskScore = 50.0; double _defaultOrderFee = 1.0; bool _autoAcceptSignals = false; @override void initState() { super.initState(); context.read().add(LoadAssetTrades(widget.symbol)); } void _showLiveTradeSettingsDialog(BuildContext context) { double tempPos = _defaultPositionSize; double tempLev = _defaultLeverage; double tempRisk = _defaultRiskScore; double tempFee = _defaultOrderFee; bool tempAuto = _autoAcceptSignals; final posController = TextEditingController(text: tempPos.toStringAsFixed(0)); final levController = TextEditingController(text: tempLev.toStringAsFixed(1)); final feeController = TextEditingController(text: tempFee.toStringAsFixed(2)); showDialog( context: context, builder: (dialogContext) { return StatefulBuilder( builder: (builderContext, setModalState) { return AlertDialog( backgroundColor: AppTheme.cardSurface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: AppTheme.glassBorder), ), title: Row( children: [ Icon(Icons.settings, color: AppTheme.accentCyan, size: 22), const SizedBox(width: 8), Expanded( child: Text('Live Trade Einstellungen', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), ), ], ), content: SizedBox( width: 440, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Standard Trade-Vorgaben für Ihr Depot:', style: TextStyle(color: AppTheme.textSecondary, fontSize: 12)), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: posController, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration(labelText: 'Standard Investment (€)', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), onChanged: (v) => tempPos = double.tryParse(v) ?? tempPos, ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: levController, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration(labelText: 'Standard Hebel (x)', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), onChanged: (v) => tempLev = double.tryParse(v) ?? tempLev, ), ), ], ), const SizedBox(height: 12), TextField( controller: feeController, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration(labelText: 'Standard Ordergebühr (€)', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), onChanged: (v) => tempFee = double.tryParse(v) ?? tempFee, ), const SizedBox(height: 14), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Standard Risiko-Toleranz:', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), Text('${tempRisk.toInt()}/100', style: TextStyle(color: AppTheme.primaryEmerald, fontWeight: FontWeight.bold, fontSize: 13)), ], ), Slider( value: tempRisk, min: 0, max: 100, divisions: 100, activeColor: AppTheme.primaryEmerald, inactiveColor: AppTheme.glassSurface, onChanged: (val) => setModalState(() => tempRisk = val), ), const SizedBox(height: 10), SwitchListTile( value: tempAuto, activeThumbColor: AppTheme.primaryEmerald, title: const Text('KI-Signale automatisch annehmen', style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.bold)), subtitle: Text('Führt eingehende Signale direkt im Depot aus', style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), onChanged: (val) => setModalState(() => tempAuto = val), ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: Text('Abbrechen', style: TextStyle(color: AppTheme.textMuted)), ), ElevatedButton.icon( onPressed: () { setState(() { _defaultPositionSize = tempPos; _defaultLeverage = tempLev; _defaultRiskScore = tempRisk; _defaultOrderFee = tempFee; _autoAcceptSignals = tempAuto; }); Navigator.pop(dialogContext); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Live Trade Einstellungen gespeichert.'), backgroundColor: AppTheme.primaryEmerald, behavior: SnackBarBehavior.floating, ), ); }, icon: const Icon(Icons.save), label: const Text('Einstellungen Speichern'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentCyan, foregroundColor: Colors.black, ), ), ], ); }, ); }, ); } void _showCloseTradeDialog(BuildContext context, TradeModel trade) { final tradesBloc = context.read(); final entry = trade.actualEntryPrice > 0 ? trade.actualEntryPrice : trade.entryPrice; final exitController = TextEditingController(text: entry.toStringAsFixed(2)); showDialog( context: context, builder: (dialogContext) { return AlertDialog( backgroundColor: AppTheme.cardSurface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: AppTheme.glassBorder), ), title: Row( children: [ Icon(Icons.flag_outlined, color: AppTheme.accentRed, size: 22), const SizedBox(width: 8), Expanded( child: Text('Trade Position Schließen', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), ), ], ), content: SizedBox( width: 400, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Trade-ID: ${trade.id} | Asset: ${trade.symbol.isNotEmpty ? trade.symbol : widget.symbol}', style: TextStyle(color: AppTheme.textSecondary, fontSize: 12)), const SizedBox(height: 16), TextField( controller: exitController, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration( labelText: 'Tatsächlicher Ausstiegskurs (€)', hintText: 'Z.B. 105.50', ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: Text('Abbrechen', style: TextStyle(color: AppTheme.textMuted)), ), ElevatedButton.icon( onPressed: () { final exitPrice = double.tryParse(exitController.text) ?? entry; final tradeId = trade.id; if (tradeId.isNotEmpty) { tradesBloc.add(CloseTradeEvent(tradeId, widget.symbol, exitPrice)); } Navigator.pop(dialogContext); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Trade $tradeId geschlossen zu €${exitPrice.toStringAsFixed(2)}.'), backgroundColor: AppTheme.accentRed, behavior: SnackBarBehavior.floating, ), ); }, icon: const Icon(Icons.check_circle), label: const Text('Position Schließen'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentRed, foregroundColor: Colors.white, ), ), ], ); }, ); } void _showAnalysisParametersDialog(BuildContext context) { final tradesBloc = context.read(); final minTimeframeController = TextEditingController(text: '1'); final maxTimeframeController = TextEditingController(text: '14'); double riskScore = _defaultRiskScore; String timeframeUnit = 'Tage'; String instrumentType = 'Aktie / ETF (Direktinvestment)'; final notesController = TextEditingController(); showDialog( context: context, builder: (dialogContext) { return StatefulBuilder( builder: (builderContext, setModalState) { return AlertDialog( backgroundColor: AppTheme.cardSurface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: AppTheme.glassBorder), ), title: Row( children: [ Icon(Icons.auto_awesome, color: AppTheme.accentCyan, size: 22), const SizedBox(width: 8), Expanded( child: Text('KI-Analyse Konfigurieren', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), ), ], ), content: SizedBox( width: 480, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Asset / ISIN: ${widget.symbol}', style: TextStyle(color: AppTheme.textSecondary, fontSize: 12)), const SizedBox(height: 16), // 1. Haltedauer von - bis mit Einheit const Text('Geplante Haltedauer:', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), const SizedBox(height: 8), Row( children: [ Expanded( child: TextField( controller: minTimeframeController, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: 'Von', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: maxTimeframeController, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: 'Bis', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), ), ), const SizedBox(width: 8), Expanded( child: DropdownButtonFormField( initialValue: timeframeUnit, dropdownColor: AppTheme.cardSurface, decoration: const InputDecoration(labelText: 'Einheit', contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8)), items: const [ DropdownMenuItem(value: 'Stunden', child: Text('Stunden')), DropdownMenuItem(value: 'Tage', child: Text('Tage')), DropdownMenuItem(value: 'Wochen', child: Text('Wochen')), DropdownMenuItem(value: 'Monate', child: Text('Monate')), ], onChanged: (val) { if (val != null) setModalState(() => timeframeUnit = val); }, ), ), ], ), const SizedBox(height: 16), // 2. Risikobereitschaft 0-100 Slider Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Risikobereitschaft:', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), Text( '${riskScore.toInt()}/100 (${riskScore < 30 ? "Konservativ" : (riskScore < 70 ? "Ausgewogen" : "Spekulativ")})', style: TextStyle(color: riskScore < 30 ? AppTheme.primaryEmerald : (riskScore < 70 ? Colors.orangeAccent : AppTheme.accentRed), fontWeight: FontWeight.bold, fontSize: 13), ), ], ), Slider( value: riskScore, min: 0, max: 100, divisions: 100, activeColor: riskScore < 30 ? AppTheme.primaryEmerald : (riskScore < 70 ? Colors.orangeAccent : AppTheme.accentRed), inactiveColor: AppTheme.glassSurface, onChanged: (val) => setModalState(() => riskScore = val), ), const SizedBox(height: 12), // 3. Instrumententyp (Trade Republic typisch) const Text('Instrumententyp (Trade Republic):', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), const SizedBox(height: 6), DropdownButtonFormField( initialValue: instrumentType, dropdownColor: AppTheme.cardSurface, decoration: const InputDecoration(contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 10)), items: const [ DropdownMenuItem(value: 'Aktie / ETF (Direktinvestment)', child: Text('Aktie / ETF (Direktinvestment)')), DropdownMenuItem(value: 'Optionsschein (Warrant)', child: Text('Optionsschein (Warrant)')), DropdownMenuItem(value: 'Knock-Out Zertifikat (Turbo)', child: Text('Knock-Out Zertifikat (Turbo)')), DropdownMenuItem(value: 'Faktor-Zertifikat', child: Text('Faktor-Zertifikat')), DropdownMenuItem(value: 'Krypto (Crypto)', child: Text('Krypto (Crypto)')), ], onChanged: (val) { if (val != null) setModalState(() => instrumentType = val); }, ), const SizedBox(height: 16), // 4. Anmerkung für die KI const Text('Anmerkung für die KI:', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), const SizedBox(height: 6), TextField( controller: notesController, maxLines: 3, decoration: const InputDecoration( hintText: 'Z.B. Besonderes Augenmerk auf Hebelprodukte legen, enge Stopps berücksichtigen...', ), ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: Text('Abbrechen', style: TextStyle(color: AppTheme.textMuted)), ), ElevatedButton.icon( onPressed: () { final payload = ManualAnalysisRequestDto( isin: widget.symbol, symbol: widget.symbol, riskScore: riskScore.toInt(), minTimeframeValue: int.tryParse(minTimeframeController.text) ?? 1, maxTimeframeValue: int.tryParse(maxTimeframeController.text) ?? 14, timeframeUnit: timeframeUnit, instrumentType: instrumentType, userNotes: notesController.text, headline: 'Manuelle KI-Analyse für ${widget.symbol}', ); setState(() { _justTriggeredAnalysis = true; }); tradesBloc.add(TriggerManualAnalysis(widget.symbol, payload: payload)); Navigator.pop(dialogContext); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('KI-Analyse für ${widget.symbol} gestartet. Trade-Ausführungsdialog öffnet sich in Kürze...'), backgroundColor: AppTheme.accentCyan, behavior: SnackBarBehavior.floating, ), ); }, icon: const Icon(Icons.flash_on), label: const Text('Analyse Jetzt Ausführen'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentCyan, foregroundColor: Colors.black, ), ), ], ); }, ); }, ); } void _showEditTradeExecutionDialog(BuildContext context, TradeModel trade, {bool isActive = false}) { final tradesBloc = context.read(); TradeExecutionDialog.show( context, trade: trade, defaultSymbol: widget.symbol, isActive: isActive, onAccept: (dto) { tradesBloc.add(AcceptTradeEvent(dto, widget.symbol)); final tId = trade.id; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(isActive ? 'Einstellungen für Trade $tId gespeichert!' : 'Trade $tId angenommen & Position eröffnet!'), backgroundColor: AppTheme.primaryEmerald, behavior: SnackBarBehavior.floating, ), ); }, onReject: (tId) { tradesBloc.add(RejectTradeEvent(tId, widget.symbol)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Trade $tId abgelehnt.'), backgroundColor: AppTheme.textSecondary, behavior: SnackBarBehavior.floating, ), ); }, ); } @override Widget build(BuildContext context) { return BlocConsumer( listener: (context, state) { if (_justTriggeredAnalysis && state is AssetTradesLoaded) { final List tradesList = state.data; if (tradesList.isNotEmpty) { _justTriggeredAnalysis = false; final latestTrade = tradesList.first; WidgetsBinding.instance.addPostFrameCallback((_) { _showEditTradeExecutionDialog(context, latestTrade); }); } } }, builder: (context, state) { final List tradesList = (state is AssetTradesLoaded) ? state.data : []; return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Action Button & Settings Card GlassContainer( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Trade & Signal Management', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: Colors.white)), const SizedBox(height: 4), Text('KI-gestützte technische & fundamentale Trade-Analyse anfordern', style: TextStyle(color: AppTheme.textMuted, fontSize: 12), maxLines: 1, overflow: TextOverflow.ellipsis), ], ), ), const SizedBox(width: 8), StatusBadge(label: widget.symbol, color: AppTheme.accentCyan), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _showAnalysisParametersDialog(context), icon: const Icon(Icons.auto_awesome, size: 18), label: const Text('Analyse starten', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentCyan, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(width: 10), IconButton.filledTonal( onPressed: () => _showLiveTradeSettingsDialog(context), icon: const Icon(Icons.settings, color: Colors.white), tooltip: 'Live Trade Einstellungen', style: IconButton.styleFrom( backgroundColor: AppTheme.glassSurface, padding: const EdgeInsets.all(14), ), ), ], ), ], ), ), const SizedBox(height: 20), if (state is AssetTradesLoading) _buildTradesShimmer(context) else if (state is AssetTradesError) GlassContainer( padding: const EdgeInsets.all(16), child: Text('Fehler: ${state.message}', style: TextStyle(color: AppTheme.accentRed)), ) else if (state is AssetTradesLoaded) ...[ _buildTradeList( 'Aktive Trade-Signale & Positionen', tradesList.where((t) { final s = t.status.toUpperCase(); return s == 'ACTIVE' || s == 'PENDING' || s == 'PROPOSED'; }).toList(), ), const SizedBox(height: 20), _buildTradeList( 'Historische Trades & KI-Bewertungen', tradesList.where((t) { final s = t.status.toUpperCase(); return s == 'CLOSED' || s == 'REJECTED' || (s != 'ACTIVE' && s != 'PENDING' && s != 'PROPOSED'); }).toList(), ), ], ], ), ); }, ); } Widget _buildTradesShimmer(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const ShimmerLoading(width: 260, height: 20, borderRadius: 6), const SizedBox(height: 12), for (int i = 0; i < 3; i++) ...[ const ShimmerLoading(width: double.infinity, height: 105, borderRadius: 14), const SizedBox(height: 12), ], ], ); } Widget _buildTradeList(String title, List trades) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)), const SizedBox(height: 10), if (trades.isEmpty) GlassContainer( padding: const EdgeInsets.all(16), child: Center( child: Text('Keine Trades in dieser Kategorie vorhanden.', style: TextStyle(color: AppTheme.textMuted, fontSize: 12)), ), ) else ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: trades.length, itemBuilder: (context, index) { final trade = trades[index]; return _buildRichTradeCard(trade); }, ), ], ); } Widget _buildRichTradeCard(TradeModel trade) { final isin = trade.isin.isNotEmpty ? trade.isin : widget.symbol; final side = (trade.signalType.isNotEmpty ? trade.signalType : 'BUY').toUpperCase(); final status = trade.status.toUpperCase(); final isBuy = side == 'BUY' || side == 'LONG'; final isActive = status == 'ACTIVE'; final sideColor = isBuy ? AppTheme.primaryEmerald : AppTheme.accentRed; // AI Execution Plan N8N values final entryZoneMin = trade.entryZoneMin; final entryZoneMax = trade.entryZoneMax; final entryPrice = trade.entryPrice; final stopLoss = trade.stopLoss; final takeProfit = trade.takeProfit; final takeProfitTargets = trade.takeProfitTargets; final crv = (takeProfit > 0 && stopLoss > 0 && entryPrice > 0) ? ((takeProfit - entryPrice).abs() / (entryPrice - stopLoss).abs()).toStringAsFixed(2) : null; final maxLeverage = trade.maxLeverage; // Real User Execution Values final actualEntry = trade.actualEntryPrice; final posSize = trade.positionSize; final levUsed = trade.leverageUsed; final qty = trade.positionSize > 0 && trade.actualEntryPrice > 0 ? trade.positionSize / trade.actualEntryPrice : 0; final entryFee = trade.entryFee; final exitFee = trade.exitFee; // Rationale strings final reasoning = trade.reasoning; final techRationale = trade.technicalRationale; final fundRationale = trade.fundamentalRationale; final riskWarning = trade.riskWarning; return Padding( padding: const EdgeInsets.only(bottom: 12), child: GlassContainer( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row: Side, Status, Instrument, Action Buttons cv Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ StatusBadge(label: side, color: sideColor), const SizedBox(width: 8), StatusBadge(label: status, color: isActive ? AppTheme.primaryEmerald : (status == 'PROPOSED' ? AppTheme.accentCyan : AppTheme.textMuted)), const SizedBox(width: 8), if (trade.instrumentType.isNotEmpty) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: AppTheme.glassSurface, borderRadius: BorderRadius.circular(6), ), child: Text(trade.instrumentType, style: TextStyle(color: AppTheme.textSecondary, fontSize: 11, fontWeight: FontWeight.bold)), ), ], ), Row( children: [ if (isActive) ...[ ElevatedButton.icon( onPressed: () => _showCloseTradeDialog(context, trade), icon: const Icon(Icons.flag_outlined, size: 14), label: const Text('Schließen'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentRed, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), const SizedBox(width: 8), IconButton( onPressed: () => _showEditTradeExecutionDialog(context, trade, isActive: true), icon: const Icon(Icons.settings, size: 16, color: Colors.white), style: IconButton.styleFrom( backgroundColor: AppTheme.glassSurface, padding: const EdgeInsets.all(8), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), ] else if (status == 'PROPOSED' || status == 'PENDING') ...[ ElevatedButton.icon( onPressed: () => _showEditTradeExecutionDialog(context, trade), icon: const Icon(Icons.check_circle, size: 14), label: const Text('Trade Annehmen'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryEmerald, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), ], ], ), ], ), const SizedBox(height: 12), // Asset ID & Timeframe Subheader Text('${trade.companyName.isNotEmpty ? trade.companyName : widget.symbol} ($isin) | Timeframe: ${trade.timeframe} | Risk: ${trade.riskTolerance}', style: TextStyle(color: AppTheme.textMuted, fontSize: 12)), const SizedBox(height: 12), // AI Execution Targets Grid (Entry Zone, SL, TP, CRV, MaxLeverage) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.glassSurface, borderRadius: BorderRadius.circular(10), border: Border.all(color: AppTheme.glassBorder), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildTradeStat('Einstiegszone', entryZoneMin > 0 && entryZoneMax > 0 ? '€${_fmt(entryZoneMin)} - €${_fmt(entryZoneMax)}' : '€${_fmt(entryPrice)}', Colors.white), _buildTradeStat('Stop-Loss', '€${_fmt(stopLoss)}', AppTheme.accentRed), _buildTradeStat('Take-Profit', takeProfitTargets.isNotEmpty ? takeProfitTargets.map((t) => '€${_fmt(t)}').join(' / ') : '€${_fmt(takeProfit)}', AppTheme.primaryEmerald), ], ), if (crv != null || maxLeverage > 0) ...[ const Divider(color: Colors.white12, height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (crv != null) _buildTradeStat('Chance-Risiko-Verh. (CRV)', '${_fmt(crv)}x', AppTheme.accentCyan), if (maxLeverage > 0) _buildTradeStat('Max. Hebel', '${_fmt(maxLeverage)}x', Colors.orangeAccent), ], ), ], ], ), ), // Real User Execution Data Section (Actual Entry, Position Size, Leverage Used, Fees, Quantity) if (actualEntry > 0 || posSize > 0 || levUsed > 0 || qty > 0) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.primaryEmerald.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(10), border: Border.all(color: AppTheme.primaryEmerald.withValues(alpha: 0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.person_pin_outlined, size: 14, color: AppTheme.primaryEmerald), const SizedBox(width: 6), const Text('Ihre Tatsächlichen Ausführungsdaten:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12, color: Colors.white)), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildTradeStat('Tatsächl. Einstieg', '€${_fmt(actualEntry > 0 ? actualEntry : entryPrice)}', Colors.white), _buildTradeStat('Investition', posSize > 0 ? '€${_fmt(posSize)}' : 'N/A', Colors.white), _buildTradeStat('Genutzter Hebel', levUsed > 0 ? '${_fmt(levUsed)}x' : '1x', AppTheme.primaryEmerald), _buildTradeStat('Stückzahl', qty > 0 ? '${_fmt(qty)} Stk.' : 'N/A', Colors.white70), ], ), if (entryFee > 0 || exitFee > 0) ...[ const SizedBox(height: 6), Text('Gebühren: Einstieg €${_fmt(entryFee)} | Ausstieg €${_fmt(exitFee)}', style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), ], ], ), ), ], // Closed Trade Outcome & Performance Section if (status == 'CLOSED' || trade.pnlAbsolute != 0) ...[ const SizedBox(height: 12), Builder( builder: (context) { final pnlVal = trade.calculatedPnlAbs; final pnlPctVal = trade.calculatedPnlPct; final isWin = pnlVal >= 0; final color = isWin ? AppTheme.primaryEmerald : AppTheme.accentRed; return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), border: Border.all(color: color), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon( isWin ? Icons.trending_up : Icons.trending_down, size: 16, color: color, ), const SizedBox(width: 6), const Text('Trade Ergebnis & Realisierter PnL:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13, color: Colors.white)), ], ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildTradeStat('Ausstiegskurs', 'N/A', Colors.white), _buildTradeStat( 'Realisierter PnL (€)', '${(isWin ? "+€" : "-€")}${_fmt(pnlVal.abs())}', color, ), _buildTradeStat( 'Rendite (%)', '${(pnlPctVal >= 0 ? "+" : "")}${_fmt(pnlPctVal)}%', pnlPctVal >= 0 ? AppTheme.primaryEmerald : AppTheme.accentRed, ), ], ), ], ), ); }, ), ], // AI Rationale & Warnings if (reasoning.isNotEmpty || techRationale.isNotEmpty || fundRationale.isNotEmpty || riskWarning.isNotEmpty) ...[ const SizedBox(height: 12), ExpansionTile( tilePadding: EdgeInsets.zero, childrenPadding: EdgeInsets.zero, dense: true, title: Text('KI-Analyse & Begründung anzeigen', style: TextStyle(color: AppTheme.accentCyan, fontWeight: FontWeight.bold, fontSize: 13)), children: [ if (reasoning.isNotEmpty) ...[ _buildRationaleBlock('Gesamt-Strategie & KI-Entscheidung', reasoning, Colors.white70), const SizedBox(height: 8), ], if (techRationale.isNotEmpty) ...[ _buildRationaleBlock('Technische Analyse', techRationale, Colors.white70), const SizedBox(height: 8), ], if (fundRationale.isNotEmpty) ...[ _buildRationaleBlock('Fundamentale Analyse', fundRationale, Colors.white70), const SizedBox(height: 8), ], if (riskWarning.isNotEmpty) _buildRationaleBlock('Risikowarnung', riskWarning, AppTheme.accentRed), ], ), ], ], ), ), ); } Widget _buildTradeStat(String title, String val, Color col) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: TextStyle(color: AppTheme.textMuted, fontSize: 10)), const SizedBox(height: 2), Text(val, style: TextStyle(color: col, fontWeight: FontWeight.bold, fontSize: 13)), ], ); } Widget _buildRationaleBlock(String title, String text, Color col) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 12)), const SizedBox(height: 2), Text(text, style: TextStyle(color: col, fontSize: 12, height: 1.4)), ], ); } String _fmt(dynamic val) { if (val == null) return 'N/A'; final n = (val is num) ? val.toDouble() : double.tryParse(val.toString()); return n != null ? n.toStringAsFixed(2) : val.toString(); } }