Les ressources ont été chargées... Je charge...

Découvrez les stratégies d'analyse de la machine à cueillir les légumineuses (1)

Auteur:L'inventeur de la quantification - un petit rêve, Créé: 2020-11-12 22:11:32, Mis à jour: 2024-12-06 22:20:54

img

Découvrez les stratégies de récolte des légumineuses

Récemment, les inventeurs ont discuté de la quantification dans le groupe WeChat.print moneyLes robots, dont les discussions sont très animées, sont une stratégie très ancienne qui revient dans les yeux des généreux:La moissonneuse de légumineusesprint moneyLe principe de négociation du robot est basé sur la stratégie de la tondeuse à noix de cajou, et il se reproche de ne pas avoir été assez clair et de ne pas avoir compris la stratégie de la tondeuse à noix de cajou.Transplantation de OKCoin dans une récolteuse de noix de cajouJe suis désolé. L'idée est de quantifier la stratégie de la machine à cueillir les légumineuses de la plate-forme, de l'analyser et de l'explorer, afin que les utilisateurs puissent l'apprendre. Dans cet article, nous analysons davantage les idées stratégiques, les intentions, etc., en essayant de réduire le plus possible le contenu ennuyeux lié à la programmation.

[Transporter OKCoin à une récolteuse de noix de cajou]

function LeeksReaper() {
    var self = {}
    self.numTick = 0
    self.lastTradeId = 0
    self.vol = 0
    self.askPrice = 0
    self.bidPrice = 0
    self.orderBook = {Asks:[], Bids:[]}
    self.prices = []
    self.tradeOrderId = 0
    self.p = 0.5
    self.account = null
    self.preCalc = 0
    self.preNet = 0

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)
        if (self.prices.length == 0) {
            while (trades.length == 0) {
                trades = trades.concat(_C(exchange.GetTrades))
            }
            for (var i = 0; i < 15; i++) {
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }
    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }
    self.balanceAccount = function() {
        var account = exchange.GetAccount()
        if (!account) {
            return
        }
        self.account = account
        var now = new Date().getTime()
        if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {
            self.preCalc = now
            var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks))
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
        }
        self.btc = account.Stocks
        self.cny = account.Balance
        self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
        var balanced = false
        
        if (self.p < 0.48) {
            Log("开始平衡", self.p)
            self.cny -= 300
            if (self.orderBook.Bids.length >0) {
                exchange.Buy(self.orderBook.Bids[0].Price + 0.00, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.01, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.02, 0.01)
            }
        } else if (self.p > 0.52) {
            Log("开始平衡", self.p)
            self.btc -= 0.03
            if (self.orderBook.Asks.length >0) {
                exchange.Sell(self.orderBook.Asks[0].Price - 0.00, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.01, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.02, 0.01)
            }
        }
        Sleep(BalanceTimeout)
        var orders = exchange.GetOrders()
        if (orders) {
            for (var i = 0; i < orders.length; i++) {
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)
                }
            }
        }
    }

    self.poll = function() {
        self.numTick++
        self.updateTrades()
        self.updateOrderBook()
        self.balanceAccount()
        
        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct
        var bull = false
        var bear = false
        var tradeAmount = 0
        if (self.account) {
            LogStatus(self.account, 'Tick:', self.numTick, ', lastPrice:', self.prices[self.prices.length-1], ', burstPrice: ', burstPrice)
        }
        
        if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -1)) > burstPrice ||
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -2)) > burstPrice && self.prices[self.prices.length-1] > self.prices[self.prices.length-2]
            )) {
            bull = true
            tradeAmount = self.cny / self.bidPrice * 0.99
        } else if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -1)) < -burstPrice ||
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -2)) < -burstPrice && self.prices[self.prices.length-1] < self.prices[self.prices.length-2]
            )) {
            bear = true
            tradeAmount = self.btc
        }
        if (self.vol < BurstThresholdVol) {
            tradeAmount *= self.vol / BurstThresholdVol
        }
        
        if (self.numTick < 5) {
            tradeAmount *= 0.8
        }
        
        if (self.numTick < 10) {
            tradeAmount *= 0.8
        }
        
        if ((!bull && !bear) || tradeAmount < MinStock) {
            return
        }
        var tradePrice = bull ? self.bidPrice : self.askPrice
        while (tradeAmount >= MinStock) {
            var orderId = bull ? exchange.Buy(self.bidPrice, tradeAmount) : exchange.Sell(self.askPrice, tradeAmount)
            Sleep(200)
            if (orderId) {
                self.tradeOrderId = orderId
                var order = null
                while (true) {
                    order = exchange.GetOrder(orderId)
                    if (order) {
                        if (order.Status == ORDER_STATE_PENDING) {
                            exchange.CancelOrder(orderId)
                            Sleep(200)
                        } else {
                            break
                        }
                    }
                }
                self.tradeOrderId = 0
                tradeAmount -= order.DealAmount
                tradeAmount *= 0.9
                if (order.Status == ORDER_STATE_CANCELED) {
                    self.updateOrderBook()
                    while (bull && self.bidPrice - tradePrice > 0.1) {
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    while (bear && self.askPrice - tradePrice < -0.1) {
                        tradeAmount *= 0.99
                        tradePrice -= 0.1
                    }
                }
            }
        }
        self.numTick = 0
    }
    return self
}

