Ce que nous construisons
À la fin de ce tutoriel vous aurez une application VCL Delphi fonctionnelle qui streame en direct les données de trade et de carnet d'ordres depuis Binance, exécute une stratégie de breakout simple, passe de vrais ordres via l'API REST, et applique des contrôles de risque (taille de position max, limite de perte quotidienne, kill switch). La même plomberie fonctionne pour tout exchange supporté par sgcWebSockets — Coinbase, Kraken, OKX, Bybit, Bitfinex — avec seulement le mapping des identifiants et des symboles qui change.
C'est la même architecture utilisée par notre exemple de référence sgcTrader. Si vous voulez un plus gros point de départ avec UI complète, graphiques et routage multi-exchanges, prenez-le. La marche à suivre ci-dessous montre ce qui se passe sous le capot en environ 300 lignes de code.
Deux prérequis avant de commencer. Premièrement, obtenez une clé API Binance (Compte > Gestion API). Pour le développement, générez une clé qui a uniquement « Activer la lecture » et « Activer le trading spot », et mettez votre IP en liste blanche. Ne mettez jamais une clé avec retrait activé dans le code. Deuxièmement, faites tout sur le testnet de Binance d'abord (testnet.binance.vision). Les endpoints, formats de message et algorithme de signature sont identiques à la production, mais les fonds sont fictifs. Nous avons perdu de l'argent réel à « je suis sûr que ma stratégie est correcte » exactement le nombre de fois où nous n'avons pas testé sur le testnet d'abord.
Architecture en un schéma
Trois threads, deux composants, un gardien des risques :
| Composant | Rôle | Thread |
TsgcWSAPI_Binance |
Flux WebSocket : trades, depth, klines, user data | Worker E/S |
TsgcHTTP_API_Binance |
REST : passation d'ordre, annulation, snapshot du compte | Worker trader |
| File de stratégie | Découplage : événements de marché → décisions → ordres | Worker stratégie |
| Gate de risque | Bloquer / réduire / autoriser chaque ordre | Inline dans le trader |
Étape 1 : streamer les données de marché
Déposez un TsgcWSAPI_Binance sur la fiche. Il parle déjà le protocole combined stream Binance — il vous suffit de vous abonner aux canaux que vous voulez.
uses
sgcWSAPI_Binance;
procedure TForm1.FormCreate(Sender: TObject);
begin
oBinance := TsgcWSAPI_Binance.Create(Self);
oBinance.WatchDog.Enabled := True;
oBinance.WatchDog.Interval := 5;
oBinance.HeartBeat.Enabled := True;
oBinance.OnBinanceMessage := DoStream;
oBinance.OnDisconnect := DoDisconnect;
// Subscribe to 1-minute klines and aggregated trades for BTCUSDT
oBinance.Streams.Add('btcusdt@kline_1m');
oBinance.Streams.Add('btcusdt@aggTrade');
oBinance.Active := True;
end;
procedure TForm1.DoStream(Sender: TObject; const aStream, aData: string);
begin
// Fire-and-forget: push to the strategy queue
oStrategyQueue.Push(TMarketEvent.Create(aStream, aData));
end;
C'est toute la couche d'ingestion des données de marché. Reconnexion sur coupure, heartbeat pour détecter les liens morts, et un push non-bloquant vers la file de stratégie.
Une chose que le composant fait que vous devriez sinon écrire vous-même : l'URL du combined stream Binance est /stream?streams=name1/name2/name3, et si vous voulez ajouter ou retirer des streams sans couper la connexion vous devez envoyer un message subscribe/unsubscribe JSON-RPC sur la même socket. TsgcWSAPI_Binance expose les méthodes SubscribeStream et UnsubscribeStream qui gèrent le handshake JSON-RPC pour vous. Utile quand l'utilisateur choisit un nouveau ticker dans l'UI — pas de reconnexion, pas de messages perdus.
Aussi : Binance impose des limites par IP et par stream. Pour la profondeur complète sur chaque paire USDT vous atteindrez rapidement la limite de débit de messages. Abonnez-vous uniquement à ce dont vous avez réellement besoin, et préférez les streams agrégés (!miniTicker@arr) aux streams par symbole quand vous avez besoin d'une vue de marché large.
Étape 2 : une stratégie minimale
La stratégie tourne sur son propre thread. Elle maintient une fenêtre glissante des N derniers closes du stream kline 1 minute et passe long quand le prix casse au-dessus d'un plus haut sur 20 périodes. Pure illustration — ne mettez pas ceci devant de l'argent réel s'il vous plaît.
procedure TStrategyThread.Execute;
var
oEvent : TMarketEvent;
oJSON : TsgcJSONObject;
vClose : Double;
vHigh : Double;
begin
while not Terminated do
begin
if not oStrategyQueue.Pop(oEvent, 100) then Continue;
try
oJSON := TsgcJSONObject.Parse(oEvent.Data);
try
if oEvent.Stream.EndsWith('@kline_1m') then
begin
vClose := oJSON.O['k'].F['c'];
FCloses.Append(vClose);
if FCloses.Count >= 21 then
begin
vHigh := FCloses.Max(20); // prior 20-bar high
if (FPosition = 0) and (vClose > vHigh) then
oOrderQueue.Push(TIntent.New(siBuy, 'BTCUSDT', 0.001))
else if (FPosition > 0) and (vClose < FCloses.MA(20)) then
oOrderQueue.Push(TIntent.New(siSell, 'BTCUSDT', FPosition));
end;
end;
finally
oJSON.Free;
end;
finally
oEvent.Free;
end;
end;
end;
Étape 3 : gate de risque
Ne laissez jamais une stratégie parler directement à l'exchange. Faites passer chaque intention par un gate qui connaît vos limites.
function TRiskGate.Validate(const aIntent: TIntent;
out aReason: string): Boolean;
begin
Result := False;
if FKillSwitch then
Exit(False);
if Abs(FDailyPnL) > FConfig.MaxDailyLoss then
begin
aReason := 'Daily loss limit hit';
Exit;
end;
if (aIntent.Side = siBuy)
and (FPositionUSD + aIntent.Qty * FLastPrice > FConfig.MaxPositionUSD) then
begin
aReason := 'Max position size';
Exit;
end;
Result := True;
end;
Étape 4 : passer l'ordre via REST
Le worker d'ordres tire les intentions validées, signe la requête et poste à Binance.
procedure TTraderThread.Execute;
var
oIntent : TIntent;
vReason : string;
oResponse: TsgcBinanceClass_Response_NewOrder;
begin
while not Terminated do
begin
if not oOrderQueue.Pop(oIntent, 100) then Continue;
try
if not FRisk.Validate(oIntent, vReason) then
begin
Log(Format('REJECT %s %s qty=%.6f reason=%s',
[SideName(oIntent.Side), oIntent.Symbol, oIntent.Qty, vReason]));
Continue;
end;
oResponse := oHttp.NewOrder(
oIntent.Symbol,
IfThen(oIntent.Side = siBuy, 'BUY', 'SELL'),
'MARKET',
oIntent.Qty,
0 // price ignored for MARKET
);
try
Log(Format('FILL %s qty=%.6f price=%.2f id=%d',
[oIntent.Symbol, oResponse.ExecutedQty,
oResponse.AvgPrice, oResponse.OrderId]));
FRisk.OnFill(oIntent.Side, oResponse.ExecutedQty, oResponse.AvgPrice);
finally
oResponse.Free;
end;
finally
oIntent.Free;
end;
end;
end;
Étape 4b : authentification et signature REST
Le composant TsgcHTTP_API_Binance signe les requêtes pour vous en utilisant le secret API. En coulisses, il construit la chaîne de requête canonique, calcule un HMAC-SHA256 avec votre secret, et l'ajoute comme paramètre signature. Vous fournissez la clé et le secret une fois au démarrage.
oHttp := TsgcHTTP_API_Binance.Create(Self);
oHttp.BinanceOptions.ApiKey := FConfig.ApiKey;
oHttp.BinanceOptions.ApiSecret := FConfig.ApiSecret;
oHttp.BinanceOptions.RecvWindow := 5000; // ms tolerance for signed requests
// Test connectivity and confirm your IP whitelist
ShowMessage('Server time: ' + IntToStr(oHttp.GetServerTime));
Si vous voulez tourner contre le testnet, définissez BinanceOptions.Testnet := True — le composant bascule à la fois l'URL de base REST et l'hôte WebSocket automatiquement. Buildez et testez contre le testnet, basculez un seul flag, déployez en production. La documentation de l'API Binance est sinon identique entre les deux environnements.
Étape 5 : flux de données utilisateur
Le même composant WebSocket s'abonne aussi à votre flux de données utilisateur privé — mises à jour de compte, événements d'ordres, changements de position. C'est ainsi que vous réconciliez les fills qui ont eu lieu en dehors de votre bot (annulation manuelle depuis l'UI web, liquidation, etc.).
oBinance.AuthOptions.ApiKey := FConfig.ApiKey;
oBinance.AuthOptions.ApiSecret := FConfig.ApiSecret;
oBinance.Streams.Add('!userData');
procedure TForm1.DoStream(Sender: TObject; const aStream, aData: string);
var
oJSON: TsgcJSONObject;
begin
if aStream = '!userData' then
begin
oJSON := TsgcJSONObject.Parse(aData);
try
if oJSON.S['e'] = 'executionReport' then
FRisk.ReconcileExternalFill(oJSON);
finally
oJSON.Free;
end;
end;
end;
Une note sur le backtesting
Rien de ce qui précède ne répond à « cette stratégie est-elle réellement profitable ? » C'est pour cela que sert le backtesting — rejouer la même stratégie contre des données historiques pour estimer sa performance future. L'architecture ci-dessus rend cela presque gratuit : le thread de stratégie ne se soucie pas que les événements de marché viennent d'un WebSocket en direct ou d'un lecteur CSV. Construisez une source d'événements synthétique qui lit les klines depuis le disque et les alimente dans la même file, et votre code de stratégie tourne inchangé contre des années de données historiques.
Deux pièges à éviter. Le biais look-ahead : ne laissez pas la stratégie jeter un œil à un point de données qui n'aurait pas été disponible au timestamp qu'elle traite. Et le biais de survie : entraînez et testez sur l'univers des symboles qui existaient à l'époque, pas sur la liste sélectionnée des symboles « réussis » qui ont survécu jusqu'aujourd'hui. Les deux ont tué plus de stratégies en production que tous les bugs de code combinés.
Checklist opérationnelle
| Préoccupation | Où la gérer |
| Reconnexion sur coupure Wi-Fi | WatchDog.Enabled := True |
| Détection de lien mort | HeartBeat.Enabled := True |
| Sync horloge (Binance rejette les signatures décalées) | NTP sur l'OS, plus un appel quotidien à l'endpoint server time |
| Idempotence d'ordre | Utilisez newClientOrderId sur chaque ordre |
| Limites de débit | Suivez les en-têtes ; reculez quand vous êtes à 90 % de la limite |
| Kill switch | Booléen unique, basculé depuis l'UI ou un processus watchdog |
| Log d'audit | Chaque intention, chaque fill, chaque reject, append-only |
Au-delà de Binance
Remplacez TsgcWSAPI_Binance par TsgcWSAPI_Coinbase, TsgcWSAPI_Kraken, ou n'importe lequel des 20+ autres composants d'exchange. La stratégie, le gate de risque et le worker d'ordres ne changent pas — seuls la configuration des identifiants et le nommage des symboles. Pour un trader multi-exchanges de qualité production avec graphiques, gestion de position et UI de routage d'ordres d'office, regardez l'exemple sgcTrader.
Les vrais systèmes multi-exchanges ajoutent une couche de plus au-dessus de ce que vous avez vu ici : un normaliseur de symboles. Binance l'appelle BTCUSDT, Coinbase l'appelle BTC-USD, Kraken l'appelle XBT/USD. Construisez un modèle de symbole interne avec un nom canonique et des alias par exchange, et traduisez à la frontière de l'API. Cinq minutes de travail en amont, infinité de bugs économisés en aval.
L'autre chose à ajouter pour les opérations multi-exchanges est une vérification du décalage d'horloge au démarrage. Binance, Coinbase et les autres rejettent les requêtes signées avec un timestamp à plus de 1 000 ms du leur. NTP vous garde habituellement bien dans cette plage, mais un VPS mal configuré peut dériver de secondes en une heure. Interrogez l'endpoint server time au démarrage, journalisez l'offset, refusez de trader s'il est > 500 ms.
Pourquoi Delphi pour cela ?
« Pourquoi pas l'écrire en Python ? » est la question évidente. Trois réponses depuis la production. Premièrement, le warm-up JIT et le GIL font de CPython un mauvais choix pour les boucles d'événements à faible latence — la même stratégie qui atteint 0,8 ms de latence médiane en Delphi prend 6 ms en CPython sans effort sérieux. Deuxièmement, l'histoire du déploiement est plus simple : un exe signé vs un virtualenv avec cent wheels, dont la moitié nécessite un compilateur C à l'installation. Troisièmement, le back office existant est en Delphi. Réutiliser ces classes — livre de comptes, calculateur P&L, journal, log d'audit — dans le nouveau bot au lieu de les ré-implémenter dans un autre langage élimine toute une catégorie de bugs de réconciliation.
Pour la recherche pure et les backtests style notebook, Python gagne facilement — l'écosystème pandas, statsmodels, vectorbt et compagnie est inégalé. La séparation qui fonctionne pour la plupart des shops : recherche en Python, production en Delphi. Exportez la logique de stratégie en petite machine à états, portez-la une fois, exécutez-la sur un runtime Delphi éprouvé au combat. Les deux moitiés n'ont pas besoin de partager un langage pour partager des résultats.
Que lire ensuite
Si vous prévoyez de faire tourner ceci 24/7 sur un VPS, lisez ensuite Optimisation des performances. Pour éviter les pièges les plus courants, voir 10 erreurs courantes. Et si vous n'avez pas encore installé sgcWebSockets, le hub Premiers pas vous met en ligne en cinq minutes.
Avertissement : la stratégie de ce billet est à des fins éducatives. Le trading de cryptomonnaies implique un risque substantiel. Ne déployez pas du code non testé avec du capital réel.