O recurso está a ser carregado... Carregamento...

Análise estratégica da máquina de colheita de louro (1)

Autora:Inventor quantificado - sonho pequeno, Criado: 2020-11-12 22:11:32, Atualizado: 2023-09-26 21:04:43

img

Estratégias de dissecação da colheita de louro

Discussão recente no grupo de WeChat de inventores de quantidadeprint moneyA discussão é muito acalorada, mas uma estratégia muito antiga está voltando a ser vista pelos visitantes:Máquina de colheita de louroprint moneyO princípio de negociação do robô baseia-se na estratégia do colchão, culpando-se de não ter entendido bem a estratégia do colchão. Por isso, voltou a olhar seriamente para a estratégia original e para a versão transplantada quantificada pelo inventor.Transplante OKCoin para a colheita de cenouraNão, não. A estratégia da máquina de colheita de cenoura é quantificada pelo inventor, analisada e extraída para que os usuários da plataforma possam aprender a estratégia. Neste artigo, analisamos mais a partir de estratégias, intenções e outros níveis, tentando minimizar o conteúdo chato relacionado à programação.

[Transporte de OKCoin para a colheita de couve]

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)
    }
}

Estratégia de resumo

Geralmente, quando se tem uma estratégia para aprender, quando se lê, a primeira coisa a fazer é dar uma olhada na estrutura geral do programa. O código da estratégia não é muito grande, apenas menos de 200 linhas de código, pode ser considerado muito elegante, e para a versão original, a estratégia tem um alto rendimento, basicamente o mesmo.main()A função começa a ser executada, com o código da política, excetomain()O que é que isso significa?LeeksReaper()Então, se eu fizer uma funçãoLeeksReaper()A função também é bem compreendida, que pode ser entendida como uma função de construção de um módulo lógico de estratégia de colchão (um objeto), em termos simples.LeeksReaper()A empresa foi responsável pela construção da lógica de negociação de uma colheitadeira.

Palavras chave:img img

  • EstratégiasmainA primeira linha da função:var reaper = LeeksReaper()O código declara uma variável local.reaperA função LeeksReaper () cria um objeto lógico de estratégia que atribui um valor à função.reaper

  • EstratégiasmainA função segue:

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

    Entrar em umwhileO ciclo da morte, a execução incessantereaperFunções de processamento de objetospoll()poll()A função é a principal lógica da estratégia de negociação, e o processo de estratégia começa a executar a lógica de negociação incessantemente. Quanto aoSleep(TickInterval)Esta linha é bem compreendida como sendo para controlar o tempo de pausa após cada execução da lógica de negociação geral, com o objetivo de controlar a frequência de rotação da lógica de negociação.

AnáliseLeeksReaper()Construção de funções

Vejam.LeeksReaper()Como é que uma função constrói um objeto lógico estratégico?

LeeksReaper()A função começa declarando um objeto vazio.var self = {}EmLeeksReaper()O processo de execução de uma função aumenta gradualmente alguns métodos, propriedades e, finalmente, termina com a construção do objeto e, finalmente, retorna este objeto (ou seja,main()A função está dentro.var reaper = LeeksReaper()O que é que isso significa?reaper)。

Dá-me isso.selfObjetos adicionados

A seguir:selfMuitas propriedades foram adicionadas, e eu vou descrever cada uma delas abaixo, para entender rapidamente essas propriedades, o uso das variáveis, a intenção, a estratégia de fácil compreensão, evitando ver esse monte de código em meio a uma nuvem de neblina.

    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          # 记录当前收益数值

Dá-me isso.selfMétodo de adição de objetos

Depois de adicionar essas propriedades ao self, você começa a darselfObjeto adicionado método para permitir que o objeto faça algumas tarefas e tenha algumas funções.

A primeira função adicionada:

    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)

    }

updateTradesO papel desta função é obter dados de transações de mercado atualizados e fazer cálculos e registros com base nos dados, fornecendo a estratégia para uso na lógica subsequente. O que eu escrevi diretamente no código acima. Para_.reduceA primeira coisa que eu quero dizer é que, para quem não tem conhecimentos de programação, pode ficar confuso._.reduceSim, é verdade.Underscore.jsA função da biblioteca, a política FMZJS, suporta esta biblioteca, por isso é muito fácil de usar para computação iterativa.Underscore.js资料链接

O significado também é simples, por exemplo:

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
}

E isso é o conjunto de números.[1, 2, 3, 4]E então, se você somar cada um dos números, então você vai voltar para a nossa estratégia.tradesCada registro de transações no conjunto é somado para obter um total de transações dos registros de transações mais recentes.self.vol = 0.7 * self.vol + 0.3 * _.reduce(...)Permita-me usar...Em vez de um monte de código.self.volO cálculo também é uma média ponderada; isto é, o mais recente volume de transações produzido é ponderado em 30% e o último volume de transações ponderado em 70%; esta proporção é definida pelos autores da estratégia e pode ser relacionada à observação das leis do mercado. E se você me perguntar o que fazer se uma interface que obteve dados de transações recentes me devolveu dados antigos duplicados, então os dados que eu obtive estão errados e ainda são úteis?

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

