feat(app): add evaluation history, bot control panel, simulation visualizer, and vendored signalr_core
This commit is contained in:
@@ -2,7 +2,6 @@ import 'package:finlytic_app/core/network/api_client.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/fundamental_data_model.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/technical_analysis_model.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/manual_analysis_request_dto.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/manual_analysis_response_dto.dart';
|
||||
import 'package:finlytic_app/features/trades/models/trade_model.dart';
|
||||
import 'package:finlytic_app/features/trades/models/trade_acceptance_dto.dart';
|
||||
import 'package:finlytic_app/features/trades/models/close_trade_request_dto.dart';
|
||||
@@ -102,23 +101,47 @@ class AssetRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<double?> getLivePrice(String isin) async {
|
||||
try {
|
||||
final res = await apiClient.get('/api/v1/assets/$isin/live');
|
||||
if (res.statusCode == 200 && res.data != null && res.data is Map<String, dynamic>) {
|
||||
final val = res.data['currentPrice'] ?? res.data['CurrentPrice'];
|
||||
if (val is num) return val.toDouble();
|
||||
if (val != null) return double.tryParse(val.toString());
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Future<List<TradeModel>> getAssetTrades(String isin, String? status) async {
|
||||
return _tradeRepository.fetchTrades(isin: isin, status: status);
|
||||
}
|
||||
|
||||
Future<ManualAnalysisResponseDto?> triggerManualAnalysis(String isin, {ManualAnalysisRequestDto? payload}) async {
|
||||
/// Triggers an on-demand manual analysis for [isin] via `POST /api/v1/analyze/manual`.
|
||||
///
|
||||
/// Server contract: always `200 OK` with a full `AssetEvaluationResultDto`
|
||||
/// body — even when the analysis ran but did not clear the bar for a trade
|
||||
/// proposal (`AssetEvaluationResultModel.proposal == null`), the response
|
||||
/// still carries the real, already-computed scores and AI reasoning, so
|
||||
/// there is no more silent `204 No Content` outcome to handle here
|
||||
/// (Rules.md §4). A non-2xx status (missing ISIN, engine unreachable, no
|
||||
/// RPC response, unexpected error) surfaces as a `DioException` that
|
||||
/// propagates to the caller instead of being swallowed into `null`.
|
||||
Future<AssetEvaluationResultModel> triggerManualAnalysis(String isin, {ManualAnalysisRequestDto? payload}) async {
|
||||
final body = payload != null ? payload.toJson() : {'isin': isin};
|
||||
final res = await apiClient.post('/api/v1/analyze/manual', data: body);
|
||||
if (res.statusCode == 200 && res.data != null && res.data is Map<String, dynamic>) {
|
||||
return ManualAnalysisResponseDto.fromJson(res.data);
|
||||
if (res.data != null && res.data is Map<String, dynamic>) {
|
||||
return AssetEvaluationResultModel.fromJson(res.data);
|
||||
}
|
||||
return null;
|
||||
throw StateError('Manual analysis endpoint returned an unexpected empty/non-object body.');
|
||||
}
|
||||
|
||||
Future<void> rejectTrade(String tradeId) async => _tradeRepository.rejectTrade(tradeId);
|
||||
|
||||
Future<void> acceptTrade(TradeAcceptanceDto tradeAcceptanceDto) async => _tradeRepository.acceptTrade(tradeAcceptanceDto);
|
||||
|
||||
Future<void> closeTrade(String tradeId, double exitPrice) async =>
|
||||
_tradeRepository.closeTrade(tradeId, dto: CloseTradeRequestDto(userExitPrice: exitPrice));
|
||||
|
||||
Future<TradeModel> addTradeFill(String tradeId, {required double executedPrice, required double quantity}) async =>
|
||||
_tradeRepository.addTradeFill(tradeId, executedPrice: executedPrice, quantity: quantity);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user