avatar of 发明者量化-小小梦 发明者量化-小小梦
Seguir Mensajes Privados
4
Seguir
1271
Seguidores

Análisis de la estrategia de la cosechadora de puerros (2)

Creado el: 2020-11-16 10:03:52, Actualizado el: 2024-12-05 22:03:01
comments   23
hits   8089

Análisis de la estrategia de la cosechadora de puerros (2)

Análisis de la estrategia de la cosechadora de puerros (2)

entoncesContenido anteriorexplicar.

La tercera función añadida:

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

ConstructorLeeksReaper()Al construir un objeto, agreguebalanceAccount()La función es actualizar la información de los activos de la cuenta, almacenada enself.account, es decir, el objeto construidoaccountpropiedad. Calcule e imprima el valor de las ganancias periódicamente. Luego, en función de la información más reciente sobre los activos de la cuenta, se calcula el ratio de saldo de la moneda al contado (saldo de la posición al contado). Cuando se activa el umbral de desviación, se cierra una pequeña orden para restablecer el equilibrio de la moneda (posición). Espere un cierto tiempo para completar la transacción y luego cancele todas las órdenes pendientes. La siguiente ronda de ejecución de esta función verificará nuevamente el saldo y realizará el procesamiento correspondiente.

Veamos el código de esta función línea por línea: Primera frasevar account = exchange.GetAccount()Declara una variable localaccount, y llama a la interfaz API del inventorexchange.GetAccount()Función, obtener los últimos datos de la cuenta corriente y asignarlos aaccountvariable. Entonces juzgaaccountEsta variable, si la variable esnullSi el valor (como tiempo de espera, red, excepción de interfaz de intercambio, etc.) no se obtiene, se devolverá directamente (correspondiente aif (!account){...}aquí).

self.account = accountEsta frase es para poner la variable localaccountAsignado al objeto construidoaccountLos atributos se utilizan para registrar la información de cuenta más reciente en el objeto construido.

var now = new Date().getTime()Esta declaración declara una variable localnow, y llamar al objeto de fecha y hora del lenguaje JavaScriptgetTime()La función devuelve la marca de tiempo actual. Asignar anowvariable.

if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {...}Este código determina si la diferencia entre la marca de tiempo actual y la última marca de tiempo registrada excede el parámetroCalcNetInterval * 1000Esto significa que desde la última actualización hasta ahora, más deCalcNetInterval * 1000milisegundo(CalcNetIntervalsegundos), para lograr la función de impresión de tiempo de los ingresos. Dado que el precio de la primera oferta se utiliza para calcular los ingresos, las condiciones también limitanself.orderBook.Bids.length > 0Esta condición (datos de profundidad, debe haber información de equipo válida en la lista de órdenes de compra). Cuando se activa esta condición de declaración if, ejecutarself.preCalc = nowActualizar la variable de marca de tiempo del ingreso de impresión más recienteself.preCalcMarca de tiempo actualnow. Las estadísticas de ingresos aquí utilizan el método de cálculo del valor neto, el código esvar net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), es decir, convertir la moneda en dinero (denominador) de acuerdo con el precio de compra actual, y luego agregarlo a la cantidad de dinero en la cuenta y asignarlo a la variable local declaradanet. Determinar si el valor neto total actual es consistente con el valor neto total registrado la última vez:

            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }

Si son inconsistentes,net != self.preNetSi es cierto, utilicenetLas actualizaciones de variables se utilizan para registrar las propiedades del valor neto.self.preNet. Luego imprime estonetDatos del patrimonio neto total en el gráfico de la curva de ganancias del robot de la plataforma de comercio cuantitativo del inventor (se pueden consultar en el documento API de FMZ)LogProfitesta función).

Si no se activan los ingresos de impresión programados, continúe con el siguiente proceso.account.Stocks(la cantidad de monedas disponibles en la cuenta corriente),account.Balance(La cantidad actual de dinero disponible en la cuenta) se registra enself.btcself.cny. Calcular la relación de desplazamiento y asignar el valor al registroself.p

self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)

El algoritmo también es muy sencillo y consiste en calcular el valor actual de la moneda como porcentaje del valor neto total de la cuenta.

Entonces, ¿cómo determinamos cuándo se activa el equilibrio de dinero (posición)? El autor utiliza 2 puntos porcentuales por encima y por debajo del 50% como colchón, y realiza un equilibrio más allá del colchón, es decir,self.p < 0.48Se activa la desviación del saldo de monedas y se cree que hay menos monedas, así que comience a comprar en una posición en el mercado y aumente el precio en 0,01 cada vez, y realice tres pedidos pequeños. De manera similar, el equilibrio monetarioself.p > 0.52Si crees que tienes demasiadas monedas, puedes realizar un pequeño pedido vendiendo al precio de apertura. Por último, espere un tiempo determinado según la configuración de los parámetros.Sleep(BalanceTimeout)Todos los pedidos serán 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取消订单
                }
            }
        }

La cuarta función añadida:

