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

El autor:No lo sé., Creado: 2022-04-26 15:57:02, Actualizado: 2022-04-26 15:57:53

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

Continuemos con elcontenido de la última vezpara explicarlo.

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("start to balance", 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("start to balance", 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)
                }
            }
        }
    }

Cuando el constructorLeeksReaper()está construyendo un objeto, elbalanceAccount()La función añadida al objeto se utiliza para actualizar la información del activo de la cuenta, que se almacena enself.account, es decir, para construir el atributoaccountCalcular y imprimir el valor de retorno regularmente. Luego, de acuerdo con la última información de activos de la cuenta, se calcula la relación de saldo de los símbolos de moneda al contado (saldo de posición al contado), y cuando se activa el umbral de compensación, se cierran pequeñas órdenes para hacer que los símbolos (posiciones) vuelvan a un estado de equilibrio. Espera un cierto período de tiempo para ejecutar la negociación, y luego cancela todas las órdenes pendientes, y ejecuta la función en la siguiente ronda, el saldo se detectará nuevamente y se realizará el procesamiento correspondiente.

Veamos el código de esta declaración de la función por declaración: En primer lugar, la primera afirmaciónvar account = exchange.GetAccount()declara una variable localaccount, llama elexchange.GetAccount()función en la interfaz FMZ API, obtener los datos más recientes de la cuenta corriente y asignarlo a la variableaccountEntonces, juzgue la variable.account; si el valor de la variable esnull(que sucederá cuando no obtiene la variable, tales como tiempo de espera, red, excepción de la interfaz de la plataforma, etc.), se volverá directamente (correspondiente aif (!account ){...}aquí).

La declaraciónself.account = accountes asignar la variable localaccountal atributoaccountdel objeto construido para registrar la última información de la cuenta en el objeto construido.

La declaraciónvar now = new Date().getTime()declara una variable localnow, y llama a lagetTime()función del objeto de tiempo y fecha del lenguaje JavaScript para devolver la marca de tiempo actual, y asignar la marca de tiempo a la variablenow.

El código:if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {...}juzga la diferencia entre la marca de tiempo actual y la última marca de tiempo registrada; si el valor excede el parámetroCalcNetInterval * 1000, significa que ha excedidoCalcNetInterval * 1000el número de milisegundos (CalcNetIntervalEn el caso de los precios de compra, el precio de compra en el mercado debe utilizarse para calcular el beneficio, la condición también se limita a la condición de que el precio de compra en el mercado sea el mismo que el precio de compra.self.orderBook.Bids.length > 0(datos de profundidad, que deben ser válidos en la lista de órdenes de compra como información de nivel).

Cuando se activa la condición de la instrucción if, ejecutarself.preCalc = nowpara actualizar la variable de marca de tiempoself.preCalcde la última utilidad impresa hasta la fecha de fecha y hora en cursonowAquí, las estadísticas de beneficios utilizan el método de cálculo del valor neto, el código es:var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), es decir, convertir la moneda en activo (moneda de cotización) de acuerdo con el precio de compra 1 actual, y luego sumarlo junto con el importe del activo en la cuenta y asignarlo a la variable local declaradanetDetermine si el valor neto total actual es coherente con el último valor neto total registrado:

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

Si es inconsistente, es decir.net != self.preNetes verdadero, actualizar el atributoself.preNetque registra el valor neto con elnetLuego, imprima los datos de valor neto totalneta la curva de ganancias del bot de la plataforma de negociación FMZ Quant (se puede consultar elLogProfitfunción en la documentación de la API de la FMZ).

Si no se activa la impresión regular de la declaración, continúe el siguiente proceso:account.Stocks(los símbolos de moneda disponibles actualmente en la cuenta) yaccount.Balance(los activos disponibles en la cuenta) enself.btcyself.cny. Calcular la proporción de desplazamiento y asignarla, que se registra enself.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 simple, que es calcular el porcentaje del valor de la moneda actual en el valor neto total de la cuenta.

