L'objectif de cet article est de raconter quelques expériences dans le développement de stratégies, ainsi que quelques petites astuces qui permettront au lecteur d'avoir une idée rapide du développement de stratégies de trading. Lorsque vous rencontrez des problèmes de détail similaires dans la conception de votre stratégie, vous pouvez immédiatement concevoir une solution raisonnable. La plateforme de trading quantifié par les inventeurs sert de plateforme d'enseignement, de test et d'exercice. Le langage de programmation stratégique: JavaScript Les marchés: les marchés d'actifs blockchain (BTC, ETH, etc.)
Il est généralement possible d'utiliser plusieurs interfaces différentes pour obtenir des données de marché en fonction de la logique de la stratégie, car la logique de négociation des stratégies est généralement basée sur les données de marché.
GetTicker: obtenir des ticks en temps réel. Il est généralement utilisé pour obtenir rapidement les derniers prix actuels, acheter un prix, vendre un prix.
GetDepth: obtenir des commandes dans des marchés peu profonds. Il est généralement utilisé pour obtenir le prix par lot, la taille de la commande. Il s'agit d'une stratégie de couverture, d'une stratégie de marché, etc.
GetTrade: accéder aux derniers enregistrements de transactions sur le marché Il est généralement utilisé pour analyser le comportement du marché à court terme et pour analyser les variations microscopiques du marché. Il est généralement utilisé pour des stratégies de haute fréquence, des stratégies d'algorithmes.
GetRecords: obtenir des données sur le marché K. Il s'agit d'une stratégie de suivi des tendances. Le nombre d'heures d'activité est estimé en fonction de l'année.
Lors de la conception d'une stratégie, les débutants ignorent généralement les erreurs, en supposant intuitivement que les résultats de chaque étape de la stratégie sont définis. Mais ce n'est pas le cas, et des situations inattendues peuvent survenir lors de la demande de données de marché lors de l'exécution du programme de stratégie. Par exemple, certaines interfaces de marché ont renvoyé des données inhabituelles:
var depth = exchange.GetDepth()
// depth.Asks[0].Price < depth.Bids[0].Price 卖一价格低于了买一价格,这种情况不可能存在于盘面上,
// 因为卖出的价格低于买入的价格,必定已经成交了。
// depth.Bids[n].Amount = 0 订单薄买入列表第n档,订单量为0
// depth.Asks[m].Price = 0 订单薄卖出列表第m档,订单价格为0
Ou directement par exchange.GetDepth ((() renvoie une valeur nulle.
Il y a beaucoup de cas étranges comme celui-ci. Il est donc nécessaire de traiter ces problèmes prévisibles de manière appropriée, ce que l'on appelle le traitement de l'erreur.
Le traitement habituel des erreurs consiste à jeter les données et à 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) { // 以 检测卖一价格是不是小于买一价这个错误的容错处理为例,
// 排除这个错误,当前函数返回 ticker 。
return ticker
}
Sleep(500)
}
}
function onTick () {
var ticker = GetTicker() // 确保获取到的 ticker 不会存在 卖一价格小于买一价格这种数据错误的情况。
// ... 具体的策略逻辑
}
D'autres traitements d'erreur prévisibles peuvent être utilisés de la même manière. Le principe de conception est qu'il est absolument impossible de donner des données erronées pour piloter la logique stratégique.
Les données de la ligne K sont récupérées et appelées:
var r = exchange.GetRecords()
Les données de la ligne K obtenues sont une matrice qui ressemble à ceci:
[
{"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 les parenthèses de chaque fleur.{}
Le milieu est composé de l'heure, du prix d'ouverture, du prix le plus élevé, du prix le plus bas, du prix de clôture, du volume.
Il s'agit d'une colonne K. Les données K générales sont utilisées pour calculer des indicateurs, tels que: MA, MACD, etc.
En entrant les données de la ligne K comme paramètres (les données brutes), nous définissons les paramètres de l'indicateur et calculons une fonction de données d'indicateur que nous appelons une fonction d'indicateur.
Il existe de nombreuses fonctions d'indicateur sur les plateformes de négociation quantitative des inventeurs.
Par exemple, nous calculons l'indicateur de la ligne moyenne, selon la période de données de la ligne K que nous passons, ce qui est calculé est la ligne moyenne de la période correspondante. Par exemple, les données K journées (une colonne K représente un jour) sont calculées comme des moyennes journalières, de même que si les données K journales de la fonction indicateur de la ligne moyenne sont des périodes d'une heure, les indicateurs calculés sont des moyennes d'une heure.
Il y a généralement un problème que nous négligeons lorsque nous calculons les indicateurs, si je dois calculer un indicateur de la ligne moyenne de 5 jours, nous devons d'abord préparer les données de la ligne moyenne de 5 jours:
var r = exchange.GetRecords(PERIOD_D1) // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
// 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
Avec les données de la ligne K, nous pouvons calculer l'indicateur de la ligne moyenne, nous devons calculer la ligne moyenne de 5 jours, alors nous devons définir le paramètre de l'indicateur de la fonction de l'indicateur à 5.
var ma = TA.MA(r, 5) // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
// 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
Nous avons ignoré un problème potentiel: si le nombre de colonnes K dans les données de la ligne K de r jours est inférieur à 5, comment calculer un indicateur de la ligne moyenne de 5 jours efficace? La réponse est certainement non. L'indicateur de la ligne moyenne est la valeur moyenne du prix de clôture pour un certain nombre de colonnes de la ligne K.
Donc, avant d'utiliser les données de ligne K, il faut déterminer si le nombre de colonnes de ligne K dans les données de ligne K satisfait aux conditions de calcul des indicateurs (paramètres d'indicateur).
Donc, avant de calculer la moyenne de 5 jours, le code complet est le suivant:
function CalcMA () {
var r = _C(exchange.GetRecords, PERIOD_D1) // _C() 是容错函数,目的就是避免 r 为 null , 具体可以查询文档:https://www.fmz.com/api#_C
if (r.length > 5) {
return TA.MA(r, 5) // 用均线指标函数 TA.MA 计算出均线数据,做为函数返回值,返回。
}
return false
}
function main () {
var ma = CalcMA()
Log(ma)
}
Les résultats montrent: [null, null, null, null, 4228.7, 4402.9400000000005,... ]
Vous pouvez voir que l'indicateur de la ligne moyenne de 5 jours calculé, les 4 premiers sont nuls, c'est parce que le nombre de colonnes de la ligne K est inférieur à 5, il est impossible de calculer la valeur moyenne; jusqu'à la 5ème base de la colonne de la ligne K, vous pouvez le calculer.
Il y a souvent un scénario où nous écrivons des stratégies et nous devons traiter des opérations à chaque cycle de ligne K ou imprimer des journaux. Pour les débutants qui n'ont aucune expérience de programmation, vous ne pouvez pas imaginer le mécanisme à utiliser, mais nous vous donnons ici quelques astuces.
Nous jugeons que le cycle d'une colonne K est terminé, nous pouvons commencer par l'attribut temps dans les données de la ligne K, et chaque fois que nous obtenons des données de la ligne K, nous jugeons si la valeur de l'attribut Time a changé dans les données de la dernière colonne K de la ligne K, si elle a changé, cela signifie qu'il y a une nouvelle colonne K (ce qui prouve que le cycle de la colonne K précédente de la nouvelle colonne K est terminé), si rien n'a changé, cela signifie qu'il n'y a pas de nouvelle colonne K pour produire la dernière colonne actuelle (la période de la colonne K n'est pas encore terminée).
Donc nous voulons une variable pour enregistrer le temps de la dernière colonne de la ligne K.
var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // lastTime 用来记录最后一根K线柱的时间。
Dans la pratique, la structure est généralement la suivante:
function main () {
var lastTime = 0
while (true) {
var r = _C(exchange.GetRecords)
if (r[r.length - 1].Time != lastTime) {
Log("新K线柱产生")
lastTime = r[r.length - 1].Time // 一定要更新 lastTime ,这个至关重要。
// ... 其它处理逻辑
// ...
}
Sleep(500)
}
}
Comme vous pouvez le voir dans le retouche, le cycle de la ligne K est défini comme jour (exchange.GetRecords n'indique pas de paramètre lors de l'appel de la fonction, mais est le paramètre par défaut en fonction du cycle de la ligne K défini par le retouche).
Si vous souhaitez avoir une certaine visibilité ou un certain contrôle sur le temps d'interface d'accès à l'échange stratégique, 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() 函数耗时:", endTime - beginTime, "毫秒")
Sleep(1000)
}
}
En d'autres termes, le temps enregistré après l'appel de la fonction GetTicker est diminué par le temps enregistré avant l'appel, pour calculer le nombre de millisecondes vécus, c'est-à-dire le temps que la fonction GetTicker a passé entre l'exécution et le retour des résultats.
Si vous souhaitez qu'une valeur numérique ait une limite, vous utilisez généralement la limite Math.min.
Par exemple, lors d'un ordre de vente, la quantité de monnaie vendue ne doit pas être supérieure au nombre de pièces sur le compte. Le problème est que si le nombre de pièces disponibles est supérieur au nombre de pièces disponibles dans le compte, le paiement sera annulé.
Il est généralement contrôlé comme suit: Par exemple, une vente de 0.2 pièces est prévue.
var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)
Cela garantit que le montant de la commande à effectuer ne dépasse pas le nombre de pièces disponibles dans le compte.
De même, Math.max est utilisé pour assurer la limite inférieure d'une valeur numérique. Dans quel scénario cela s'applique-t-il habituellement? En général, les bourses ont des limites de volume minimum pour certaines paires de transactions et refusent les commandes si elles sont inférieures à ce volume minimum. Supposons que la quantité minimale de BTC soit généralement de 0.01. Il est parfois possible de calculer des unités inférieures à 0.01, nous pouvons donc utiliser Math.max pour assurer le minimum de unités.
Vous pouvez utiliser la fonction _N() ou la fonction SetPrecision pour contrôler la précision.
La fonction SetPrecision (() est configurée une seule fois et interrompt automatiquement le nombre de décimales excédentaires de la quantité et du prix dans le système.
La fonction _N() est une fonction de compression numérique pour une valeur donnée.
Par exemple:
var pi = _N(3.141592653, 2)
Log(pi)
La valeur de pi est découpée par des décimales et conserve 2 décimales, soit: 3.14
Pour plus de détails, voir la documentation de l'API.
Un tel mécanisme peut être utilisé pour déterminer le temps écoulé par rapport au temps écoulé à la fin de la dernière tâche de chronométrage, en calculant en temps réel le temps écoulé lorsque ce temps écoulé dépasse une certaine durée de temps définie, c'est-à-dire l'exécution d'une nouvelle opération.
Par exemple, il est utilisé dans les stratégies de placement.
var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // 一天的毫秒数
function main () {
while (true) {
var nowTime = new Date().getTime()
if (nowTime - lastActTime > waitTime) {
Log("执行定投")
// ... 具体的定投操作,买入操作。
lastActTime = nowTime
}
Sleep(500)
}
}
C'est un exemple simple.
Il est facile de concevoir une stratégie pour quitter le processus de sauvegarde et redémarrer l'état de récupération automatique en utilisant la fonction _G() quantifiée par l'inventeur et la fonction de sauvegarde de sortie.
var hold = {
price : 0,
amount : 0,
}
function main () {
if (_G("hold")) {
var ret = _G("hold")
hold.price = ret.price
hold.amount = ret.amount
Log("恢复 hold:", hold)
}
var count = 1
while (true) {
// ... 策略逻辑
// ... 策略运行中,可能开仓,交易,把开仓的持仓价格赋值给 hold.price ,开仓的数量赋值给 hold.amount,用以记录持仓信息。
hold.price = count++ // 模拟一些数值
hold.amount = count/10 // 模拟一些数值
Sleep(500)
}
}
function onexit () { // 点击机器人上的停止按钮,会触发执行这个函数,执行完毕机器人停止。
_G("hold", hold)
Log("保存 hold:", JSON.stringify(hold))
}
Comme vous pouvez le voir, chaque fois que le robot s'arrête, les données de l'objet hold sont sauvegardées, chaque fois qu'il est redémarré, les données sont lues et la valeur de la retenue est restaurée à l'état précédent l'arrêt. Bien sûr, il s'agit d'un exemple simple, qui, s'il est utilisé dans une stratégie pratique, doit être conçu en fonction des données clés à récupérer dans la stratégie (généralement des informations sur les comptes, les avoirs, les bénéfices, la direction des transactions, etc.). Bien sûr, il est possible de fixer des conditions pour la reprise.
Voici quelques petites astuces pour développer des stratégies, et j'espère qu'elles seront utiles pour les débutants et les développeurs de stratégies! Je vous souhaite beaucoup de bonheur et de longues années d'existence.
- Je vous en prie.Merci de partager, c'est parfait pour les débutants qui ne comprennent pas l'API, et demandez si notre plateforme prend en charge les versions supérieures d'es, par exemple, avez-vous l'habitude d'utiliser?
- Je vous en prie.Merci Dreamweaver! Dreamweaver, c'est vraiment un double-double, la technologie de programmation est élevée, l'article est bon, l'admiration est excellente!!!
L'inventeur de la quantification - un petit rêveBonjour, le support actuel est le standard ES8.
L'inventeur de la quantification - un petit rêveMerci de soutenir FMZ Quantitative!