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 atributoaccount
Calcular 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 variableaccount
Entonces, 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 = account
es asignar la variable localaccount
al atributoaccount
del 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 * 1000
el número de milisegundos (CalcNetInterval
En 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 self.preCalc = now
para actualizar la variable de marca de tiempoself.preCalc
de la última utilidad impresa hasta la fecha de fecha y hora en cursonow
Aquí, 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 declaradanet
Determine 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.preNet
es verdadero, actualizar el atributoself.preNet
que registra el valor neto con elnet
Luego, imprima los datos de valor neto totalnet
a la curva de ganancias del bot de la plataforma de negociación FMZ Quant (se puede consultar elLogProfit
funció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.btc
yself.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.48
Si 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 elwhile
el bucle infinito, usamosvar reaper = LeeksReaper()
para construir el objeto cosechador de ganancias, y luegoReaper.poll()
se llama cíclicamente en elmain()
function.
Elself.poll
La 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 tiempoprices
Esta 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 > 2
debe 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.self.prices
, es decir, la diferencia entre los datos más recientes y el precio máximo o mínimo en el mercado.self.prices
la matriz en el rango anterior debe romper a través delburstPrice
.Si todas las condiciones son ciertas, marquebull
o bienbear
comotrue
, y asignar un valor a la variabletradeAmount
, y planea un intercambio de caballos.
Entonces, para el parámetroBurstThresholdVol
, basado en elself.vol
Se 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.askPrice
En 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 unawhile
el 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 siorderId
es verdadero (si el pedido falla, el orden ID no será devuelto, y la condición self.tradeOrderId
.
Declarar una variableorder
para 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.numTick
hasta 0.
La última ejecución del constructorLeeksReaper()
devuelve elself
Objeto, 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.