Discussão recente no grupo de WeChat de inventores de quantidadeprint money
A discussão é muito acalorada, mas uma estratégia muito antiga está voltando a ser vista pelos visitantes:Máquina de colheita de louro。
print money
O 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)
}
}
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:
Estratégiasmain
A primeira linha da função:var reaper = LeeksReaper()
O código declara uma variável local.reaper
A função LeeksReaper () cria um objeto lógico de estratégia que atribui um valor à função.reaper
。
Estratégiasmain
A função segue:
while (true) {
reaper.poll()
Sleep(TickInterval)
}
Entrar em umwhile
O ciclo da morte, a execução incessantereaper
Funçõ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.
LeeksReaper()
Construção de funçõesVejam.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
)。
self
Objetos adicionadosA seguir:self
Muitas 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 # 记录当前收益数值
self
Método de adição de objetosDepois de adicionar essas propriedades ao self, você começa a darself
Objeto 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)
}
updateTrades
O 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_.reduce
A primeira coisa que eu quero dizer é que, para quem não tem conhecimentos de programação, pode ficar confuso._.reduce
Sim, é 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.trades
Cada 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.vol
O 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 == 0
A partir de agora, o site pode usar o cronômetro do registro de transações para julgar se a transação ocorreu.self.lastTradeId
O 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.updateOrderBook
A 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.orderBook
A 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.prices
Arrays, 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.prices
Um conjunto é um fluxo de dados com uma sequência temporal.
Tossir, beber água da boca, dissecar até aqui, até o próximo.
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.