Expérience dans le développement de stratégies de négociation

Auteur:La bonté, Créé: 2019-09-05 11:49:31, Mis à jour: 2023-11-07 20:48:06

img

Le but de cet article est de décrire une certaine expérience dans le développement de la stratégie, ainsi que quelques conseils, qui permettront aux lecteurs de saisir rapidement le point clé du développement de la stratégie de trading.

Lorsque vous rencontrez des détails similaires dans une stratégie, vous pouvez immédiatement trouver une solution raisonnable.

Nous utilisons la plateforme FMZ Quant comme exemple d'explication, de test et de pratique.

Stratégie langage de programmation nous allons utiliser JavaScript

Pour la cible de négociation, nous prenons le marché des actifs blockchain (BTC, ETH, etc.) comme objet

L'acquisition et le traitement des données

Habituellement, en fonction de la logique de stratégie, il peut utiliser les différentes interfaces suivantes pour obtenir des données de marché, la plupart des logiques de stratégie sont guidées par des données de marché (bien sûr, certaines stratégies ne se soucient pas des données de prix, comme une stratégie d'investissement fixe).

  • Obtenez des citations en temps réel. Généralement utilisé pour obtenir rapidement le dernier prix actuel, Price d'achat 1, Price de vente 1.

  • Obtenez la profondeur des commandes du carnet de commandes. Généralement utilisé pour obtenir le prix de chaque couche de la profondeur du carnet d'ordres et la taille des ordres en attente.

  • GetTrade: obtenir le dernier enregistrement de transaction du marché. Généralement utilisé pour analyser le comportement du marché dans un cycle de temps court et analyser les changements microscopiques sur le marché.

  • GetRecords: Obtenez des données de ligne K du marché. Utilisé habituellement pour les stratégies de suivi des tendances et pour calculer les indicateurs.

Tolérance à la défaillance

Lors de la conception de la stratégie, le débutant ignore généralement les différentes erreurs et croit intuitivement que les résultats de chaque partie de la stratégie sont établis.

Par exemple, certaines interfaces de marché renvoient des données non exécutées:

var depth = exchange.GetDepth()

// depth.Asks[0].Price < depth.Bids[0].Price "Selling 1" price is lower than "buying 1" price, this situation cannot exist on the market.
// Because the selling price is lower than the buying price, the order must have been executed.
// depth.Bids[n].Amount = 0 Order book buying list "nth" layer, order quantity is 0
// depth.Asks[m].Price = 0 Order book selling list "mth" layer, the order price is 0

Ou directement exchange.GetDepth() renvoie une valeur nulle.

Il existe de nombreuses situations étranges de ce genre. Il est donc nécessaire de traiter ces problèmes prévisibles. Un tel schéma de traitement est appelé traitement tolérant aux défauts.

La façon normale de gérer les défauts est de jeter les données et de les récupérer.

Par exemple:

function main () {
     while (true) {
         onTick()
         Sleep(500)
     }
}

function GetTicker () {
     while (true) {
         var ticker = exchange.GetTicker()
         if (ticker.Sell > ticker.Buy) { // Take the example of fault-tolerant processing that detects whether the "Selling 1" price is less than the "Buying 1" price.
                                               // Exclude this error, the current function returns "ticker".
             Return ticker
         }
         Sleep(500)
     }
}

function onTick () {
     var ticker = GetTicker() // Make sure the "ticker" you get doesn't exist the situation that "Selling 1" price is less than the "Buying 1" price.
     // ... specific strategy logic
}

Une approche similaire peut être utilisée pour d'autres procédés prévisibles à tolérance aux défauts.

Le principe de conception est que vous ne pouvez jamais utiliser la mauvaise logique pour conduire la logique de la stratégie.

Utilisation des données de ligne K

Acquisition de données de ligne K, appel:

var r = exchange.GetRecords()

Les données de ligne K obtenues sont un tableau, tel que celui-ci:

[
    {"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
    {"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
    ...
    {"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]

Vous pouvez voir que chaque support en boucle contient le temps, le prix d'ouverture, le prix le plus élevé, le prix le plus bas, le prix de clôture et le volume.

Les données générales de la ligne K sont utilisées pour calculer des indicateurs tels que les moyennes mobiles, le MACD, etc.

Les données de la ligne K sont transmises sous forme de paramètres (données de matières premières), puis les paramètres de l'indicateur sont définis pour calculer la fonction des données de l'indicateur, que nous appelons la fonction de l'indicateur.

Il existe de nombreuses fonctions d'indicateur sur la plateforme de trading quantitative FMZ Quant.

Par exemple, nous calculons l'indicateur de moyenne mobile. Selon le cycle des données de ligne K passées, nous calculons la moyenne mobile du cycle correspondant.

Par exemple, les données de la ligne K passante (une barre de ligne K représente un jour), calcule la ligne moyenne quotidienne, la même chose, si les données de la ligne K de la fonction d'indicateur de moyenne passante est un cycle de 1 heure, alors l'indicateur calculé est la moyenne mobile de 1 heure.

Si je veux calculer l'indicateur de moyenne mobile sur 5 jours, alors nous préparons d'abord les données quotidiennes de la ligne K:

var r = exchange.GetRecords(PERIOD_D1) // Pass parameters to the "GetRecords" function "PERIOD_D1" specifies the day K line to be acquired.
                                       // Specific function using method can be seen at: https://www.fmz.com/api#GetRecords

Avec les données quotidiennes de la ligne K, nous pouvons calculer l'indicateur de moyenne mobile. si nous voulons calculer la moyenne mobile de 5 jours, alors nous devons définir le paramètre de l'indicateur de la fonction indicateur à 5.

var ma = TA.MA(r, 5) // "TA.MA()" is the indicator function used to calculate the moving average indicator. The first parameter sets the daily K-line data r just obtained.
                             // The second parameter is set to 5. The calculated 5-day moving average is the same as the other indicators.

Si le nombre de barres de lignes K dans les données de lignes K est inférieur à 5, que pouvons-nous faire pour calculer une moyenne mobile de 5 jours valide?

La réponse est que vous ne pouvez rien faire.

Parce que l'indicateur de moyenne mobile est la moyenne des prix de clôture d'un certain nombre de barres de ligne K.

img

Par conséquent, avant d'utiliser les données de ligne K et la fonction d'indicateur pour calculer les données d'indicateur, il est nécessaire de déterminer si le nombre de barres de ligne K dans les données de ligne K satisfait aux conditions du calcul de l'indicateur (paramètres de l'indicateur).

Donc, avant de calculer la moyenne mobile sur 5 jours, vous devez d'abord le vérifier.

function CalcMA () {
     var r = _C(exchange.GetRecords, PERIOD_D1) // _C() is a fault-tolerant function, the purpose is to avoid r being null, you can get more information at: https://www.fmz.com/api#_C
     if (r.length > 5) {
         Return TA.MA(r, 5) // Calculate the moving average data with the moving average indicator function "TA.MA", return it as a function return value.
     }

     Return false
}

function main () {
     var ma = CalcMA()
     Log(ma)
}

img

Affichage du test arrière:

[null,null,null,null,4228.7,4402.9400000000005, ... ]

Vous pouvez voir l'indicateur de la moyenne mobile de 5 jours calculé. Les quatre premiers sont nuls, parce que le nombre de barres de la ligne K est inférieur à 5, et la moyenne ne peut pas être calculée.

Conseils pour juger les mises à jour de la ligne K

Lorsque nous écrivons la stratégie, nous avons souvent un tel scénario, par exemple la stratégie doit traiter certaines opérations lorsque chaque cycle de ligne K est terminé, ou imprimer quelques journaux.

Pour les débutants qui n'ont pas d'expérience de programmation, cela peut être un problème gênant.

Comment juger un cycle de barre de ligne K est terminé. Nous pouvons commencer par l'attribut temps dans les données de ligne K. Chaque fois que nous obtenons les données de ligne K, nous jugerons si l'attribut temps de la dernière barre de ligne K de ces données de ligne K change ou non. Si elle est modifiée, cela signifie qu'une nouvelle barre de ligne K est générée (prouvant que le cycle de barre de ligne K précédent de la barre de ligne K nouvellement générée est terminé), s'il n'y a pas de changement, cela signifie qu'aucune nouvelle barre de ligne K n'est générée (le dernier cycle de barre de ligne K actuel n'a pas encore été terminé).

Donc nous avons besoin d'une variable pour enregistrer l'heure de la dernière barre de ligne K des données de ligne K.

var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // "lastTime" used to record the last K-line bar time.

Dans la pratique, c'est généralement le cas:

function main () {
     var lastTime = 0
     while (true) {
         var r = _C(exchange.GetRecords)
         if (r[r.length - 1].Time != lastTime) {
             Log ("New K-line bar generated")
             lastTime = r[r.length - 1].Time // Be sure to update "lastTime", this is crucial.

             // ... other processing logic
             // ...
         }

         Sleep(500)
     }
}

img

Vous pouvez voir que dans le backtest, le cycle de ligne K est réglé sur le quotidien (le paramètre n'est pas spécifié lorsque leexchange.GetRecordsLa fonction K est appelée, et le cycle de ligne K défini selon le backtest est le paramètre par défaut).

Calculs de la valeur numérique

  • Calculer le temps passé à accéder à l'interface d'échange

Si vous voulez avoir un certain affichage ou un certain contrôle sur le temps nécessaire à la stratégie pour accéder à l'interface de l'échange, vous pouvez utiliser le code suivant:

function main () {
     while (true) {
         var beginTime = new Date().getTime()
         var ticker = exchange.GetTicker()
         var endTime = new Date().getTime()

         LogStatus(_D(), "GetTicker() function time-consuming:", endTime - beginTime, "millisecond")
         Sleep(1000)
     }
}

En termes simples, l'horodatage enregistré après avoir appelé leGetTickerLa fonction est soustraite de l'horodatage avant l'appel, et le nombre de millisecondes expérimentées est calculé, c'est-à-dire le temps pris par leGetTickerfonction de l'exécution au retour.

  • Utilisez Math.min / Math.max pour limiter les limites supérieure et inférieure de la valeur

Par exemple, dans le processus de placement d'un ordre de vente, le montant de l'ordre de vente ne doit pas être supérieur au nombre de pièces sur le compte. Parce que si elle est supérieure au nombre de pièces disponibles sur le compte, l'ordre provoquera des erreurs.

On le contrôle comme ça:

Par exemple, nous prévoyons de vendre à découvert 0,2 pièces.

var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)

Cela garantit que le nombre de commandes passées ne dépassera pas le nombre de pièces disponibles sur le compte.

Pour la même raison,Math.maxest utilisé pour assurer la limite inférieure d'une valeur.

  • À quel genre de scène s'applique- t- il habituellement?

Généralement, l'échange normal a une limite minimale d'envoi d'ordre pour certaines paires commerciales. Si elle est inférieure au montant minimum, l'ordre sera rejeté. Cela entraînera également l'échec du programme.

En supposant que le BTC ait généralement une quantité minimale d'ordre de placement de 0,01.

Les stratégies de trading peuvent parfois aboutir à des quantités d'ordre inférieures à 0,01. Nous pouvons donc utiliserMath.maxpour assurer la quantité minimale de commande.

  • Quantité de commande, contrôle de la précision des prix

La précision peut être contrôlée à l'aide du_N()fonction ou leSetPrecision function.

LeSetPrecision()La fonction n'a besoin d'être définie qu'une seule fois, et le nombre de décimales dans la quantité de commande et la valeur du prix est automatiquement tronqué dans le système.

Le_N()La fonction est d'effectuer une troncation en virgule décimale (contrôle de précision) pour une certaine valeur.

Par exemple:

var pi = _N(3.141592653, 2)
Log(pi)

La valeur de pi est tronquée par la virgule, et 2 virgules sont réservées, ce qui est: 3.14

Voir la documentation API pour plus de détails.

Quelques paramètres logiques

  • Temps, effectuer certaines opérations pendant une certaine période de temps

Vous pouvez utiliser un tel mécanisme pour utiliser la méthode de détection d'horodatage pour déterminer l'horodatage actuel moins l'horodatage de la dernière fois que la tâche programmée a été exécutée, et calculer le temps écoulé en temps réel. Lorsque le temps écoulé dépasse une certaine durée de temps définie. Après cela, une nouvelle opération est effectuée.

Par exemple, utilisé dans une stratégie d'investissement fixe.

var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // number of milliseconds a day
function main () {
     while (true) {
         var nowTime = new Date().getTime()
         if (nowTime - lastActTime > waitTime) {
             Log ("Execution Fixed")
             // ... specific fixed investment operation, buying operation.


             lastActTime = nowTime
         }

         Sleep(500)
     }
}

C'est un exemple simple.

  • Concevoir un mécanisme de récupération automatique pour la stratégie

Utilisation de la quantité FMZ_G()Il est pratique de concevoir une stratégie pour sortir de l'état de sauvegarde et redémarrer l'état de récupération automatique.

var hold = {
     Price : 0,
     Amount : 0,
}

function main () {
     if (_G("hold")) {
         var ret = _G("hold")
         hold.price = ret.price
         hold.amount = ret.amount
         Log("restore hold:", hold)
     }

     var count = 1
     while (true) {
         // ... strategy logic
         // ... In the strategy operation, it is possible that when opening a position, then assign the position price of the open position to "hold.price", and the amount of open positions is assigned to "hold.amount" to record the position information.

         hold.price = count++ // simulate some values
         hold.amount = count/10 // Simulate some values

         Sleep(500)
     }
}

function onexit () { // Click the stop button on the robot to trigger the execution of this function. After the execution, the robot stops.
     _G("hold", hold)
     Log("save hold:", JSON.stringify(hold))
}

img

On peut constater que les donnéesholdl'objet est enregistré à chaque fois que le robot est arrêté. et à chaque fois que les données sont redémarrées, les données sont lues et la valeur duholdest restauré à l'état d'avant l'arrêt.

Bien sûr, ce qui précède est un exemple simple. s'il est utilisé dans une stratégie de trading réelle, il devrait être conçu en fonction des données clés qui doivent être restaurées dans la stratégie (généralement sont des informations de compte, position, valeur de profit, direction de trading et ainsi de suite.).

En outre, vous pouvez également définir d'autres conditions de restauration.

Ce sont quelques conseils pour développer une stratégie de trading, et j'espère que cela pourrait aider les débutants!

L'entraînement pratique est le moyen le plus rapide de s'améliorer! Je vous souhaite bonne chance.


Relationnée

Plus de