241 lines
9.3 KiB
Dart
241 lines
9.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../../../core/theme/app_theme.dart';
|
|
import '../models/trade_acceptance_dto.dart';
|
|
import '../models/trade_model.dart';
|
|
|
|
|
|
class TradeAcceptanceDialog extends StatefulWidget {
|
|
final TradeModel trade;
|
|
final ThemePreset theme;
|
|
final String userId;
|
|
|
|
const TradeAcceptanceDialog({
|
|
super.key,
|
|
required this.trade,
|
|
required this.theme,
|
|
required this.userId,
|
|
});
|
|
|
|
@override
|
|
State<TradeAcceptanceDialog> createState() => _TradeAcceptanceDialogState();
|
|
}
|
|
|
|
class _TradeAcceptanceDialogState extends State<TradeAcceptanceDialog> {
|
|
late TextEditingController _entryPriceCtrl;
|
|
late TextEditingController _positionSizeCtrl;
|
|
late TextEditingController _leverageCtrl;
|
|
late TextEditingController _entryFeeCtrl;
|
|
late TextEditingController _exitFeeCtrl;
|
|
late TextEditingController _quantityCtrl;
|
|
late TextEditingController _stopLossCtrl;
|
|
late TextEditingController _takeProfitCtrl;
|
|
late TextEditingController _notesCtrl;
|
|
bool _isRecurring = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_entryPriceCtrl = TextEditingController(text: widget.trade.entryPrice.toStringAsFixed(2));
|
|
_positionSizeCtrl = TextEditingController(text: '1000');
|
|
_leverageCtrl = TextEditingController(text: (widget.trade.maxLeverage > 0 ? widget.trade.maxLeverage : 1).toStringAsFixed(0));
|
|
_stopLossCtrl = TextEditingController(text: widget.trade.stopLoss.toStringAsFixed(2));
|
|
_takeProfitCtrl = TextEditingController(text: widget.trade.takeProfit.toStringAsFixed(2));
|
|
_notesCtrl = TextEditingController();
|
|
_entryFeeCtrl = TextEditingController(text: '0.00');
|
|
_exitFeeCtrl = TextEditingController(text: '0.00');
|
|
double price = widget.trade.entryPrice;
|
|
_quantityCtrl = TextEditingController(text: (1000 / (price > 0 ? price : 1)).toStringAsFixed(4));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_entryPriceCtrl.dispose();
|
|
_positionSizeCtrl.dispose();
|
|
_leverageCtrl.dispose();
|
|
_stopLossCtrl.dispose();
|
|
_takeProfitCtrl.dispose();
|
|
_notesCtrl.dispose();
|
|
_entryFeeCtrl.dispose();
|
|
_exitFeeCtrl.dispose();
|
|
_quantityCtrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
double? _parseNum(String text) {
|
|
final cleaned = text.replaceAll(',', '.').trim();
|
|
return double.tryParse(cleaned);
|
|
}
|
|
|
|
void _recalcQuantity() {
|
|
double posSize = _parseNum(_positionSizeCtrl.text) ?? 0;
|
|
double price = _parseNum(_entryPriceCtrl.text) ?? 0;
|
|
if (price > 0) {
|
|
_quantityCtrl.text = (posSize / price).toStringAsFixed(4);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
insetPadding: const EdgeInsets.all(16),
|
|
child: Container(
|
|
width: 450,
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: BoxDecoration(
|
|
color: widget.theme.cardSurface,
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(color: widget.theme.glassBorder, width: 1),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: 0.5),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
)
|
|
],
|
|
),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(Icons.add_shopping_cart, color: widget.theme.primaryColor, size: 28),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'Trade Akzeptieren - ${widget.trade.symbol}',
|
|
style: TextStyle(
|
|
color: widget.theme.textPrimary,
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
icon: Icon(Icons.close, color: widget.theme.textMuted),
|
|
)
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
_buildInputField('Kaufkurs / Entry Price', _entryPriceCtrl, Icons.attach_money, onChanged: (_) => _recalcQuantity()),
|
|
const SizedBox(height: 16),
|
|
|
|
Row(
|
|
children: [
|
|
Expanded(child: _buildInputField('Kapital (Position Size)', _positionSizeCtrl, Icons.account_balance_wallet, onChanged: (_) => _recalcQuantity())),
|
|
const SizedBox(width: 16),
|
|
Expanded(child: _buildInputField('Stückzahl (Quantity)', _quantityCtrl, Icons.format_list_numbered)),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
Row(
|
|
children: [
|
|
Expanded(child: _buildInputField('Stop Loss', _stopLossCtrl, Icons.block)),
|
|
const SizedBox(width: 16),
|
|
Expanded(child: _buildInputField('Take Profit', _takeProfitCtrl, Icons.trending_up)),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
Row(
|
|
children: [
|
|
Expanded(child: _buildInputField('Hebel (Leverage)', _leverageCtrl, Icons.speed)),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: CheckboxListTile(
|
|
title: Text('Sparplan', style: TextStyle(color: widget.theme.textPrimary, fontSize: 14)),
|
|
value: _isRecurring,
|
|
activeColor: widget.theme.primaryColor,
|
|
checkColor: Colors.white,
|
|
onChanged: (val) {
|
|
setState(() {
|
|
_isRecurring = val ?? false;
|
|
});
|
|
},
|
|
contentPadding: EdgeInsets.zero,
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildInputField('Notizen', _notesCtrl, Icons.edit),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
final dto = TradeAcceptanceDto(
|
|
userId: widget.userId,
|
|
tradeId: widget.trade.id,
|
|
analysisId: widget.trade.analysisId,
|
|
isin: widget.trade.isin,
|
|
symbol: widget.trade.symbol,
|
|
actualEntryPrice: _parseNum(_entryPriceCtrl.text),
|
|
positionSize: _parseNum(_positionSizeCtrl.text),
|
|
leverageUsed: _parseNum(_leverageCtrl.text) ?? 1.0,
|
|
entryFee: _parseNum(_entryFeeCtrl.text) ?? 0.0,
|
|
exitFee: _parseNum(_exitFeeCtrl.text) ?? 0.0,
|
|
quantity: _parseNum(_quantityCtrl.text),
|
|
isRecurring: _isRecurring,
|
|
executionTimestamp: DateTime.now().toUtc(),
|
|
signalType: widget.trade.signalType,
|
|
entryPrice: widget.trade.entryPrice,
|
|
stopLoss: widget.trade.stopLoss,
|
|
takeProfit: widget.trade.takeProfit,
|
|
instrumentType: widget.trade.instrumentType,
|
|
timeframe: widget.trade.timeframe,
|
|
reasoning: widget.trade.reasoning,
|
|
);
|
|
Navigator.of(context).pop(dto);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: widget.theme.primaryColor,
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
),
|
|
child: const Text('Jetzt Ausführen', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInputField(String label, TextEditingController controller, IconData icon, {Function(String)? onChanged}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: TextStyle(color: widget.theme.textSecondary, fontSize: 12)),
|
|
const SizedBox(height: 6),
|
|
TextField(
|
|
controller: controller,
|
|
style: TextStyle(color: widget.theme.textPrimary),
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
onChanged: onChanged,
|
|
decoration: InputDecoration(
|
|
prefixIcon: Icon(icon, color: widget.theme.textMuted, size: 18),
|
|
filled: true,
|
|
fillColor: widget.theme.darkBackground.withValues(alpha: 0.5),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|