feat(App): update Finlytic Flutter app UI and blocs
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
class TradeAcceptanceDto {
|
||||
final String userId;
|
||||
final String tradeId;
|
||||
final String analysisId;
|
||||
final String isin;
|
||||
final String symbol;
|
||||
final double? actualEntryPrice;
|
||||
final double? positionSize;
|
||||
final double? leverageUsed;
|
||||
final double? entryFee;
|
||||
final double? exitFee;
|
||||
final double? quantity;
|
||||
final double? knockoutThreshold;
|
||||
final bool isRecurring;
|
||||
final DateTime executionTimestamp;
|
||||
|
||||
final String? signalType;
|
||||
final double? entryPrice;
|
||||
final double? stopLoss;
|
||||
final double? takeProfit;
|
||||
final String? instrumentType;
|
||||
final String? timeframe;
|
||||
final String? reasoning;
|
||||
|
||||
TradeAcceptanceDto({
|
||||
required this.userId,
|
||||
required this.tradeId,
|
||||
this.analysisId = '',
|
||||
required this.isin,
|
||||
required this.symbol,
|
||||
this.actualEntryPrice,
|
||||
this.positionSize,
|
||||
this.leverageUsed,
|
||||
this.entryFee,
|
||||
this.exitFee,
|
||||
this.quantity,
|
||||
this.knockoutThreshold,
|
||||
this.isRecurring = false,
|
||||
required this.executionTimestamp,
|
||||
this.signalType,
|
||||
this.entryPrice,
|
||||
this.stopLoss,
|
||||
this.takeProfit,
|
||||
this.instrumentType,
|
||||
this.timeframe,
|
||||
this.reasoning,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'userId': userId.isNotEmpty ? userId : 'default_user',
|
||||
'tradeId': tradeId,
|
||||
'analysisId': analysisId,
|
||||
'isin': isin,
|
||||
'symbol': symbol,
|
||||
'actualEntryPrice': actualEntryPrice,
|
||||
'positionSize': positionSize,
|
||||
'leverageUsed': leverageUsed,
|
||||
'entryFee': entryFee,
|
||||
'exitFee': exitFee,
|
||||
'quantity': quantity,
|
||||
'knockoutThreshold': knockoutThreshold,
|
||||
'isRecurring': isRecurring,
|
||||
'executionTimestamp': executionTimestamp.toIso8601String(),
|
||||
'signalType': signalType,
|
||||
'entryPrice': entryPrice,
|
||||
'stopLoss': stopLoss,
|
||||
'takeProfit': takeProfit,
|
||||
'instrumentType': instrumentType,
|
||||
'timeframe': timeframe,
|
||||
'reasoning': reasoning,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
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,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user