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 String derivativeIsin; final DateTime? createdAt; final String riskTolerance; final double vixValue; final String vixRegime; final List 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.derivativeIsin = '', 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 actualExitPrice => currentPrice; double get calculatedPnlAbs { if (isClosed && pnlAbsolute != 0) return pnlAbsolute; final curr = currentPrice; if (curr <= 0) return pnlAbsolute; final entry = actualEntryPrice > 0 ? actualEntryPrice : entryPrice; 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 : (quantity > 0 ? quantity * entry : entry); final lev = leverageUsed > 0 ? leverageUsed : 1.0; final fees = entryFee + exitFee; return (rawMove * posSize * lev) - fees; } double get calculatedPnlPct { if (isClosed && pnlPercent != 0) return pnlPercent; final pnlAbs = calculatedPnlAbs; final posSize = positionSize > 0 ? positionSize : (actualEntryPrice > 0 ? actualEntryPrice : (entryPrice > 0 ? entryPrice : 1.0)); if (posSize <= 0) return 0.0; return (pnlAbs / posSize) * 100.0; } double calculateLivePnlAbs(double livePrice) { if (isClosed && pnlAbsolute != 0) return pnlAbsolute; final curr = livePrice > 0 ? livePrice : currentPrice; if (curr <= 0) return pnlAbsolute; final entry = actualEntryPrice > 0 ? actualEntryPrice : entryPrice; 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 : (quantity > 0 ? quantity * entry : entry); final lev = leverageUsed > 0 ? leverageUsed : 1.0; final fees = entryFee + exitFee; return (rawMove * posSize * lev) - fees; } double calculateLivePnlPct(double livePrice) { if (isClosed && pnlPercent != 0) return pnlPercent; final pnlAbs = calculateLivePnlAbs(livePrice); final posSize = positionSize > 0 ? positionSize : (actualEntryPrice > 0 ? actualEntryPrice : (entryPrice > 0 ? entryPrice : 1.0)); if (posSize <= 0) return 0.0; return (pnlAbs / posSize) * 100.0; } factory TradeModel.fromJson(Map 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', derivativeIsin: (json['derivativeIsin'] ?? json['DerivativeIsin'] ?? json['knockoutIsin'] ?? json['KnockoutIsin'])?.toString() ?? '', 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 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, 'derivativeIsin': derivativeIsin, '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 get props => [ id, analysisId, status, isGlobalProposal, userId, symbol, isin, signalType, entryPrice, currentPrice, pnlAbsolute, pnlPercent, ]; }