Entonces, ¿cómo juzgar cuando el saldo de la moneda (posición) se activa? Aquí el desarrollador utiliza el 50% hacia arriba y hacia abajo 2 puntos porcentuales como búfer; si supera el búfer, ejecutar el saldo, es decir, cuandoself.p < 0.48Si usted piensa que la cantidad de moneda es pequeña, cada vez que el precio aumenta en 0.01, colocar tres órdenes pequeñas.self.p > 0.52, si usted piensa que la cantidad de moneda es grande, espera pequeñas órdenes de vender 1 precio en el mercado.Sleep(BalanceTimeout), y cancelar todos los pedidos.

        var orders = exchange.GetOrders()                  # obtain all the current pending orders, and save them in the variable orders"
        if (orders) {                                      # if the variable "orders", which obtains all the current pending orders, is not null
            for (var i = 0; i < orders.length; i++) {      # use the loop to traverse "orders", and cancel the orders one by one 
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)     # call "exchange.CancelOrder", and cancel orders by "orders[i].Id"
                }
            }
        }

Cuarta función añadida:

Aquí viene la parte central de la estrategia, el punto culminante.self.poll = function() {...}En el artículo anterior también hablamos de ello.main( )Función, comienza a ejecutar; antes de entrar en elwhileel bucle infinito, usamosvar reaper = LeeksReaper()para construir el objeto cosechador de ganancias, y luegoReaper.poll()se llama cíclicamente en elmain() function.

Elself.pollLa función comienza a ejecutarse, y hace algunas preparaciones antes de cada bucle;self.numTick++incrementa el recuento;self.updateTrades()actualizará los registros de operaciones recientes en el mercado y calculará los datos correspondientes utilizados;self.updateOrderBook()actualizará los datos del mercado (cartera de pedidos) y calculará los datos pertinentes;self.balanceAccount()comprueba el saldo de la divisa (posición).

        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct   # calculate the burst price 
        var bull = false             # declare the variable marked by the bull market; the initial value is false
        var bear = false             # declare the variable marked by the bear market; the initial value is false
        var tradeAmount = 0          # declare the variable of trading amount; the initial value is 0

A continuación, tenemos que juzgar si el mercado actual a corto plazo es un toro o un oso.

        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
        }

¿Recuerdas elself.updateOrderBook()función en el artículo anterior, en el que utilizamos el algoritmo de promedio ponderado para construir una serie de tiempopricesEsta pieza de código utiliza tres nuevas funciones, a saber,_.min, _.max, slice, que también son muy fáciles de entender.

  • _.min: La función es encontrar el mínimo en la matriz de parámetros.

  • _.max: La función es encontrar el máximo en la matriz de parámetros.

  • slice: Esta función es una función miembro del objeto de matriz JavaScript. Intercepta y devuelve una parte de la matriz de acuerdo con el índice. 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))    // it will intercept several elements from 4 to 1, and return a new array: [4,3,2,1]
    }
    

Aquí, las condiciones para juzgar si se trata de un mercado alcista o de un mercado bajista son:

  • self.numTick > 2debe ser verdadero, es decir, si la explosión de precios ocurre en una nueva ronda de detección, tiene que ser desencadenada después de al menos tres rondas de detección, y evitar el desencadenamiento al principio.
  • Los últimos datos de la serie de preciosself.prices, es decir, la diferencia entre los datos más recientes y el precio máximo o mínimo en el mercado.self.pricesla matriz en el rango anterior debe romper a través delburstPrice .

Si todas las condiciones son ciertas, marquebullo bienbearcomotrue, y asignar un valor a la variabletradeAmount, y planea un intercambio de caballos.

Entonces, para el parámetroBurstThresholdVol, basado en elself.volSe han actualizado y calculado en el último año.self.updateTrades()En el caso de las operaciones de negociación, se toma la decisión de reducir la intensidad de las operaciones (reducir el volumen de operaciones previsto).

        if (self.vol < BurstThresholdVol) {
            tradeAmount *= self.vol / BurstThresholdVol   // reduce the planned trading volume, and reduce it to the previous volume multiplied by "self.vol / BurstThresholdVol" 
        }
        
        if (self.numTick < 5) {
            tradeAmount *= 0.8      // reduced to 80% of the plan 
        }
        
        if (self.numTick < 10) {    // reduced to 80% of the plan
            tradeAmount *= 0.8
        }

