feat(trades): add live execution cockpit, closing cockpit, calculation cards and precision trade settings
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../core/network/api_client.dart';
|
||||
import '../../favorites/cubit/favorites_cubit.dart';
|
||||
import '../bloc/fundamentals/asset_fundamentals_bloc.dart';
|
||||
import '../bloc/fundamentals/asset_fundamentals_event.dart';
|
||||
import '../bloc/technical/asset_technical_bloc.dart';
|
||||
@@ -8,6 +9,7 @@ import '../bloc/technical/asset_technical_event.dart';
|
||||
import '../bloc/trades/asset_trades_bloc.dart';
|
||||
import '../bloc/trades/asset_trades_event.dart';
|
||||
import '../repositories/asset_repository.dart';
|
||||
import '../utils/ticker_resolver.dart';
|
||||
import 'layouts/asset_page_desktop_layout.dart';
|
||||
import 'layouts/asset_page_mobile_layout.dart';
|
||||
|
||||
@@ -29,39 +31,49 @@ class AssetDetailScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final repository = AssetRepository(apiClient: apiClient);
|
||||
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => AssetFundamentalsBloc(repository: repository)
|
||||
..add(LoadAssetFundamentals(isin, ticker: symbol)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => AssetTechnicalBloc(repository: repository)
|
||||
..add(LoadAssetTechnical(isin, ticker: symbol)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => AssetTradesBloc(repository: repository)
|
||||
..add(LoadAssetTrades(isin)),
|
||||
),
|
||||
],
|
||||
child: Scaffold(
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth >= 900) {
|
||||
return AssetPageDesktopLayout(
|
||||
isin: isin,
|
||||
name: name,
|
||||
selectedTicker: symbol,
|
||||
);
|
||||
}
|
||||
return AssetPageMobileLayout(
|
||||
isin: isin,
|
||||
name: name,
|
||||
selectedTicker: symbol,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
return BlocBuilder<FavoritesCubit, FavoritesState>(
|
||||
builder: (context, favState) {
|
||||
final initialTicker = TickerResolver.resolve(
|
||||
isin: isin,
|
||||
candidateSymbol: symbol,
|
||||
favoriteDetails: favState.favoriteDetails,
|
||||
);
|
||||
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => AssetFundamentalsBloc(repository: repository)
|
||||
..add(LoadAssetFundamentals(isin, ticker: initialTicker)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => AssetTechnicalBloc(repository: repository)
|
||||
..add(LoadAssetTechnical(isin, ticker: initialTicker)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => AssetTradesBloc(repository: repository)
|
||||
..add(LoadAssetTrades(isin)),
|
||||
),
|
||||
],
|
||||
child: Scaffold(
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth >= 900) {
|
||||
return AssetPageDesktopLayout(
|
||||
isin: isin,
|
||||
name: name,
|
||||
selectedTicker: initialTicker ?? symbol,
|
||||
);
|
||||
}
|
||||
return AssetPageMobileLayout(
|
||||
isin: isin,
|
||||
name: name,
|
||||
selectedTicker: initialTicker ?? symbol,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@ import '../../../../core/widgets/glass_container.dart';
|
||||
import '../../../../core/widgets/shimmer_loading.dart';
|
||||
import '../../../../core/widgets/status_badge.dart';
|
||||
import '../../../trades/models/trade_model.dart';
|
||||
import '../../../trades/widgets/trade_execution_dialog.dart';
|
||||
import '../../../trades/widgets/trade_execution_cockpit.dart';
|
||||
import '../../../trades/widgets/trade_closing_cockpit.dart';
|
||||
import '../../bloc/trades/asset_trades_bloc.dart';
|
||||
import '../../bloc/trades/asset_trades_event.dart';
|
||||
import '../../bloc/trades/asset_trades_state.dart';
|
||||
import '../../widgets/trades/live_trade_settings_dialog.dart';
|
||||
import '../../widgets/trades/manual_analysis_dialog.dart';
|
||||
import '../../widgets/trades/close_trade_dialog.dart';
|
||||
import '../../widgets/trades/asset_trade_item_card.dart';
|
||||
|
||||
class TradesTab extends StatefulWidget {
|
||||
@@ -24,13 +23,6 @@ class TradesTab extends StatefulWidget {
|
||||
|
||||
class _TradesTabState extends State<TradesTab> {
|
||||
bool _justTriggeredAnalysis = false;
|
||||
LiveTradeSettings _settings = const LiveTradeSettings(
|
||||
defaultPositionSize: 2500.0,
|
||||
defaultLeverage: 5.0,
|
||||
defaultRiskScore: 50.0,
|
||||
defaultOrderFee: 1.0,
|
||||
autoAcceptSignals: false,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -41,7 +33,7 @@ class _TradesTabState extends State<TradesTab> {
|
||||
void _showEditTradeExecutionDialog(BuildContext context, TradeModel trade, {bool isActive = false}) {
|
||||
final tradesBloc = context.read<AssetTradesBloc>();
|
||||
|
||||
TradeExecutionDialog.show(
|
||||
TradeExecutionCockpit.show(
|
||||
context,
|
||||
trade: trade,
|
||||
defaultSymbol: widget.symbol,
|
||||
@@ -116,55 +108,36 @@ class _TradesTabState extends State<TradesTab> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
ManualAnalysisDialog.show(
|
||||
context,
|
||||
symbol: widget.symbol,
|
||||
initialRiskScore: _settings.defaultRiskScore,
|
||||
onTrigger: (payload) {
|
||||
setState(() => _justTriggeredAnalysis = true);
|
||||
context.read<AssetTradesBloc>().add(TriggerManualAnalysis(widget.symbol, payload: payload));
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
ManualAnalysisDialog.show(
|
||||
context,
|
||||
symbol: widget.symbol,
|
||||
initialRiskScore: 50.0,
|
||||
onTrigger: (payload) {
|
||||
setState(() => _justTriggeredAnalysis = true);
|
||||
context.read<AssetTradesBloc>().add(TriggerManualAnalysis(widget.symbol, payload: payload));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('KI-Analyse für ${widget.symbol} abgeschlossen. Trade-Cockpit öffnet sich...'),
|
||||
backgroundColor: AppTheme.accentCyan,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
},
|
||||
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)),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.auto_awesome, size: 18),
|
||||
label: const Text('KI-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(12)),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
IconButton.filledTonal(
|
||||
onPressed: () {
|
||||
LiveTradeSettingsDialog.show(
|
||||
context,
|
||||
currentSettings: _settings,
|
||||
onSave: (newSettings) => setState(() => _settings = newSettings),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.settings, color: Colors.white),
|
||||
tooltip: 'Live Trade Einstellungen',
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: AppTheme.glassSurface,
|
||||
padding: const EdgeInsets.all(14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -245,7 +218,7 @@ class _TradesTabState extends State<TradesTab> {
|
||||
onSettings: () => _showEditTradeExecutionDialog(context, trade, isActive: true),
|
||||
onClose: isActive
|
||||
? () {
|
||||
CloseTradeDialog.show(
|
||||
TradeClosingCockpit.show(
|
||||
context,
|
||||
trade: trade,
|
||||
defaultSymbol: widget.symbol,
|
||||
@@ -254,7 +227,7 @@ class _TradesTabState extends State<TradesTab> {
|
||||
context.read<AssetTradesBloc>().add(CloseTradeEvent(trade.id, isinVal, dto.userExitPrice));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Trade ${trade.id} geschlossen! Ausstiegskurs: €${dto.userExitPrice.toStringAsFixed(2)}'),
|
||||
content: Text('Trade ${trade.id} geschlossen! Realisierter Ausstiegskurs: €${dto.userExitPrice.toStringAsFixed(2)}'),
|
||||
backgroundColor: AppTheme.primaryEmerald,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
@@ -270,3 +243,4 @@ class _TradesTabState extends State<TradesTab> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user