Files
Finlytic/FinlyticApp/lib/features/trades/models/trade_model.dart
T

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,
];
}