function main() {
    var reaper = LeeksReaper()
    while (true) {
        reaper.poll()
        Sleep(TickInterval)
    }
}

Une vue d'ensemble de la stratégie

Généralement, pour apprendre une stratégie, il faut d'abord lire l'ensemble de la structure du programme. Le code de la stratégie n'est pas très grand, moins de 200 lignes de code, il est très élégant et a un rendement de stratégie très élevé par rapport à la version originale.main()La fonction commence à exécuter, le code de la stratégie de l'article, saufmain()Il s'agit d'un groupe appeléLeeksReaper()C'est une fonction deLeeksReaper()La fonction est également bien comprise, elle peut être comprise comme une fonction constructrice d'un module logique de stratégie d'un moissonneur de légumes (un objet), en termes simples.LeeksReaper()Il est responsable de la construction de la logique de négociation d'une moissonneuse à légumes.

Les mots clés:img img

  • La stratégiemainLa première ligne de la fonction:var reaper = LeeksReaper()Le code déclare une variable locale.reaper, puis appeler la fonction LeeksReaper))) pour construire un objet logique stratégique qui attribue une valeur àreaper

  • La stratégiemainLa fonction est la suivante:

    while (true) {
        reaper.poll()
        Sleep(TickInterval)
    }
    

    entrer dans unwhileLe cycle de la mort, l'exécution incessantereaperFonction de traitement des objetspoll()poll()La fonction est la logique principale de la stratégie de transaction, et c'est à partir de là que commence la logique d'exécution de la stratégie. Pour ce qui est deSleep(TickInterval)Cette ligne est bien comprise pour contrôler le temps de pause après chaque exécution de la logique de transaction globale, afin de contrôler la fréquence de rotation de la logique de transaction.

Le dépistageLeeksReaper()Fonction de construction

Regardez!LeeksReaper()Comment les fonctions construisent-elles un objet logique stratégique?

LeeksReaper()La fonction commence en déclarant un objet vide.var self = {}Je ne sais pas.LeeksReaper()Au cours de l'exécution, la fonction ajoute progressivement des méthodes, des attributs à cet objet vide, pour finalement terminer la construction de cet objet et finalement retourner cet objet (c'est-à-diremain()Dans la fonctionvar reaper = LeeksReaper()Cette étape donne une valeur à l'objet retourné.reaper)。

Donne-moi uneselfAjouter des attributs

Je vais vous donnerselfBeaucoup d'attributs ont été ajoutés, et je vais les décrire ci-dessous, afin de comprendre rapidement ces attributs, l'utilisation des variables, leur intention, les stratégies faciles à comprendre, et d'éviter de voir cette pile de code dans le brouillard.

    self.numTick = 0         # 用来记录poll函数调用时未触发交易的次数,当触发下单并且下单逻辑执行完时,self.numTick重置为0
    self.lastTradeId = 0     # 交易市场已经成交的订单交易记录ID,这个变量记录市场当前最新的成交记录ID
    self.vol = 0             # 通过加权平均计算之后的市场每次考察时成交量参考(每次循环获取一次市场行情数据,可以理解为考察了行情一次)
    self.askPrice = 0        # 卖单提单价格,可以理解为策略通过计算后将要挂卖单的价格
    self.bidPrice = 0        # 买单提单价格
    self.orderBook = {Asks:[], Bids:[]}    # 记录当前获取的订单薄数据,即深度数据(卖一...卖n,买一...买n)
    self.prices = []                       # 一个数组,记录订单薄中前三档加权平均计算之后的时间序列上的价格,简单说就是每次储存计算得到的订单薄前三档加权平均价格,放在一个数组中,用于后续策略交易信号参考,所以该变量名是prices,复数形式,表示一组价格
    self.tradeOrderId = 0    # 记录当前提单下单后的订单ID
    self.p = 0.5             # 仓位比重,币的价值正好占总资产价值的一半时,该值为0.5,即平衡状态
    self.account = null      # 记录账户资产数据,由GetAccount()函数返回数据
    self.preCalc = 0         # 记录最近一次计算收益时的时间戳,单位毫秒,用于控制收益计算部分代码触发执行的频率
    self.preNet = 0          # 记录当前收益数值