A continuación, juzgue si la señal de negociación y el volumen de negociación cumplen los requisitos:

        if ((!bull && !bear) || tradeAmount < MinStock) {   # if it is not a bull market nor a bear market, or the planned trading volume "tradeAmount" is less than the minimum trading volume "MinStock" set by the parameter, the "poll" function returns directly without any trading operation
            return
        }

Después de la sentencia anterior, ejecutarvar tradePrice = bull ? self.bidPrice : self.askPriceEn función de si se trata de un mercado bajista o de un mercado alcista, establecer el precio de negociación y asignar el valor con el precio de la orden de entrega correspondiente.

Por último, introduzca unawhileel bucle; la única condición de parada y ruptura del bucle estradeAmount >= MinStock, es decir, el volumen de operaciones previsto es inferior al volumen mínimo de operaciones. En el bucle, de acuerdo con el estado actual del mercado alcista o el estado del mercado bajista, ejecutar la orden.orderId. ejecutarSleep(200)El bucle entonces juzga siorderIdes verdadero (si el pedido falla, el orden ID no será devuelto, y la condición if no se activará). Si la condición es verdadera, obtener el orden ID y asignarlo aself.tradeOrderId.

Declarar una variableorderpara almacenar los datos de pedido con el valor inicial denull. Luego, use un bucle para obtener los datos de orden con el ID, y determinar si el pedido está en el estado de orden pendiente; si está en el estado de orden pendiente, cancele el pedido con el ID; si no está en el estado de orden pendiente, se romperá del bucle de detección.

                var order = null           // declare a variable to save the order data 
                while (true) {             // a while loop 
                    order = exchange.GetOrder(orderId)    // call "GetOrder" to query the order data with the ID of  orderId
                    if (order) {                          // if the order data is queried,and the query fails, the order is null, and "if" will not be triggered  
                        if (order.Status == ORDER_STATE_PENDING) {   // judge whether the current order status is pending order
                            exchange.CancelOrder(orderId)            // if the current order status is pending order, cancel the order 
                            Sleep(200)
                        } else {                                     // if not, execute "break" to break out of the while loop 
                            break
                        }
                    }
                }

Luego, realice el siguiente proceso:

                self.tradeOrderId = 0              // reset "self.tradeOrderId"
                tradeAmount -= order.DealAmount    // update "tradeAmount", and subtract the executed amount of the orders in the delivery order 
                tradeAmount *= 0.9                 // reduce the intensity of ordering  
                if (order.Status == ORDER_STATE_CANCELED) {     // if the order is canceled 
                    self.updateOrderBook()                      // update the data, including the order book data
                    while (bull && self.bidPrice - tradePrice > 0.1) {   // in a bull market, if the updated bid price exceeds the current trading price by 0.1, reduce the trading intensity, and slightly adjust the trading price 
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    while (bear && self.askPrice - tradePrice < -0.1) {  // in a bear market, if the updated ask price exceeds the current trading price by 0.1, reduce the trading intensity, and slightly adjust the trading price 
                        tradePrice -= 0.1
                    }
                }

Cuando el flujo del programa se rompe de lawhile (tradeAmount >= MinStock) {...}Loop, significa que la ejecución del proceso de negociación de explosión de precios se completa. Ejecutarself.numTick = 0, es decir, reiniciarself.numTickhasta 0.

La última ejecución del constructorLeeksReaper()devuelve elselfObjeto, es decir, cuandovar reaper = LeeksReaper(), el objeto se devuelve areaper.

Hasta ahora, hemos analizado cómo elLeeksReaper()constructor construye este objeto cosechador de ganancias, los diversos métodos del objeto, y el proceso de ejecución de las funciones lógicas principales.


Más.