import 'dart:async'; import 'package:finlytic_app/core/network/api_client.dart'; import '../models/bot_models.dart'; class BotRepository { final ApiClient apiClient; const BotRepository({required this.apiClient}); Future fetchStatus() async { final response = await apiClient.get('/api/v1/bot/status'); if (response.statusCode == 200 && response.data != null) { return BotStatusModel.fromJson(response.data); } throw Exception('Failed to fetch bot status'); } Future fetchSummary() async { final response = await apiClient.get('/api/v1/bot/portfolio/summary'); if (response.statusCode == 200 && response.data != null) { return AccountSummaryModel.fromJson(response.data); } throw Exception('Failed to fetch account summary'); } Future> fetchActivePositions() async { final response = await apiClient.get('/api/v1/bot/positions/active'); if (response.statusCode == 200 && response.data != null) { final List list = response.data; return list.map((json) => BotTradeOrderModel.fromJson(json)).toList(); } return []; } Future executeProposal(String proposalId, {String? venue, double? quantity}) async { final response = await apiClient.post('/api/v1/bot/orders/execute', data: { 'proposalId': proposalId, if (venue != null) 'preferredVenue': venue, if (quantity != null) 'customQuantity': quantity, }); if (response.statusCode == 200 && response.data != null) { return BotTradeOrderModel.fromJson(response.data); } throw Exception('Failed to execute bot proposal'); } Future panicCloseAll() async { final response = await apiClient.post('/api/v1/bot/orders/panic-close'); if (response.statusCode == 200 && response.data != null) { return PanicCloseResultModel.fromJson(response.data); } // A non-200 (e.g. 503 when FinlyticBot is unreachable) must NOT be swallowed into a fake "0 // closed / 0 skipped" result for an emergency action - the caller needs to know the attempt did // not even go through (Rules.md ยง4). throw Exception('Failed to trigger panic close'); } /// Updates FinlyticBot's dynamic settings. The backend now responds with the raw list of persisted /// `DynamicSettingDto` entries (see FinlyticBackend BotController.UpdateBotSettings), not a BotStatusModel, /// so the canonical status is re-fetched afterward instead of being reconstructed from that list. Future updateSettings({ bool? autoExecutionEnabled, int? maxPositions, double? riskPerTradePercent, int? minCompositeScore, }) async { final response = await apiClient.post('/api/v1/bot/settings/update', data: { if (autoExecutionEnabled != null) 'autoExecutionEnabled': autoExecutionEnabled, if (maxPositions != null) 'maxPositions': maxPositions, if (riskPerTradePercent != null) 'riskPerTradePercent': riskPerTradePercent, if (minCompositeScore != null) 'minCompositeScore': minCompositeScore, }); if (response.statusCode != 200) { throw Exception('Failed to update bot settings'); } return fetchStatus(); } }