Donne-moi uneselfComment ajouter des objets

Et puis on a ajouté ces propriétés à self et on a commencé à donner des propriétés à self.selfL'objectif est d'ajouter des méthodes pour permettre à cet objet de faire des choses et d'avoir des fonctions.

La première fonction ajoutée:

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)  # 调用FMZ封装的接口GetTrades,获取当前最新的市场成交数据
        if (self.prices.length == 0) {       # 当self.prices.length == 0时,需要给self.prices数组填充数值,只有策略启动运行时才会触发
            while (trades.length == 0) {     # 如果近期市场上没有更新的成交记录,这个while循环会一直执行,直到有最新成交数据,更新trades变量
                trades = trades.concat(_C(exchange.GetTrades))   # concat 是JS数组类型的一个方法,用来拼接两个数组,这里就是把“trades”数组和“_C(exchange.GetTrades)”返回的数组数据拼接成一个数组
            }
            for (var i = 0; i < 15; i++) {   # 给self.prices填充数据,填充15个最新成交价格
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {  # _.reduce 函数迭代计算,累计最新成交记录的成交量
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }

updateTradesLe rôle de cette fonction est d'obtenir les données de transaction les plus récentes du marché, et de faire des calculs et des enregistrements basés sur les données, ce qui est utilisé dans la logique de suivi de la stratégie. Les commentaires sont écrits directement dans le code ci-dessus. Pour_.reduceLes élèves qui n'ont pas de connaissances en programmation peuvent être perplexes._.reduceOuiUnderscore.jsLes fonctions de cette bibliothèque, les stratégies FMZJS qui la prennent en charge, sont donc très pratiques pour le calcul itératif.Underscore.js资料链接

Il y a aussi une signification très simple, comme:

function main () {
   var arr = [1, 2, 3, 4]
   var sum = _.reduce(arr, function(ret, ele){
       ret += ele
       
       return ret
   }, 0)

   Log("sum:", sum)    # sum 等于 10
}

C'est une série de nombres.[1, 2, 3, 4]Nous avons donc un système de calcul de la somme des nombres.tradesLes données de chaque enregistrement de transaction dans l'arithmétique s'additionnent à la valeur de chaque enregistrement de transaction.self.vol = 0.7 * self.vol + 0.3 * _.reduce(...)Je vous en prie, permettez-moi...C'est un peu comme si vous aviez créé un code pour remplacer ce code.self.volLe calcul de l'indice est également une moyenne pondérée; c'est-à-dire que la transaction la plus récente est pondérée à 30% de la transaction totale et que la transaction la plus récente est pondérée à 70% de la transaction totale. Cette proportion est définie par l'auteur de la stratégie et peut être liée à l'observation des lois du marché. Et si vous me demandez ce que je ferais si l'interface qui récupère les données des transactions récentes me renvoyait des données anciennes répétitives, si les données que j'ai obtenues étaient fausses et utilisables?

if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
    ...
}

Ce jugement peut être basé sur l'ID de transaction dans les enregistrements de transactions, et déclenche l'accumulation uniquement lorsque l'ID est supérieur à l'ID enregistré précédemment, ou si l'interface de l'échange ne fournit pas l'ID.trade.Id == 0Le temps de transaction est calculé en utilisant le chronomètre de la transaction, et le temps de transaction est calculé en utilisant le chronomètre de la transaction.self.lastTradeIdLes données sont stockées sur le timestamp de l'enregistrement de transaction, et non sur l'ID.

La deuxième fonction ajoutée:

    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }

Vous pouvez le voir.updateOrderBookCette fonction, comme on peut le voir dans le nom littéral de la fonction, a pour fonction de mettre à jour l'ordre mince. Bien sûr, il ne s'agit pas seulement de mettre à jour l'ordre mince.GetDepth()On obtient les données actuelles sur les dépôts de marché (vendre un... vendre n, acheter un... acheter n) et on enregistre les données sur les dépôts.self.orderBookEnsuite, si les commandes sont de données minces, les commandes de vente et de paiement sont inférieures à trois rangées, la fonction est déclarée nulle et retourne directement.

