feat(app): add evaluation history, bot control panel, simulation visualizer, and vendored signalr_core
This commit is contained in:
@@ -30,6 +30,35 @@ class TradeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches system-wide, currently active trade proposals
|
||||
/// (`GET /api/v1/user/trades?status=Proposed`).
|
||||
///
|
||||
/// Unlike [fetchTrades], the server does **not** return `ActiveTradeDto`
|
||||
/// (`TradeModel`) for this query: `UserTradesController.GetUserTrades`
|
||||
/// branches on `status == "Proposed"` and instead calls `engine_GetProposals`,
|
||||
/// which returns `List<TradeProposalDto>` — a structurally different shape
|
||||
/// (`proposalId` instead of `id`, no `userId`/PnL fields, since proposals are
|
||||
/// system-wide opportunities not owned by any user). Parsing that response
|
||||
/// as `TradeModel` would silently produce garbage/empty fields, so this is a
|
||||
/// dedicated method that parses `TradeProposalModel` instead of overloading
|
||||
/// [fetchTrades] for two incompatible server-side contracts.
|
||||
Future<List<TradeProposalModel>> fetchProposals() async {
|
||||
try {
|
||||
final response = await apiClient.get('/api/v1/user/trades', queryParameters: {
|
||||
'status': 'Proposed',
|
||||
'_t': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final List<dynamic> data = response.data;
|
||||
return data.map((json) => TradeProposalModel.fromJson(Map<String, dynamic>.from(json))).toList();
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
throw Exception('Vorschläge konnten nicht geladen werden: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<DerivativeItemModel>> fetchDerivatives(
|
||||
String isin, {
|
||||
String optionType = 'long',
|
||||
@@ -73,12 +102,13 @@ class TradeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> rejectTrade(String tradeId) async {
|
||||
final response = await apiClient.post('/api/v1/user/trades/$tradeId/reject');
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Trade konnte nicht abgelehnt werden');
|
||||
}
|
||||
}
|
||||
// NOTE: there is intentionally no `rejectTrade` here anymore. A trade
|
||||
// proposal is a system-wide opportunity that many users may accept
|
||||
// independently; "rejecting" it server-side would have no meaning and
|
||||
// the corresponding endpoint (`POST /api/v1/user/trades/{id}/reject`) has
|
||||
// been removed. Dismissing a proposal is now a purely local UI action
|
||||
// (see `AssetTradesBloc`'s `DismissTradeEvent`) — the proposal keeps
|
||||
// existing server-side until it naturally expires (24h TTL).
|
||||
|
||||
Future<void> closeTrade(String id, {CloseTradeRequestDto? dto}) async {
|
||||
final response = await apiClient.post('/api/v1/user/trades/$id/close', data: dto?.toJson());
|
||||
@@ -86,5 +116,34 @@ class TradeRepository {
|
||||
throw Exception('Trade konnte nicht geschlossen werden');
|
||||
}
|
||||
}
|
||||
|
||||
/// Records an additional/corrective fill against an already-active trade
|
||||
/// (`EngineController.AddTradeFill` -> `engine_AddFill`). This is the
|
||||
/// correct server-side counterpart for the "review/edit execution
|
||||
/// numbers" path on an active trade — unlike `acceptTrade`, which targets
|
||||
/// a proposal, not an existing trade. `userId`/`tradeId` are always
|
||||
/// overwritten server-side from the JWT claim/route, never trusted from
|
||||
/// this payload.
|
||||
Future<TradeModel> addTradeFill(
|
||||
String tradeId, {
|
||||
required double executedPrice,
|
||||
required double quantity,
|
||||
double fee = 0,
|
||||
String? note,
|
||||
}) async {
|
||||
final response = await apiClient.post(
|
||||
'/api/v1/engine/trades/$tradeId/fills',
|
||||
data: {
|
||||
'executedPrice': executedPrice,
|
||||
'quantity': quantity,
|
||||
'fee': fee,
|
||||
if (note != null) 'note': note,
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
return TradeModel.fromJson(response.data);
|
||||
}
|
||||
throw Exception('Ausführung konnte nicht gespeichert werden');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user