Créer un courtier personnalisé
Utilisez un courtier personnalisé pour connecter AQE à un service d’exécution. Votre adaptateur prend en charge le transport, les observations du compte et l’exécution des ordres. La stratégie conserve la maîtrise de ses insights ; le courtier reçoit des demandes d’ordres immuables plutôt que l’arbre complet des insights de la stratégie.
Ce guide suit les interfaces AQE 0.2.0. Alignez l’implémentation sur les traits de la révision AQE utilisée par votre stratégie. Commencez avec un transport simulé ou l’environnement de test du service, et non avec un compte de trading doté de fonds réels.
1. Implémenter la connexion et la gestion des ordres
Section intitulée « 1. Implémenter la connexion et la gestion des ordres »Créez un type Rust implémentant aq_engine::core::broker::traits::{Broker, OrderManagementProvider}. Il doit être Send + Sync et posséder sa connexion, ses caches et sa file d’événements.
| Trait | Méthodes requises | Responsabilité |
|---|---|---|
Broker | connect, disconnect, is_connected | Gérer le transport et libérer ses ressources, même après une connexion annulée. |
Broker | get_name, get_account_type | Identifier correctement l’adaptateur et l’environnement du compte. |
OrderManagementProvider | submit_order, cancel_order | Soumettre un BrokerOrderRequest et annuler un ordre par son identifiant chez le courtier. |
OrderManagementProvider | update_take_profit, update_stop_loss | Modifier une protection avec le prix et la quantité fournis. |
OrderManagementProvider | close_position, close_all_positions | Demander une clôture et gérer les protections restantes. |
OrderManagementProvider | get_orders, get_order | Fournir des enregistrements Order AQE avec les identifiants et les statuts effectifs du courtier. |
OrderManagementProvider | get_positions, get_position | Fournir des enregistrements Position ; la méthode de lecture d’une seule position reçoit un symbole. |
OrderManagementProvider | get_account | Fournir un Account avec soldes, devise, pouvoir d’achat et limites. |
OrderManagementProvider | drain_trade_events_bounded, has_pending_trade_events | Exposer une file d’événements ordonnée et non bloquante. |
OrderManagementProvider | subscribe_to_trade_stream, unsubscribe_from_trade_stream | Gérer les fonctions de rappel des transactions et leur cycle de vie. |
Plusieurs méthodes exigent explicitement des futures Send, notamment connect, les opérations individuelles de modification des ordres, get_orders, get_positions et get_account. Respectez les signatures exactes des traits ; une implémentation async fn convient si sa future est Send. Ne conservez jamais une garde non-Send pendant .await et n’effectuez pas d’opérations de transport bloquantes sur un thread de travail du moteur.
Sans événements poussés par le service, prévoyez comment l’interrogation périodique produit les événements et respecte le contrat d’abonnement obligatoire. N’annoncez pas TradeEventStream si le courtier ne fournit pas d’événements sans interrogation par AQE.
Déclarer les capacités natives
Section intitulée « Déclarer les capacités natives »Redéfinissez Broker::get_features() avec une tranche statique de valeurs BrokerFeature. Déclarez uniquement les capacités réellement prises en charge, telles que MarketOrder, LimitOrder, PartialClose ou BrokerManagedOco. Annoncez AmendLimitPrice et AmendStopPrice séparément lorsque le service accepte ces modifications du prix d’entrée sur un ordre non exécuté.
Restez cohérent sur le pouvoir d’achat : BuyingPowerLeveraged indique qu’il inclut déjà le levier. Reportez les plafonds disponibles dans Account.max_total_orders et Asset.max_total_size ; la taille maximale d’un ordre appartient à Asset.max_order_size. N’inventez pas de limites ni d’estimations faisant autorité à partir d’un simple calcul de notionnel.
Pour une opération non prise en charge, renvoyez BrokerError::UnsupportedOperation avant toute modification transmise au service. Ne substituez pas silencieusement un autre type d’ordre et n’annoncez pas de réussite.
2. Traduire les demandes sans perdre leur identité
Section intitulée « 2. Traduire les demandes sans perdre leur identité »submit_order reçoit un BrokerOrderRequest. Convertissez le symbole, le sens, la quantité, le type et la classe d’ordre, les prix limite et stop, les protections et le stop suiveur vers la représentation acceptée par le service.
Préservez client_order_id pour la corrélation et utilisez-le comme identifiant d’ordre client auprès du service lorsque celui-ci le permet. Il reste stable lors des nouvelles tentatives ou de la reprise d’une même intention d’entrée ; il ne prouve pas que le service a créé un ordre. Renvoyez l’identifiant effectif de l’ordre chez le courtier dans l’Order résultant.
Distinguez ces résultats :
| Résultat | Comportement de l’adaptateur |
|---|---|
| Opération non prise en charge avant l’envoi | Renvoyer UnsupportedOperation ; aucune modification n’a eu lieu. |
| Refus avéré avant l’appel de modification au courtier | Utiliser SubmissionRejected avec le motif. |
| Délai réseau dépassé après l’envoi | Préserver l’incertitude. Ne pas annoncer un rejet ni soumettre à nouveau sans vérification. |
| Demande acceptée | Renvoyer l’état de l’ordre observé chez le courtier ; l’acceptation seule ne confirme pas l’exécution de l’ordre. |
| Exécution, rejet, annulation ou clôture ultérieurs | Émettre les éléments attestant le cycle de vie avec l’ordre et l’événement correspondants. |
replace_order est facultative et renvoie par défaut une erreur d’opération non prise en charge. Redéfinissez-la pour remplacer le prix ou la quantité d’un ordre d’entrée en attente. Elle ne remplace pas update_take_profit ou update_stop_loss.
amend_entry_prices(order_id, limit_price, stop_price) est une méthode facultative distincte pour modifier le prix limite ou le seuil stop d’un ordre d’entrée en attente sans changer sa quantité. Son implémentation par défaut renvoie une erreur d’opération non prise en charge. Implémentez-la avant d’annoncer l’une de ces capacités de modification. Ne transmettez pas une modification du seuil d’entrée à update_stop_loss, qui gère les stops de protection.
Renseigner les enregistrements de compte, d’ordre et de position
Section intitulée « Renseigner les enregistrements de compte, d’ordre et de position »Les littéraux de structure Rust doivent inclure tous les champs du type actuel, y compris les champs facultatifs. #[serde(default)] facilite la lecture des anciens enregistrements sérialisés ; il ne rend pas un champ facultatif dans un constructeur Rust.
| Enregistrement et champs | Exigence pour l’adaptateur |
|---|---|
Account.account_id, currency, accrued_commission, max_total_orders | Limitez les observations au compte et à la devise concernés. Renseignez la commission accumulée à partir d’éléments attestés par le courtier et le plafond des ordres en attente lorsqu’il est communiqué. Les anciennes valeurs par défaut ne prouvent pas l’absence de coûts ou de limites. |
Position.order_id | Nouvel identifiant facultatif correspondant au ticket exact d’exécution ou de position associé à l’entrée. Utilisez Some(...) uniquement si l’adaptateur peut prouver l’association ; utilisez None pour les anciens enregistrements ou ceux qui agrègent plusieurs propriétaires par symbole. N’inventez jamais d’identité à partir du symbole. |
Position.entry_commission | Préservez la commission d’entrée de la position indépendamment du résultat de trading latent. Ne comptabilisez pas à nouveau les mêmes frais dans le bilan final. |
Order.filled_qty, realized_pnl, commission, swap | Utilisez les quantités d’entrée exécutées cumulées pour un instantané d’entrée, mais uniquement la quantité exécutée par la sortie concernée pour un événement de sortie. Préservez les valeurs d’exécution et les montants monétaires communiqués par le courtier ; l’acceptation ne constitue pas une exécution. |
Order.position_accounting, OrderLifecycleCloseDetails.position_accounting | Éléments comptables facultatifs de type PositionCloseAccounting pour une position dont le rattachement est prouvé. Conservez None s’ils ne sont pas pris en charge ou si le rattachement est ambigu ; ils ne sont pas requis pour la seule implémentation de la soumission d’ordres. |
BrokerLifecycleSnapshot.snapshot_complete, captured_at | N’indiquez que l’instantané est complet qu’après avoir recensé toute la vue pertinente du cycle de vie, et conservez son instant d’observation. Un instantané vide et incomplet ne peut pas prouver qu’un ordre ou une position a disparu. |
Lorsque vous construisez un Order, conservez ses identifiants d’insight et de stratégie ainsi que l’identifiant effectif de l’ordre chez le courtier. Renvoyez les métadonnées Asset actuelles avec les ordres et les positions, y compris les règles de négociation, le calendrier et les frais lorsqu’ils sont connus. Consultez les métadonnées du flux de données personnalisé pour les champs de l’instrument.
Rechercher les positions par l’identité exacte de leur entrée
Section intitulée « Rechercher les positions par l’identité exacte de leur entrée »get_position(symbol) peut fournir une vue agrégée. La méthode plus récente get_position_by_order_id(order_id, symbol) effectue une recherche exacte asynchrone et renvoie Result<Option<Position>, BrokerError>. Par défaut, elle examine get_positions() et rejette les identités d’ordre ambiguës ou manquantes plutôt que de sélectionner arbitrairement une position sur le même symbole. Redéfinissez-la si le service nécessite une recherche dédiée par ticket.
Pour une simulation exacte en mémoire ou un cache personnalisé, position_snapshot_reader peut renvoyer un PositionSnapshotReader : une fonction de rappel renvoyée par valeur, utilisable en toute sécurité depuis plusieurs threads et recevant l’identifiant de l’ordre et le symbole. N’y effectuez aucun accès au transport. Renvoyez Ok(None) uniquement pour une absence observée ; renvoyez une erreur lorsque les données sont indisponibles ou ne permettent pas d’établir l’identité demandée. Les décisions de la stratégie en direct utilisent le cache d’instantanés horodatés d’AQE, et non une requête réseau supplémentaire depuis une fonction de rappel d’insight.
3. Livrer les événements de manière fiable
Section intitulée « 3. Livrer les événements de manière fiable »Maintenez une file de (Order, TradeUpdateEvent) dans l’ordre de la source. drain_trade_events_bounded(max_events) retire au plus ce nombre d’événements et laisse les autres dans la file. Une limite de zéro ne retire rien. Ne videz jamais toute la file pour tronquer ensuite le résultat.
Cette fonction illustre l’opération sans imposer une bibliothèque de verrouillage. Appelez-la sous une garde de courte durée protégeant la file de votre adaptateur :
use aq_engine::core::broker::types::{Order, TradeUpdateEvent};use std::collections::VecDeque;
pub fn take_trade_events( queue: &mut VecDeque<(Order, TradeUpdateEvent)>, max_events: usize,) -> Vec<(Order, TradeUpdateEvent)> { let count = max_events.min(queue.len()); queue.drain(..count).collect()}has_pending_trade_events vérifie la présence d’événements dans la file sans accès réseau ni accès à une base de données. Appelez les fonctions de rappel en dehors des sections verrouillées et gardez-les courtes. Signalez précisément les exécutions partielles et les mises à jour des branches de protection ; normalisez les doublons et les messages reçus dans le désordre sans inventer d’états finaux.
Les composants d’encapsulation d’événements par défaut, indépendants du courtier, génèrent des identifiants sans horodatage source connu. Redéfinissez subscribe_to_broker_event_stream et la gestion des enveloppes associées lorsque l’adaptateur peut fournir des identifiants, des numéros de séquence et des horodatages fiables pour BrokerOrderEvent.
4. Gérer les protections et la reprise
Section intitulée « 4. Gérer les protections et la reprise »close_position prend en charge le nettoyage des protections : une clôture complète annule les branches natives restantes ; une clôture partielle conserve ou redimensionne les protections de l’exposition restante. Ok(true) accuse réception de la commande, sans confirmer la clôture. Émettez séparément les éléments attestant l’état final de la transaction.
Des méthodes optionnelles améliorent la supervision et la reprise :
| Méthode | Utilité |
|---|---|
get_cached_account | Fournir le dernier instantané du compte sans accès réseau. |
get_cached_positions | Fournir les positions en mémoire pour une exécution déterministe, sans remplacer les rafraîchissements horodatés en direct. |
get_cached_lifecycle_snapshot | Fournir une vue complète et de référence du cycle de vie sans E/S ; renvoyer None tant qu’elle n’existe pas. |
estimate_order_margin | Obtenir une estimation faisant autorité pour la demande exacte, sans placer l’ordre. |
reconcile_submission | Rechercher le compte et la commande client exacts sans soumettre à nouveau. |
reconcile_order_lifecycle | Résoudre les identifiants d’ordres à partir de l’historique des ordres et de l’identité des positions après un redémarrage. |
lifecycle_reconciler | Fournir un Arc<dyn OrderLifecycleReconciler> dont l’adaptateur détient la propriété pour permettre la reprise après un redémarrage. |
get_position_by_order_id, position_snapshot_reader | Retrouver le ticket exact de l’entrée, en séparant le rafraîchissement asynchrone de la recherche sans accès au transport. |
pending_cancellation_recovery, retry_cancellation | Permettre la reprise d’une annulation incertaine à partir de preuves, par l’intermédiaire du processus durable de traitement des commandes. |
execution_fill_history_reader | Fournir un historique borné des exécutions en lecture seule pour un compte, un ordre d’entrée et un symbole précis. |
position_valuation_reader | Fournir en lecture seule des valorisations dans la devise du compte pour des positions effectives précisément identifiées, et non une estimation globale par symbole. |
account_cash_flow_reader | Fournir en lecture seule les mouvements de financement et les ajustements du compte, avec l’identité de la source, la séquence et la couverture. |
configure_live_session, configure_asset_metadata | Recevoir le contexte de session et les règles d’actif si nécessaire. |
Le mécanisme de résolution de la soumission renvoie Unsupported par défaut. Le mécanisme de résolution du cycle de vie utilise par défaut get_orders() et signale les identifiants absents comme Unknown ; il ne permet pas d’établir qu’une position issue d’un ordre exécuté a été clôturée. N’interprétez jamais un ordre absent, un instantané incomplet ou un délai dépassé comme une preuve d’annulation ou de clôture.
Reprendre les annulations sans rejouer les ordres
Section intitulée « Reprendre les annulations sans rejouer les ordres »pending_cancellation_recovery(account, order) fournit en lecture seule des éléments attestant qu’une annulation incertaine cible toujours un ordre en attente. Par défaut, cette méthode renvoie None, ce qui ne prouve pas un état final. Seul le processus durable de traitement des commandes peut appeler retry_cancellation après avoir enregistré une tentative de reprise. Cette méthode ne doit ni soumettre, ni clôturer, ni remplacer un ordre ; elle doit obtenir des preuves d’annulation faisant autorité et les transmettre par le réducteur habituel des événements du courtier. Par défaut, elle n’est pas prise en charge. N’ajoutez pas de minuterie de nouvelle tentative indépendante qui contourne ce processus.
Garder les fonctions analytiques facultatives et en lecture seule
Section intitulée « Garder les fonctions analytiques facultatives et en lecture seule »Les méthodes fournissant les lecteurs d’historique, de valorisation et de flux de trésorerie renvoient None par défaut. Un adaptateur d’exécution peut conserver ces valeurs par défaut lorsqu’il ne peut pas fournir les éléments probants. Les données analytiques non prises en charge doivent rester indisponibles, et non devenir un historique vide fabriqué, un portefeuille sans position ou un flux de trésorerie nul.
ExecutionFillHistoryReader::read(account_id, entry_order_id, symbol)renvoie unExecutionFillHistorypour une position dont le rattachement à un seul ordre d’entrée est prouvé. Préservez les identifiants des exécutions et leurs horodatages source ;position_snapshotpeut ajouter un contrôle de quantité fondé sur une observation indépendante. Un historique manquant ou issu d’une compensation de positions ne prouve pas l’absence d’exécutions.PositionValuationReader::read_positions(account_id, symbol, position_ids)renvoie desPositionValuationspour un lot borné de positions précisément identifiées. Préservez la devise du compte,as_of_us, les horodatages des cotations, le résultat de trading, le portage et une base notionnelle explicite ou un motif d’indisponibilité.NotOpenest une observation d’absence, et non une exécution de clôture ou une autorisation de clôturer un insight.AccountCashFlowReader::read(account_id, source_id, after_sequence)renvoie uneAccountCashFlowPageavec l’identité de la source, la devise, la couverture temporelle, le curseur de séquence et les enregistrements. Enregistrez les mouvements de financement ou les ajustements réels ; ne déduisez pas de dépôts ou de retraits à partir des variations des capitaux propres, des exécutions ou de l’état des insights.
Les trois lecteurs renvoient un résultat facultatif à l’intérieur d’un Result. Une lecture indisponible et une erreur se distinguent d’une observation complète faisant autorité sans enregistrement correspondant. La lecture de données analytiques ne doit jamais avoir pour effet de bord de placer des ordres ou d’effectuer un rapprochement du cycle de vie.
5. Associer l’exécution et les données
Section intitulée « 5. Associer l’exécution et les données »UnifiedBroker accepte directement votre type d’exécution. Cette fonction associe tout courtier et flux conformes ; les deux adaptateurs doivent déjà implémenter leurs traits :
use aq_engine::core::broker::{ traits::{Broker, DataFeed, DataProvider, OrderManagementProvider}, UnifiedBroker,};
pub fn live_with<E, D>(execution: E, datafeed: D) -> UnifiedBroker<E, D>where E: Broker + OrderManagementProvider, D: DataFeed + DataProvider,{ UnifiedBroker::new(execution, datafeed)}Utilisez PaperBroker avec UnifiedBroker::new_backtest pour le simulateur historique intégré. Les traits de courtier en direct ne transforment pas un service distant en simulateur. L’ajout au sélecteur de connexions d’AQS nécessite une intégration distincte dans l’application.
6. Valider avant utilisation en direct
Section intitulée « 6. Valider avant utilisation en direct »- Testez les échecs de connexion, l’annulation, la reconnexion et l’arrêt complet.
- Testez chaque type d’ordre, règle de précision et limite annoncés.
- Vérifiez les exécutions partielles, annulations, protections, clôtures partielles et nettoyage après clôture complète.
- Testez les retraits bornés avec zéro, un et plusieurs événements ; vérifiez l’ordre et la conservation des éléments restants.
- Simulez un délai dépassé après l’acceptation par le service. Vérifiez que la reprise ne duplique pas l’ordre.
- Redémarrez avec des ordres en attente et des positions ouvertes ; distinguez les résultats inconnus et confirmés.
- Comparez les observations du compte, des ordres et des positions aux relevés de l’environnement de test du service avant d’envisager une utilisation en direct.
- Gardez distinctes deux entrées sur le même symbole. Testez la recherche par ticket exact, les identités inconnues et les instantanés incomplets plutôt que de sélectionner la première correspondance de symbole.
- Simulez une annulation incertaine avec un redémarrage et vérifiez que seul le chemin de reprise durable peut la retenter.
- Testez les lecteurs facultatifs avec des données non prises en charge, incomplètes, périmées ou associées à un autre compte. Vérifiez que les lectures analytiques ne peuvent ni soumettre d’ordres ni fabriquer d’éléments comptables probants.
Références du code source
Section intitulée « Références du code source »Dans votre copie d’AQE, commencez par :
src/core/broker/mod.rs: traits d’exécution, valeurs par défaut et composition.src/core/broker/commands.rs:BrokerOrderRequestetSubmissionResolution.src/core/broker/types/mod.rs: types de compte, ordre, position, cycle de vie et erreur.src/core/broker/features.rs: capacités natives.src/core/broker/fill_history.rs,position_valuation.rsetaccount_cash_flow.rs: contrats analytiques facultatifs en lecture seule.live-sync-protocol/src/position_accounting.rsetposition_valuation.rs: éléments comptables probants et validation des valorisations.src/core/broker/mt5_broker.rsetsrc/core/broker/paper_broker.rs: implémentations en direct et simulée.
Suite : Créer un flux de données personnalisé ou Exécution en direct.
Nous utilisons des cookies et un stockage essentiels pour la connexion, la sécurité du compte, les préférences de thème, cet avis et les mesures internes PostHog nécessaires au suivi de l’utilisation, de la qualité des sessions et de la fiabilité. Nous n’utilisons pas de cookies publicitaires.
