feat(app): add evaluation history, bot control panel, simulation visualizer, and vendored signalr_core
This commit is contained in:
@@ -1,180 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../trades/models/trade_model.dart';
|
||||
|
||||
class ExecutionPlanModel extends Equatable {
|
||||
final double stopLoss;
|
||||
final List<double> takeProfitTargets;
|
||||
final double riskRewardRatio;
|
||||
final double maxLeverage;
|
||||
|
||||
const ExecutionPlanModel({
|
||||
this.stopLoss = 0.0,
|
||||
this.takeProfitTargets = const [],
|
||||
this.riskRewardRatio = 0.0,
|
||||
this.maxLeverage = 1.0,
|
||||
});
|
||||
|
||||
factory ExecutionPlanModel.fromJson(Map<String, dynamic> json) {
|
||||
double parseDbl(dynamic v) => (v as num?)?.toDouble() ?? 0.0;
|
||||
return ExecutionPlanModel(
|
||||
stopLoss: parseDbl(json['stopLoss']),
|
||||
takeProfitTargets: (json['takeProfitTargets'] as List<dynamic>? ?? []).map((e) => parseDbl(e)).toList(),
|
||||
riskRewardRatio: parseDbl(json['riskRewardRatio']),
|
||||
maxLeverage: parseDbl(json['maxLeverage']) == 0 ? 1.0 : parseDbl(json['maxLeverage']),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [stopLoss, takeProfitTargets, riskRewardRatio, maxLeverage];
|
||||
}
|
||||
|
||||
class DetailedAnalysisModel extends Equatable {
|
||||
final String technicalRationale;
|
||||
final String fundamentalRationale;
|
||||
final String riskWarning;
|
||||
|
||||
const DetailedAnalysisModel({
|
||||
this.technicalRationale = '',
|
||||
this.fundamentalRationale = '',
|
||||
this.riskWarning = '',
|
||||
});
|
||||
|
||||
factory DetailedAnalysisModel.fromJson(Map<String, dynamic> json) {
|
||||
return DetailedAnalysisModel(
|
||||
technicalRationale: json['technicalRationale']?.toString() ?? '',
|
||||
fundamentalRationale: json['fundamentalRationale']?.toString() ?? '',
|
||||
riskWarning: json['riskWarning']?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [technicalRationale, fundamentalRationale, riskWarning];
|
||||
}
|
||||
|
||||
class N8nAnalysisResponseDto extends Equatable {
|
||||
final String aiDecision; // "Proceed", "Rejected", "Hold"
|
||||
final String aiReasoning;
|
||||
final int evalScore;
|
||||
final String suggestedDirection; // "Long", "Short"
|
||||
final String suggestedRisk;
|
||||
final String suggestedTimeframe;
|
||||
final ExecutionPlanModel? executionPlan;
|
||||
final DetailedAnalysisModel? detailedAnalysis;
|
||||
|
||||
const N8nAnalysisResponseDto({
|
||||
this.aiDecision = 'Rejected',
|
||||
this.aiReasoning = '',
|
||||
this.evalScore = 0,
|
||||
this.suggestedDirection = 'Long',
|
||||
this.suggestedRisk = 'Moderate',
|
||||
this.suggestedTimeframe = '1D',
|
||||
this.executionPlan,
|
||||
this.detailedAnalysis,
|
||||
});
|
||||
|
||||
factory N8nAnalysisResponseDto.fromJson(Map<String, dynamic> json) {
|
||||
ExecutionPlanModel? execPlan;
|
||||
if (json['executionPlan'] != null && json['executionPlan'] is Map<String, dynamic>) {
|
||||
execPlan = ExecutionPlanModel.fromJson(json['executionPlan']);
|
||||
}
|
||||
|
||||
DetailedAnalysisModel? detailAnalysis;
|
||||
if (json['detailedAnalysis'] != null && json['detailedAnalysis'] is Map<String, dynamic>) {
|
||||
detailAnalysis = DetailedAnalysisModel.fromJson(json['detailedAnalysis']);
|
||||
}
|
||||
|
||||
return N8nAnalysisResponseDto(
|
||||
aiDecision: json['aiDecision']?.toString() ?? 'Rejected',
|
||||
aiReasoning: json['aiReasoning']?.toString() ?? '',
|
||||
evalScore: (json['evalScore'] as num?)?.toInt() ?? 0,
|
||||
suggestedDirection: json['suggestedDirection']?.toString() ?? 'Long',
|
||||
suggestedRisk: json['suggestedRisk']?.toString() ?? 'Moderate',
|
||||
suggestedTimeframe: json['suggestedTimeframe']?.toString() ?? '1D',
|
||||
executionPlan: execPlan,
|
||||
detailedAnalysis: detailAnalysis,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
aiDecision,
|
||||
aiReasoning,
|
||||
evalScore,
|
||||
suggestedDirection,
|
||||
suggestedRisk,
|
||||
suggestedTimeframe,
|
||||
executionPlan,
|
||||
detailedAnalysis,
|
||||
];
|
||||
}
|
||||
|
||||
class ManualAnalysisResponseDto extends Equatable {
|
||||
final String analysisId;
|
||||
final bool isTradeProposed;
|
||||
final String status;
|
||||
final String recommendation; // "RECOMMENDED", "NOT_RECOMMENDED"
|
||||
final N8nAnalysisResponseDto? n8nResponse;
|
||||
final TradeModel? proposal;
|
||||
final String message;
|
||||
|
||||
const ManualAnalysisResponseDto({
|
||||
required this.analysisId,
|
||||
this.isTradeProposed = false,
|
||||
this.status = 'Success',
|
||||
this.recommendation = 'NOT_RECOMMENDED',
|
||||
this.n8nResponse,
|
||||
this.proposal,
|
||||
this.message = '',
|
||||
});
|
||||
|
||||
factory ManualAnalysisResponseDto.fromJson(Map<String, dynamic> json) {
|
||||
N8nAnalysisResponseDto? n8n;
|
||||
if (json['n8nResponse'] != null && json['n8nResponse'] is Map<String, dynamic>) {
|
||||
n8n = N8nAnalysisResponseDto.fromJson(json['n8nResponse']);
|
||||
}
|
||||
|
||||
TradeModel? prop;
|
||||
if (json['proposal'] != null && json['proposal'] is Map<String, dynamic>) {
|
||||
prop = TradeModel.fromJson(json['proposal']);
|
||||
} else if (n8n != null) {
|
||||
final exec = n8n.executionPlan;
|
||||
final det = n8n.detailedAnalysis;
|
||||
final isProceed = n8n.aiDecision.toLowerCase() == 'proceed';
|
||||
final analysisIdStr = (json['analysisId'] ?? json['AnalysisId'])?.toString() ?? '';
|
||||
final tradeIdStr = 'PROP-${analysisIdStr.length > 10 ? analysisIdStr.substring(0, 10).toUpperCase() : 'MANUAL'}';
|
||||
|
||||
prop = TradeModel(
|
||||
id: tradeIdStr,
|
||||
analysisId: analysisIdStr,
|
||||
symbol: (json['symbol'] ?? json['Symbol'])?.toString() ?? '',
|
||||
isin: (json['isin'] ?? json['Isin'])?.toString() ?? '',
|
||||
status: isProceed ? 'Proposed' : 'Rejected',
|
||||
signalType: n8n.suggestedDirection.toUpperCase() == 'SHORT' ? 'SELL' : 'BUY',
|
||||
entryPrice: 0.0,
|
||||
stopLoss: exec?.stopLoss ?? 0.0,
|
||||
takeProfit: (exec?.takeProfitTargets.isNotEmpty ?? false) ? exec!.takeProfitTargets.first : 0.0,
|
||||
reasoning: n8n.aiReasoning,
|
||||
technicalRationale: det?.technicalRationale ?? '',
|
||||
fundamentalRationale: det?.fundamentalRationale ?? '',
|
||||
riskWarning: det?.riskWarning ?? '',
|
||||
takeProfitTargets: exec?.takeProfitTargets ?? const [],
|
||||
maxLeverage: exec?.maxLeverage ?? 1.0,
|
||||
riskTolerance: n8n.suggestedRisk,
|
||||
timeframe: n8n.suggestedTimeframe,
|
||||
);
|
||||
}
|
||||
|
||||
return ManualAnalysisResponseDto(
|
||||
analysisId: (json['analysisId'] ?? json['AnalysisId'])?.toString() ?? '',
|
||||
isTradeProposed: json['isTradeProposed'] == true || json['IsTradeProposed'] == true,
|
||||
status: (json['status'] ?? json['Status'])?.toString() ?? 'Success',
|
||||
recommendation: (json['recommendation'] ?? json['Recommendation'])?.toString() ?? 'NOT_RECOMMENDED',
|
||||
n8nResponse: n8n,
|
||||
proposal: prop,
|
||||
message: (json['message'] ?? json['Message'])?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [analysisId, isTradeProposed, status, recommendation, n8nResponse, proposal, message];
|
||||
}
|
||||
Reference in New Issue
Block a user