La parte central de la estrategia, lo más destacado está aquí,self.poll = function() {...}La función es la lógica principal de toda la estrategia. También hablamos de ello en el artículo anterior.main()La función comienza a ejecutarse y entrawhileAntes del bucle infinito, usamosvar reaper = LeeksReaper()Construyó un objeto recolector de puerros y luegomain()Llamada de bucle en funciónreaper.poll()Esta es la función que se llama.

self.pollLa función comienza a ejecutarse y realiza algún trabajo de preparación antes de cada bucle.self.numTick++Aumentar el conteo,self.updateTrades()Actualice los últimos registros de transacciones del mercado y calcule los datos relevantes.self.updateOrderBook()Actualice los datos de la cartera de pedidos y calcule los datos relacionados.self.balanceAccount()Verifique el saldo de dinero (posición).

        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct   # 计算爆发价格
        var bull = false             # 声明牛市标记的变量,初始为假
        var bear = false             # 声明熊市标记的变量,初始为假
        var tradeAmount = 0          # 声明交易数量变量,初始为0

El siguiente paso es determinar si el mercado actual a corto plazo es alcista o bajista.

        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
        }

Recuerde, en el artículo anteriorself.updateOrderBook()función, en la que utilizamos el algoritmo de promedio ponderado para construir una serie de tiempo con el ordenpricesFormación. En este código se utilizan tres nuevas funciones_.min_.maxsliceEstas tres funciones también son muy fáciles de entender.

  • _.min:Su función es encontrar el valor más pequeño en la matriz de parámetros.

  • _.max:Su función es encontrar el valor más grande en la matriz de parámetros.

  • slice:Esta función es una función miembro del objeto array de JavaScript. Su función es interceptar una parte del array según el índice y devolverlo. Por ejemplo:

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

Análisis de la estrategia de la cosechadora de puerros (2)

Las condiciones para juzgar a los osos y a los toros aquí son:

  • self.numTick > 2Para que esto se establezca, esto significa que cuando se produce una nueva ronda de precios de detección, esta debe activarse después de al menos tres rondas de detección para evitar que se active al principio.
  • Serie de preciosself.pricesLos últimos datos en elself.pricesSe debe dividir la diferencia entre el precio máximo o mínimo en el rango anterior de la matriz.burstPriceÉste es el precio explosivo.

Si se cumplen todas las condiciones, entonces marquebullobear,paratrue, y dartradeAmountAsignar variables y planificar operaciones de Stud.

De acuerdo con lo anteriorself.updateTrades()Actualizado y calculado en la funciónself.vol, para el parámetroBurstThresholdVolDecidir si se debe reducir la intensidad de las operaciones (reducir el tamaño de las operaciones planificadas).

        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
        }

A continuación, determine si la señal comercial y el volumen comercial cumplen los requisitos:

        if ((!bull && !bear) || tradeAmount < MinStock) {   # 如果非牛市并且也非熊市,或者计划交易的量tradeAmount小于参数设置的最小交易量MinStock,poll函数直接返回,不做交易操作
            return
        }

Después de la sentencia anterior, ejecútesevar tradePrice = bull ? self.bidPrice : self.askPriceEstablezca el precio de la transacción según sea un mercado bajista o un mercado alcista y asigne el valor con el precio del conocimiento de embarque correspondiente.

Finalmente ingrese unwhileLa única condición para detener el bucle estradeAmount >= MinStockEl volumen de transacción planificado es menor que el volumen de transacción mínimo. En el bucle, las órdenes se colocan en función de si el mercado actual es alcista o bajista. Y registre el ID del pedido en la variableorderId. Después de cada ronda de pedidosSleep(200)Espere 200 milisegundos. Entonces juzga en el circuitoorderId¿Es verdadero (si el pedido falla, no se devolverá el ID del pedido y no se activará la condición if), si la condición es verdadera? Obtenga el ID del pedido y asígnelo aself.tradeOrderId

Declarar una variable para almacenar datos de pedidosorderEl valor inicial esnull. Luego, realice un bucle para obtener los datos del pedido de este ID y determine si el pedido está en estado de pedido pendiente. Si está en estado de pedido pendiente, cancele el pedido de este ID. Si no está en estado de pedido pendiente, salga de este bucle de detección.

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

A continuación siga el proceso a continuación:

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

Cuando el flujo del programa sobresalewhile (tradeAmount >= MinStock) {...}Este ciclo indica que se ha completado el proceso de transacción de explosión de precios. implementarself.numTick = 0, es decir, reiniciarself.numTickes 0.

LeeksReaper()El constructor finalmente se ejecutaselfEl objeto devuelto esvar reaper = LeeksReaper()Cuando fue devuelto areaper

Hasta ahoraLeeksReaper()Hemos analizado cómo el constructor construye el objeto recolector de puerros, los distintos métodos del objeto recolector de puerros y el proceso de ejecución de las principales funciones lógicas. Creo que después de leer este artículo, debería tener una comprensión más clara de la alta frecuencia proceso de algoritmo de estrategia. entender.