Les résultats de l'enquête ont été publiés dans le journal Le Monde.

  • Calculer le prix de la commande Le calcul du prix de la facture est également effectué en utilisant une moyenne pondérée. Pour le calcul du paiement, le droit d'achat est significatif à 61.8% ((0.618), le droit de vente représente 38.2% ((0.382) Le prix de vente est le même pour le calcul du prix d'achat. Le prix de vente est plus important que le prix d'achat. En ce qui concerne la raison pour laquelle 0.618, peut-être que l'auteur préfère le pourcentage de fractionnement en or. En ce qui concerne le prix d'un point à la fin, le prix d'un point à la fin est un peu décalé par rapport au centre de la facture.

  • Mise à jour de la séquence chronologique des commandes en moins de trois premières tranches et prix moyen pondéré Pour les trois premiers niveaux d'achat, le prix de vente est calculé en moyenne pondérée, le premier niveaux de poids est de 0,7, le deuxième de 0,2 et le troisième de 0,1 ; peut-être que certains collègues diront: oh, non, il y a 0,7, 0,2, 0,1 dans le code. Nous allons commencer à calculer:

    (买一 + 卖一) * 0.35 + (买二 + 卖二) * 0.1 + (买三 + 卖三) * 0.05
    ->
    (买一 + 卖一) / 2 * 2 * 0.35 + (买二 + 卖二) / 2 * 2 * 0.1 + (买三 + 卖三) / 2 * 2 * 0.05
    ->
    (买一 + 卖一) / 2 * 0.7 + (买二 + 卖二) / 2 * 0.2 + (买三 + 卖三) / 2 * 0.1
    ->
    第一档平均的价格 * 0.7 + 第二档平均的价格 * 0.2 + 第三档平均的价格 * 0.1
    

    On peut voir que le prix final calculé est en fait la position des prix des trois classes intermédiaires sur le marché actuel. Et puis avec ce prix calculé, vous pouvez mettre à jour.self.pricesL'arithmétique, pour extraire une des données les plus anciennes (par exempleshift()Fonction), à mettre à jour vers une dernière donnée (par exemple,push()Les fonctions, shift et push sont des méthodes d'objets de l'arithmétique du langage JS.self.pricesL'arithmétique est un flux de données avec une séquence chronologique.

Cough, cough, drink, dissection avant d'arriver ici, on se voit plus tard


Plus de

Le moi qui grandit lentementBonjour, j'aimerais vous poser une question. ⋅self.prices 15 prix historiques de transaction pré-remplis, puis remplissez le prix moyen pondéré des trois premières tranches de l'ordre.

- Je ne sais pas.Je veux féliciter le rêve.

M0606Malheureusement, de nombreux négociants ont réduit le prix de vente à un seul tick, ce qui rend inutile l'idée d'intégrer une stratégie de vente à mi-chemin.

Maman, où est-ce que tu vas?Merci, j'ai écrit une version de python et je l'ai fait fonctionner sur mon simulateur, c'est vraiment un moissonneur de frais.

- Je ne sais pas.C'est génial, je n'arrive pas vraiment à comprendre les rêves sans explication, merci pour votre patience!

Je suis désolée.La division en or 0.618 0.382 est utilisée pour les Fibres. Le rêve du bétail

Je vous en prie.C'est vrai que c'est un gros troupeau de bœufs, c'est très détaillé.

Je suis Evan1987Merci de partager, je suis très heureuse d'avoir une réponse aussi détaillée.

le fait d'êtreLe rêve du bœuf

- Je vous en prie.Le rêve, le lot de vaches! Il y a des commentaires, mais cela semble très compliqué...

Neuf soleilsLe rêve, le lot de vaches!

L'inventeur de la quantification - un petit rêveJe ne sais pas si c'est vrai.

L'inventeur de la quantification - un petit rêveIl s'agit principalement d'apprendre à penser et à percevoir ces opportunités commerciales qui peuvent être très fréquentes.

L'inventeur de la quantification - un petit rêveDans le même temps, il est important de souligner que les stratégies de haute fréquence ont besoin d'un peu de soutien.

L'inventeur de la quantification - un petit rêveMerci de votre soutien. Aidez-moi à partager si vous aimez, mon cher.

L'inventeur de la quantification - un petit rêveMerci pour votre soutien!

L'inventeur de la quantification - un petit rêveMerci pour votre soutien!

L'inventeur de la quantification - un petit rêveMerci pour votre soutien!

L'inventeur de la quantification - un petit rêveJe me souviens très bien de cette proportion d'or quand j'allais à l'école, et je disais que c'était le plus beau rectangle de cette proportion. Mais je ne sais pas pourquoi.

L'inventeur de la quantification - un petit rêveMerci pour votre soutien.

L'inventeur de la quantification - un petit rêveEn fait, il n'est pas compliqué, cette note est assez compliquée, les lignes par lignes sont décrites de la manière la plus facile à comprendre.