
entãoConteúdo anteriorexplicar.
A terceira função adicionada:
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)
}
}
}
}
ConstrutorLeeksReaper()Ao construir um objeto, adicionebalanceAccount()A função é atualizar as informações do ativo da conta, armazenadas emself.account, isto é, o objeto construídoaccountpropriedade. Calcule e imprima o valor do lucro regularmente. Então, com base nas últimas informações de ativos da conta, a taxa de saldo da moeda spot (saldo da posição spot) é calculada. Quando o limite de desvio é acionado, uma pequena ordem é fechada para restaurar a moeda (posição) a um estado equilibrado. Aguarde um certo período de tempo para concluir a transação, então cancele todos os pedidos pendentes. A próxima rodada de execução desta função verificará o saldo novamente e fará o processamento correspondente.
Vamos analisar o código desta função linha por linha:
Primeira frasevar account = exchange.GetAccount()Ele declara uma variável localaccount, e chame a interface da API do inventorexchange.GetAccount()Função, obter os últimos dados da conta corrente e atribuí-los aaccountvariável. Então julgueaccountEsta variável, se a variável fornullSe o valor (como tempo limite, rede, exceção de interface de troca, etc.) não for obtido, ele será retornado diretamente (correspondendo aif (!account){...}aqui).
self.account = accountEsta frase é para colocar a variável localaccountAtribuído ao objeto construídoaccountAtributos são usados para registrar as informações mais recentes da conta no objeto construído.
var now = new Date().getTime()Esta declaração declara uma variável localnow, e chamar o objeto de hora e data da linguagem JavaScriptgetTime()A função retorna o registro de data e hora atual. Atribuir anowvariável.
if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {...}Este código determina se a diferença entre o registro de data e hora atual e o último registro de data e hora registrado excede o parâmetroCalcNetInterval * 1000Isto significa que desde a última atualização até agora, mais deCalcNetInterval * 1000milissegundo(CalcNetIntervalsegundos), para atingir a função de cronometrar a impressão da renda. Como o preço do primeiro lance é usado no cálculo da renda, as condições também limitamself.orderBook.Bids.length > 0Esta condição (dados de profundidade, deve haver informações de equipamento válidas na lista de ordens de compra). Quando esta condição de instrução if é acionada, a execuçãoself.preCalc = nowAtualizar a variável de registro de data e hora da receita de impressão mais recenteself.preCalcCarimbo de data/hora atualnow. As estatísticas de renda aqui usam o método de cálculo do valor líquido, o código évar net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), ou seja, converter a moeda em dinheiro (denominador) de acordo com o preço de compra atual e, em seguida, adicioná-la à quantidade de dinheiro na conta e atribuí-la à variável local declaradanet. Determine se o valor líquido total atual é consistente com o valor líquido total registrado na última vez:
if (net != self.preNet) {
self.preNet = net
LogProfit(net)
}
Se forem inconsistentes,net != self.preNetSe for verdade, usenetAtualizações variáveis são usadas para registrar as propriedades do valor líquidoself.preNet. Então imprima istonetDados do patrimônio líquido total para o gráfico da curva de lucro do robô da plataforma de negociação quantitativa do inventor (pode ser consultado no documento da API FMZLogProfitesta função).
Se a receita de impressão programada não for acionada, continue com o processo a seguir.account.Stocks(a quantidade de moedas disponíveis na conta corrente),account.Balance(O valor atual de dinheiro disponível na conta) é registrado emself.btc,self.cny. Calcular a taxa de deslocamento e atribuir o valor ao registroself.p。
self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
O algoritmo também é muito simples, que consiste em calcular o valor atual da moeda como uma porcentagem do valor líquido total da conta.
Então, como determinamos quando o saldo de dinheiro (posição) é acionado?
O autor utiliza 2 pontos percentuais acima e abaixo de 50% como buffer, e realiza o equilíbrio além do buffer, ou seja,self.p < 0.48O desvio do saldo de moedas é acionado e acredita-se que há menos moedas, então comece a comprar em uma posição no mercado e aumente o preço em 0,01 a cada vez e faça três pequenas ordens. Da mesma forma, o equilíbrio monetárioself.p > 0.52, se você acha que tem muitas moedas, você pode fazer uma pequena ordem vendendo pelo preço de abertura. Por fim, aguarde um certo tempo de acordo com as configurações dos parâmetrosSleep(BalanceTimeout)Todos os pedidos serão cancelados posteriormente.
var orders = exchange.GetOrders() # 获取当前所有挂单,存在orders变量
if (orders) { # 如果获取当前挂单数据的变量orders不为null
for (var i = 0; i < orders.length; i++) { # 循环遍历orders,逐个取消订单
if (orders[i].Id != self.tradeOrderId) {
exchange.CancelOrder(orders[i].Id) # 调用exchange.CancelOrder,根据orders[i].Id取消订单
}
}
}
A quarta função adicionada:
A parte central da estratégia, o destaque está aqui,self.poll = function() {...}A função é a lógica principal de toda a estratégia. Também falamos sobre isso no artigo anterior.main()A função começa a ser executada e entrawhileAntes do loop infinito, usamosvar reaper = LeeksReaper()Construiu um objeto colhedor de alho-poró e entãomain()Chamada de loop na funçãoreaper.poll()Esta é a função que é chamada.
self.pollA função começa a ser executada e faz algum trabalho de preparação antes de cada loop.self.numTick++Aumentar a contagem,self.updateTrades()Atualize os últimos registros de transações de mercado e calcule os dados relevantes.self.updateOrderBook()Atualize os dados do livro de pedidos e calcule os dados relacionados.self.balanceAccount()Verifique o saldo do dinheiro (posição).
var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct # 计算爆发价格
var bull = false # 声明牛市标记的变量,初始为假
var bear = false # 声明熊市标记的变量,初始为假
var tradeAmount = 0 # 声明交易数量变量,初始为0
O próximo passo é determinar se o mercado de curto prazo atual está em alta ou em baixa.
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
}
Lembre-se, no artigo anteriorself.updateOrderBook()função, na qual usamos o algoritmo de média ponderada para construir uma série temporal com a ordempricesVariedade. Três novas funções são usadas neste código_.min,_.max,sliceEssas três funções também são muito fáceis de entender.
_.min:Sua função é encontrar o menor valor na matriz de parâmetros.
_.max:Sua função é encontrar o maior valor na matriz de parâmetros.
slice: Esta função é uma função membro do objeto array do JavaScript. Sua função é interceptar uma parte do array de acordo com o índice e retorná-lo. Por exemplo:
function main() {
// index .. -8 -7 -6 -5 -4 -3 -2 -1
var arr = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Log(arr.slice(-5, -1)) // 会截取 4 ~ 1 这几个元素,返回一个新数组:[4,3,2,1]
}

