feat(trades): add live execution cockpit, closing cockpit, calculation cards and precision trade settings
This commit is contained in:
@@ -1,5 +1,59 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum DriftStatus {
|
||||
onTrack,
|
||||
trailingActive,
|
||||
driftWarning,
|
||||
exitAlert,
|
||||
}
|
||||
|
||||
class TradeHourlyUpdateModel extends Equatable {
|
||||
final String recommendation;
|
||||
final double currentPrice;
|
||||
final double? suggestedStopLoss;
|
||||
final double? suggestedTakeProfit;
|
||||
final double vixValue;
|
||||
final String reasoning;
|
||||
final DateTime timestamp;
|
||||
|
||||
const TradeHourlyUpdateModel({
|
||||
required this.recommendation,
|
||||
required this.currentPrice,
|
||||
this.suggestedStopLoss,
|
||||
this.suggestedTakeProfit,
|
||||
this.vixValue = 0.0,
|
||||
required this.reasoning,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
factory TradeHourlyUpdateModel.fromJson(Map<String, dynamic> json) {
|
||||
double parseDbl(dynamic val) {
|
||||
if (val == null) return 0.0;
|
||||
if (val is num) return val.toDouble();
|
||||
return double.tryParse(val.toString()) ?? 0.0;
|
||||
}
|
||||
|
||||
DateTime ts = DateTime.now();
|
||||
final tsStr = (json['timestamp'] ?? json['Timestamp'])?.toString();
|
||||
if (tsStr != null && tsStr.isNotEmpty) {
|
||||
ts = DateTime.tryParse(tsStr) ?? DateTime.now();
|
||||
}
|
||||
|
||||
return TradeHourlyUpdateModel(
|
||||
recommendation: (json['recommendation'] ?? json['Recommendation'])?.toString() ?? 'Hold',
|
||||
currentPrice: parseDbl(json['currentPrice'] ?? json['CurrentPrice']),
|
||||
suggestedStopLoss: json['suggestedStopLoss'] != null ? parseDbl(json['suggestedStopLoss'] ?? json['SuggestedStopLoss']) : null,
|
||||
suggestedTakeProfit: json['suggestedTakeProfit'] != null ? parseDbl(json['suggestedTakeProfit'] ?? json['SuggestedTakeProfit']) : null,
|
||||
vixValue: parseDbl(json['vixValue'] ?? json['VixValue']),
|
||||
reasoning: (json['reasoning'] ?? json['Reasoning'])?.toString() ?? '',
|
||||
timestamp: ts,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [recommendation, currentPrice, suggestedStopLoss, suggestedTakeProfit, reasoning, timestamp];
|
||||
}
|
||||
|
||||
class TradeModel extends Equatable {
|
||||
final String id;
|
||||
final String analysisId;
|
||||
@@ -27,6 +81,9 @@ class TradeModel extends Equatable {
|
||||
final double winRate;
|
||||
final String timeframe;
|
||||
final String instrumentType;
|
||||
final String assetType;
|
||||
final bool hasCfd;
|
||||
final List<String> derivativeProductCategories;
|
||||
final String derivativeIsin;
|
||||
final DateTime? createdAt;
|
||||
|
||||
@@ -41,6 +98,12 @@ class TradeModel extends Equatable {
|
||||
final double exitFee;
|
||||
final double quantity;
|
||||
|
||||
final String closeReason;
|
||||
final DateTime? userExitTimestamp;
|
||||
final bool hasPendingExitAlert;
|
||||
final String pendingExitReason;
|
||||
final List<TradeHourlyUpdateModel> hourlyUpdates;
|
||||
|
||||
const TradeModel({
|
||||
required this.id,
|
||||
this.analysisId = '',
|
||||
@@ -68,6 +131,9 @@ class TradeModel extends Equatable {
|
||||
this.winRate = 50.0,
|
||||
this.timeframe = '1D',
|
||||
this.instrumentType = 'Stock',
|
||||
this.assetType = 'stock',
|
||||
this.hasCfd = false,
|
||||
this.derivativeProductCategories = const [],
|
||||
this.derivativeIsin = '',
|
||||
this.createdAt,
|
||||
this.riskTolerance = 'Moderate',
|
||||
@@ -80,6 +146,11 @@ class TradeModel extends Equatable {
|
||||
this.entryFee = 0.0,
|
||||
this.exitFee = 0.0,
|
||||
this.quantity = 0.0,
|
||||
this.closeReason = '',
|
||||
this.userExitTimestamp,
|
||||
this.hasPendingExitAlert = false,
|
||||
this.pendingExitReason = '',
|
||||
this.hourlyUpdates = const [],
|
||||
});
|
||||
|
||||
bool get isActive => status.toLowerCase() == 'active';
|
||||
@@ -87,6 +158,15 @@ class TradeModel extends Equatable {
|
||||
bool get isRejected => status.toLowerCase() == 'rejected';
|
||||
bool get isProposed => (status.toLowerCase() == 'proposed' || isGlobalProposal) && !isRejected && !isActive && !isClosed;
|
||||
|
||||
DriftStatus get driftStatus {
|
||||
if (hasPendingExitAlert) return DriftStatus.exitAlert;
|
||||
if (hourlyUpdates.any((u) => u.recommendation.toLowerCase().contains('adjustsl') || u.recommendation.toLowerCase().contains('trailing'))) {
|
||||
return DriftStatus.trailingActive;
|
||||
}
|
||||
if (calculatedPnlPct < -3.5) return DriftStatus.driftWarning;
|
||||
return DriftStatus.onTrack;
|
||||
}
|
||||
|
||||
double get effectiveCurrentPrice {
|
||||
if (currentPrice > 0) return currentPrice;
|
||||
if (actualEntryPrice > 0) return actualEntryPrice;
|
||||
@@ -162,6 +242,18 @@ class TradeModel extends Equatable {
|
||||
dt = DateTime.tryParse(createdStr);
|
||||
}
|
||||
|
||||
DateTime? exitDt;
|
||||
final exitStr = (json['userExitTimestamp'] ?? json['UserExitTimestamp'])?.toString();
|
||||
if (exitStr != null && exitStr.isNotEmpty) {
|
||||
exitDt = DateTime.tryParse(exitStr);
|
||||
}
|
||||
|
||||
List<TradeHourlyUpdateModel> updates = [];
|
||||
final rawUpdates = json['hourlyUpdates'] ?? json['HourlyUpdates'];
|
||||
if (rawUpdates is List) {
|
||||
updates = rawUpdates.map((u) => TradeHourlyUpdateModel.fromJson(Map<String, dynamic>.from(u))).toList();
|
||||
}
|
||||
|
||||
return TradeModel(
|
||||
id: idVal,
|
||||
analysisId: (json['analysisId'] ?? json['AnalysisId'])?.toString() ?? '',
|
||||
@@ -189,6 +281,11 @@ class TradeModel extends Equatable {
|
||||
winRate: parseDbl(json['winRate'] ?? json['WinRate']),
|
||||
timeframe: (json['timeframe'] ?? json['Timeframe'])?.toString() ?? '1D',
|
||||
instrumentType: (json['instrumentType'] ?? json['InstrumentType'])?.toString() ?? 'Stock',
|
||||
assetType: (json['assetType'] ?? json['AssetType'])?.toString() ?? 'stock',
|
||||
hasCfd: json['hasCfd'] == true || json['HasCfd'] == true,
|
||||
derivativeProductCategories: (json['derivativeProductCategories'] ?? json['DerivativeProductCategories']) is List
|
||||
? ((json['derivativeProductCategories'] ?? json['DerivativeProductCategories']) as List).map((e) => e.toString()).toList()
|
||||
: const [],
|
||||
derivativeIsin: (json['derivativeIsin'] ?? json['DerivativeIsin'] ?? json['knockoutIsin'] ?? json['KnockoutIsin'])?.toString() ?? '',
|
||||
createdAt: dt,
|
||||
riskTolerance: (json['riskTolerance'] ?? json['RiskTolerance'])?.toString() ?? 'Moderate',
|
||||
@@ -203,6 +300,11 @@ class TradeModel extends Equatable {
|
||||
entryFee: parseDbl(json['entryFee'] ?? json['EntryFee']),
|
||||
exitFee: parseDbl(json['exitFee'] ?? json['ExitFee']),
|
||||
quantity: parseDbl(json['quantity'] ?? json['Quantity']),
|
||||
closeReason: (json['closeReason'] ?? json['CloseReason'])?.toString() ?? '',
|
||||
userExitTimestamp: exitDt,
|
||||
hasPendingExitAlert: json['hasPendingExitAlert'] == true || json['HasPendingExitAlert'] == true,
|
||||
pendingExitReason: (json['pendingExitReason'] ?? json['PendingExitReason'])?.toString() ?? '',
|
||||
hourlyUpdates: updates,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,6 +336,9 @@ class TradeModel extends Equatable {
|
||||
'winRate': winRate,
|
||||
'timeframe': timeframe,
|
||||
'instrumentType': instrumentType,
|
||||
'assetType': assetType,
|
||||
'hasCfd': hasCfd,
|
||||
'derivativeProductCategories': derivativeProductCategories,
|
||||
'derivativeIsin': derivativeIsin,
|
||||
'createdAt': createdAt?.toIso8601String(),
|
||||
'riskTolerance': riskTolerance,
|
||||
@@ -246,6 +351,10 @@ class TradeModel extends Equatable {
|
||||
'entryFee': entryFee,
|
||||
'exitFee': exitFee,
|
||||
'quantity': quantity,
|
||||
'closeReason': closeReason,
|
||||
'userExitTimestamp': userExitTimestamp?.toIso8601String(),
|
||||
'hasPendingExitAlert': hasPendingExitAlert,
|
||||
'pendingExitReason': pendingExitReason,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -263,5 +372,8 @@ class TradeModel extends Equatable {
|
||||
currentPrice,
|
||||
pnlAbsolute,
|
||||
pnlPercent,
|
||||
hasPendingExitAlert,
|
||||
hourlyUpdates,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user