Este julgamento pode ser feito com base no ID de transação no registro de transações, e o acúmulo só é desencadeado quando o ID é maior do que o ID registrado anteriormente, ou se a interface do exchange não fornecer o ID.trade.Id == 0A partir de agora, o site pode usar o cronômetro do registro de transações para julgar se a transação ocorreu.self.lastTradeIdO que é armazenado é o timestamp do registro de transações, não o ID.

A segunda função adicionada:

    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))
    }

Veja mais.updateOrderBookA função, como pode ser visto pelo significado literal do nome da função, é a função de atualizar o menu de pedidos.GetDepth()Obtenha os dados de minúsculas de pedidos do mercado atual (vendendo um...vendendo n, comprando um...comprando n) e registe os dados de minúsculas de pedidos noself.orderBookA função não válida é devolvida diretamente se a ordem tiver menos de 3 dados.

A partir daí, dois dados foram calculados:

  • Cálculo do preço da encomenda O preço do bilhete também é calculado usando a média ponderada, quando o pagamento é calculado, o direito de compra é maior: 61.8% ((0.618), o direito de venda é maior: 38.2% ((0.382)). O preço de venda e receita é o mesmo, mas o preço de venda e receita é maior. Por que é 0,618, talvez o autor prefira a proporção de divisão do ouro. Quanto ao preço final, adicionado e subtraído, o ponto (0,01) é para desviar um pouco mais do centro da lista.

  • Atualização da ordem do tempo em ordem de preço médio ponderado dos três primeiros grãos Para a compra de três primeiros grãos, o preço da venda é calculado em média, o primeiro grão é de 0,7, o segundo é de 0,2, o terceiro é de 0,1. Alguns colegas podem dizer: "Oh, não, não, o código tem 0,7, 0,2, 0,1". A partir daí, vamos começar a fazer o cálculo:

    (买一 + 卖一) * 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
    

    A partir daí, pode-se ver que o preço final calculado é na verdade a posição do preço do intermediário de três classes de negociação no mercado anterior. E então, com esse preço calculado, você pode atualizar.self.pricesArrays, extrair um dos dados mais antigos (((shift()Função), atualizar para um dado mais recente ((porpush()Funções, shift e push são métodos de objetos de arquivos da linguagem JS, que podem ser consultados em JS.self.pricesUm conjunto é um fluxo de dados com uma sequência temporal.

Tossir, beber água da boca, dissecar até aqui, até o próximo.


Mais.

O meu "eu" está a crescer lentamente.Bem-vindo, gostaria de perguntar.self.prices preencher os 15 preços de transação históricos, e depois preencher os três primeiros preços de transação ponderados.

SNLPPQuero dar um elogio ao sonho.

m0606Infelizmente, muitos comerciantes de bolsas de câmbio têm reduzido o preço de compra e venda para apenas um tick, deixando inútil essa tentativa de inserir uma estratégia de compra e venda no meio.

MamãObrigado, escrevi uma versão do python que correu em uma das moedas, que é uma máquina de processamento.

BvxiaokÉ ótimo, sem uma interpretação geral do sonho, eu realmente não entendo completamente, obrigado pela paciência da minha mãe!

Eddie, não.O ouro dividido em 0.618 0.382 é o Fibro usado O sonho de um lote de vacas

O que é isso?Afinal de contas, o que é que é uma massa de vacas?

Evan1987Muito obrigado pela resposta tão detalhada. Obrigado por compartilhar.

- O que foi?O sonho de um bovino

- Sim, sim.O que você está fazendo? O que é que ele está a fazer aqui?

Nove sóisSonho total, massa de vacas!

Inventor quantificado - sonho pequenoSim, eu sei.

Inventor quantificado - sonho pequenoA principal coisa é aprender ideias e perceber as oportunidades de negociação que podem ser de alta frequência.

Inventor quantificado - sonho pequenoA estratégia de alta frequência precisa de algum apoio.

Inventor quantificado - sonho pequenoObrigado pelo apoio. Se gostarem, ajudem a compartilhar, por favor.

Inventor quantificado - sonho pequenoObrigado pelo apoio!

Inventor quantificado - sonho pequenoObrigado pelo apoio!

Inventor quantificado - sonho pequenoObrigado pelo apoio!

Inventor quantificado - sonho pequenoNo momento em que entrou na escola, aprendeu a proporção de divisão de ouro e se lembra muito bem, dizendo que é o rectângulo mais bonito, mas não sabe por quê.

Inventor quantificado - sonho pequenoObrigado pelo apoio.

Inventor quantificado - sonho pequenoNa verdade, não é complicado, esta comparação de comentários é bastante complicada, tentando descrever linha a linha da maneira mais fácil de entender.