As condições para julgar ursos e touros aqui são:
self.numTick > 2Para ser estabelecido, isso significa que quando uma nova rodada de preços de detecção ocorre, ela deve ser acionada após pelo menos três rodadas de detecção para evitar ser acionada no início.self.pricesOs últimos dados noself.pricesA diferença entre o preço máximo ou mínimo no intervalo anterior na matriz deve ser quebradaburstPriceEste é o preço da explosão.Se todas as condições forem atendidas, marquebulloubear,paratrue, e dartradeAmountAtribua variáveis e planeje negociações de Stud.
De acordo com o anteriorself.updateTrades()Atualizado e calculado na funçãoself.vol, para o parâmetroBurstThresholdVolDecida se deve reduzir a intensidade da negociação (reduzir o tamanho das negociações planejadas).
if (self.vol < BurstThresholdVol) {
tradeAmount *= self.vol / BurstThresholdVol // 缩减计划交易量,缩减为之前量的self.vol / BurstThresholdVol 倍
}
if (self.numTick < 5) {
tradeAmount *= 0.8 // 缩减为计划的80%
}
if (self.numTick < 10) { // 缩减为计划的80%
tradeAmount *= 0.8
}
Em seguida, determine se o sinal de negociação e o volume de negociação atendem aos requisitos:
if ((!bull && !bear) || tradeAmount < MinStock) { # 如果非牛市并且也非熊市,或者计划交易的量tradeAmount小于参数设置的最小交易量MinStock,poll函数直接返回,不做交易操作
return
}
Após o julgamento acima, executevar tradePrice = bull ? self.bidPrice : self.askPriceDefina o preço da transação de acordo com o mercado em baixa ou em alta e atribua o valor ao preço do conhecimento de embarque correspondente.
Finalmente insira umwhileA única condição para parar o loop étradeAmount >= MinStockO volume de transações planejado é menor que o volume mínimo de transações.
No loop, as ordens são colocadas com base no fato de o mercado atual estar em alta ou em baixa. E registre o ID do pedido na variávelorderId. Após cada rodada de pedidosSleep(200)Aguarde 200 milissegundos. Então julgue no looporderIdÉ verdade (se o pedido falhar, o ID do pedido não será retornado e a condição if não será acionada), se a condição for verdadeira. Obtenha o ID do pedido e atribua-o aself.tradeOrderId。
Declarar uma variável para armazenar dados do pedidoorderO valor inicial énull. Em seguida, faça um loop para obter os dados do pedido deste ID e determine se o pedido está em status de pedido pendente. Se estiver em status de pedido pendente, cancele o pedido deste ID. Se não estiver em status de pedido pendente, pule para fora deste circuito de detecção.
var order = null // 声明一个变量用于保存订单数据
while (true) { // 一个while循环
order = exchange.GetOrder(orderId) // 调用GetOrder查询订单ID为 orderId的订单数据
if (order) { // 如果查询到订单数据,查询失败order为null,不会触发当前if条件
if (order.Status == ORDER_STATE_PENDING) { // 判断订单状态是不是正在挂单中
exchange.CancelOrder(orderId) // 如果当前正在挂单,取消该订单
Sleep(200)
} else { // 否则执行break跳出当前while循环
break
}
}
}
Em seguida, siga o processo abaixo:
self.tradeOrderId = 0 // 重置self.tradeOrderId
tradeAmount -= order.DealAmount // 更新tradeAmount,减去提单的订单已经成交的数量
tradeAmount *= 0.9 // 减小下单力度
if (order.Status == ORDER_STATE_CANCELED) { // 如果订单已经是取消了
self.updateOrderBook() // 更新订单薄等数据
while (bull && self.bidPrice - tradePrice > 0.1) { // 牛市时,更新后的提单价格超过当前交易价格0.1就减小交易力度,略微调整交易价格
tradeAmount *= 0.99
tradePrice += 0.1
}
while (bear && self.askPrice - tradePrice < -0.1) { // 熊市时,更新后的提单价格超过当前交易价格0.1就减小交易力度,略微调整交易价格
tradeAmount *= 0.99
tradePrice -= 0.1
}
}
Quando o fluxo do programa salta para forawhile (tradeAmount >= MinStock) {...}Este ciclo indica que o processo de transação de explosão de preços foi concluído.
implementarself.numTick = 0, isto é, redefinirself.numTické 0.
LeeksReaper()O construtor finalmente executaselfO objeto retornado évar reaper = LeeksReaper()Quando foi devolvido areaper。
Até aquiLeeksReaper()Analisamos como o construtor constrói o objeto leek harvester, os vários métodos do objeto leek harvester e o processo de execução das principais funções lógicas. Acredito que depois de ler este artigo, você deve ter uma compreensão mais clara da alta frequência processo de algoritmo de estratégia. entender.