238 lines
8.7 KiB
Dart
238 lines
8.7 KiB
Dart
import 'package:equatable/equatable.dart';
|
|
|
|
class TradeModel extends Equatable {
|
|
final String id;
|
|
final String analysisId;
|
|
final String status;
|
|
final bool isGlobalProposal;
|
|
final String userId;
|
|
final String symbol;
|
|
final String isin;
|
|
final String companyName;
|
|
final String sector;
|
|
final String signalType;
|
|
final double entryPrice;
|
|
final double actualEntryPrice;
|
|
final double currentPrice;
|
|
final double stopLoss;
|
|
final double takeProfit;
|
|
final double positionSize;
|
|
final double leverageUsed;
|
|
final double pnlAbsolute;
|
|
final double pnlPercent;
|
|
final String reasoning;
|
|
final String technicalRationale;
|
|
final String fundamentalRationale;
|
|
final String riskWarning;
|
|
final double winRate;
|
|
final String timeframe;
|
|
final String instrumentType;
|
|
final DateTime? createdAt;
|
|
|
|
final String riskTolerance;
|
|
final double vixValue;
|
|
final String vixRegime;
|
|
final List<double> takeProfitTargets;
|
|
final double maxLeverage;
|
|
final double entryZoneMin;
|
|
final double entryZoneMax;
|
|
final double entryFee;
|
|
final double exitFee;
|
|
final double quantity;
|
|
|
|
const TradeModel({
|
|
required this.id,
|
|
this.analysisId = '',
|
|
this.status = 'Active',
|
|
this.isGlobalProposal = false,
|
|
this.userId = '',
|
|
required this.symbol,
|
|
this.isin = '',
|
|
this.companyName = '',
|
|
this.sector = '',
|
|
required this.signalType,
|
|
required this.entryPrice,
|
|
this.actualEntryPrice = 0.0,
|
|
this.currentPrice = 0.0,
|
|
required this.stopLoss,
|
|
required this.takeProfit,
|
|
this.positionSize = 0.0,
|
|
this.leverageUsed = 1.0,
|
|
this.pnlAbsolute = 0.0,
|
|
this.pnlPercent = 0.0,
|
|
this.reasoning = '',
|
|
this.technicalRationale = '',
|
|
this.fundamentalRationale = '',
|
|
this.riskWarning = '',
|
|
this.winRate = 50.0,
|
|
this.timeframe = '1D',
|
|
this.instrumentType = 'Stock',
|
|
this.createdAt,
|
|
this.riskTolerance = 'Moderate',
|
|
this.vixValue = 0.0,
|
|
this.vixRegime = 'Normal',
|
|
this.takeProfitTargets = const [],
|
|
this.maxLeverage = 1.0,
|
|
this.entryZoneMin = 0.0,
|
|
this.entryZoneMax = 0.0,
|
|
this.entryFee = 0.0,
|
|
this.exitFee = 0.0,
|
|
this.quantity = 0.0,
|
|
});
|
|
|
|
bool get isActive => status.toLowerCase() == 'active';
|
|
bool get isClosed => status.toLowerCase() == 'closed';
|
|
bool get isRejected => status.toLowerCase() == 'rejected';
|
|
bool get isProposed => (status.toLowerCase() == 'proposed' || isGlobalProposal) && !isRejected && !isActive && !isClosed;
|
|
|
|
double get effectiveCurrentPrice {
|
|
if (currentPrice > 0) return currentPrice;
|
|
if (actualEntryPrice > 0) return actualEntryPrice;
|
|
return entryPrice;
|
|
}
|
|
|
|
double get calculatedPnlAbs {
|
|
if (pnlAbsolute != 0) return pnlAbsolute;
|
|
final entry = actualEntryPrice > 0 ? actualEntryPrice : entryPrice;
|
|
final curr = effectiveCurrentPrice;
|
|
if (entry <= 0) return 0.0;
|
|
final isShort = signalType == 'SELL' || signalType == 'SHORT';
|
|
final rawMove = isShort ? ((entry - curr) / entry) : ((curr - entry) / entry);
|
|
final posSize = positionSize > 0 ? positionSize : entry;
|
|
final lev = leverageUsed > 0 ? leverageUsed : 1.0;
|
|
return (rawMove * posSize * lev);
|
|
}
|
|
|
|
double get calculatedPnlPct {
|
|
if (pnlPercent != 0) return pnlPercent;
|
|
final pnlAbs = calculatedPnlAbs;
|
|
final posSize = positionSize > 0 ? positionSize : (actualEntryPrice > 0 ? actualEntryPrice : entryPrice);
|
|
if (posSize <= 0) return 0.0;
|
|
return (pnlAbs / posSize) * 100.0;
|
|
}
|
|
|
|
factory TradeModel.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;
|
|
}
|
|
|
|
final idVal = (json['tradeId'] ?? json['TradeId'] ?? json['id'] ?? json['Id'])?.toString() ?? '';
|
|
final sig = (json['signalType'] ?? json['SignalType'] ?? json['side'] ?? json['Side'])?.toString() ?? 'BUY';
|
|
final entry = parseDbl(json['entryPrice'] ?? json['EntryPrice']);
|
|
final actualEntry = parseDbl(json['actualEntryPrice'] ?? json['ActualEntryPrice']);
|
|
final currPrice = parseDbl(json['currentPrice'] ?? json['CurrentPrice'] ?? json['price'] ?? json['Price']);
|
|
final sl = parseDbl(json['stopLoss'] ?? json['StopLoss']);
|
|
final tp = parseDbl(json['takeProfit'] ?? json['TakeProfit']);
|
|
final pnlAbs = parseDbl(json['pnlAbsolute'] ?? json['PnlAbsolute'] ?? json['pnl'] ?? json['Pnl']);
|
|
final pnlPct = parseDbl(json['pnlPercent'] ?? json['PnlPercent']);
|
|
|
|
DateTime? dt;
|
|
final createdStr = (json['createdAt'] ?? json['CreatedAt'])?.toString();
|
|
if (createdStr != null && createdStr.isNotEmpty) {
|
|
dt = DateTime.tryParse(createdStr);
|
|
}
|
|
|
|
return TradeModel(
|
|
id: idVal,
|
|
analysisId: (json['analysisId'] ?? json['AnalysisId'])?.toString() ?? '',
|
|
status: (json['status'] ?? json['Status'])?.toString() ?? 'Active',
|
|
isGlobalProposal: json['isGlobalProposal'] == true || json['IsGlobalProposal'] == true,
|
|
userId: (json['userId'] ?? json['UserId'])?.toString() ?? '',
|
|
symbol: (json['symbol'] ?? json['Symbol'])?.toString() ?? 'UNKNOWN',
|
|
isin: (json['isin'] ?? json['Isin'])?.toString() ?? '',
|
|
companyName: (json['companyName'] ?? json['CompanyName'])?.toString() ?? '',
|
|
sector: (json['sector'] ?? json['Sector'])?.toString() ?? '',
|
|
signalType: sig.toUpperCase(),
|
|
entryPrice: entry,
|
|
actualEntryPrice: actualEntry,
|
|
currentPrice: currPrice,
|
|
stopLoss: sl,
|
|
takeProfit: tp,
|
|
positionSize: parseDbl(json['positionSize'] ?? json['PositionSize']),
|
|
leverageUsed: parseDbl(json['leverageUsed'] ?? json['LeverageUsed']) == 0 ? 1.0 : parseDbl(json['leverageUsed'] ?? json['LeverageUsed']),
|
|
pnlAbsolute: pnlAbs,
|
|
pnlPercent: pnlPct,
|
|
reasoning: (json['reasoning'] ?? json['Reasoning'])?.toString() ?? '',
|
|
technicalRationale: (json['technicalRationale'] ?? json['TechnicalRationale'])?.toString() ?? '',
|
|
fundamentalRationale: (json['fundamentalRationale'] ?? json['FundamentalRationale'])?.toString() ?? '',
|
|
riskWarning: (json['riskWarning'] ?? json['RiskWarning'])?.toString() ?? '',
|
|
winRate: parseDbl(json['winRate'] ?? json['WinRate']),
|
|
timeframe: (json['timeframe'] ?? json['Timeframe'])?.toString() ?? '1D',
|
|
instrumentType: (json['instrumentType'] ?? json['InstrumentType'])?.toString() ?? 'Stock',
|
|
createdAt: dt,
|
|
riskTolerance: (json['riskTolerance'] ?? json['RiskTolerance'])?.toString() ?? 'Moderate',
|
|
vixValue: parseDbl(json['vixValue'] ?? json['VixValue']),
|
|
vixRegime: (json['vixRegime'] ?? json['VixRegime'])?.toString() ?? 'Normal',
|
|
takeProfitTargets: (json['takeProfitTargets'] ?? json['TakeProfitTargets']) is List
|
|
? ((json['takeProfitTargets'] ?? json['TakeProfitTargets']) as List).map((e) => parseDbl(e)).toList()
|
|
: [],
|
|
maxLeverage: parseDbl(json['maxLeverage'] ?? json['MaxLeverage']),
|
|
entryZoneMin: parseDbl(json['entryZoneMin'] ?? json['EntryZoneMin']),
|
|
entryZoneMax: parseDbl(json['entryZoneMax'] ?? json['EntryZoneMax']),
|
|
entryFee: parseDbl(json['entryFee'] ?? json['EntryFee']),
|
|
exitFee: parseDbl(json['exitFee'] ?? json['ExitFee']),
|
|
quantity: parseDbl(json['quantity'] ?? json['Quantity']),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'tradeId': id,
|
|
'analysisId': analysisId,
|
|
'status': status,
|
|
'isGlobalProposal': isGlobalProposal,
|
|
'userId': userId,
|
|
'symbol': symbol,
|
|
'isin': isin,
|
|
'companyName': companyName,
|
|
'sector': sector,
|
|
'signalType': signalType,
|
|
'entryPrice': entryPrice,
|
|
'actualEntryPrice': actualEntryPrice,
|
|
'currentPrice': currentPrice,
|
|
'stopLoss': stopLoss,
|
|
'takeProfit': takeProfit,
|
|
'positionSize': positionSize,
|
|
'leverageUsed': leverageUsed,
|
|
'pnlAbsolute': pnlAbsolute,
|
|
'pnlPercent': pnlPercent,
|
|
'reasoning': reasoning,
|
|
'technicalRationale': technicalRationale,
|
|
'fundamentalRationale': fundamentalRationale,
|
|
'riskWarning': riskWarning,
|
|
'winRate': winRate,
|
|
'timeframe': timeframe,
|
|
'instrumentType': instrumentType,
|
|
'createdAt': createdAt?.toIso8601String(),
|
|
'riskTolerance': riskTolerance,
|
|
'vixValue': vixValue,
|
|
'vixRegime': vixRegime,
|
|
'takeProfitTargets': takeProfitTargets,
|
|
'maxLeverage': maxLeverage,
|
|
'entryZoneMin': entryZoneMin,
|
|
'entryZoneMax': entryZoneMax,
|
|
'entryFee': entryFee,
|
|
'exitFee': exitFee,
|
|
'quantity': quantity,
|
|
};
|
|
}
|
|
|
|
@override
|
|
List<Object?> get props => [
|
|
id,
|
|
analysisId,
|
|
status,
|
|
isGlobalProposal,
|
|
userId,
|
|
symbol,
|
|
isin,
|
|
signalType,
|
|
entryPrice,
|
|
currentPrice,
|
|
pnlAbsolute,
|
|
pnlPercent,
|
|
];
|
|
}
|