Discusiones recientes en el grupo de WeChat de inventores cuantificadosprint money
En el caso de los robots, una estrategia muy antigua está volviendo a la vista de los amantes de los robots:La cosechadora de colza。
print money
El principio de negociación del robot se basó en la estrategia de la cosechadora de colza, y culpó a sí mismo por no tener una visión clara de la estrategia de la cosechadora de colza. Por lo tanto, volvió a ver seriamente la estrategia original y vio una versión de trasplante cuantificada por el inventor.Transplante OKCoin para cosechadora de colza¿Qué es esto?
La estrategia de la cosechadora de espinacas, cuantificada por el inventor, es analizada y explorada para que los usuarios de la plataforma aprendan la estrategia.
En este artículo analizamos más desde el punto de vista de la estrategia, la intención, etc., tratando de reducir al máximo el contenido aburrido relacionado con la programación.
[Transportación de OKCoin para cosechadoras de colza]
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)
}
}
En general, cuando se aprende una política, al leerla, primero hay que echar un vistazo a la estructura general del programa. La política no tiene mucho código, solo menos de 200 líneas de código, puede decirse que es muy delgada y tiene un rendimiento de la política muy alto para la versión original, básicamente la misma.main()
La función se ejecuta, el código de la política de extensión, exceptomain()
Es un proyecto llamadoLeeksReaper()
Así que esto es una función.LeeksReaper()
También se entiende bien la función que puede entenderse como la función constructora de un módulo lógico de estrategia de cosechadora de colza (un objeto), simplemente.LeeksReaper()
Es el responsable de construir la lógica de transacción de una cosechadora de colza.
Las palabras clave:
La estrategiamain
La primera línea de la función:var reaper = LeeksReaper()
El código declara una variable local.reaper
La función LeeksReaper (), entonces, construye un objeto lógico de la política, que asigna un valor a la función.reaper
。
La estrategiamain
La función sigue así:
while (true) {
reaper.poll()
Sleep(TickInterval)
}
Entrar en unawhile
El ciclo de la muerte, la ejecución continuareaper
Función de procesamiento de objetospoll()
,poll()
La función es la lógica principal de la estrategia de transacción, y el proceso de estrategia comienza con la lógica de ejecución continua de la transacción.
En cuanto aSleep(TickInterval)
Esta línea es bien entendida para controlar el tiempo de pausa después de cada ejecución de la lógica de transacción global, con el fin de controlar la frecuencia de rotación de la lógica de transacción.
LeeksReaper()
Construcción de funciones¿Qué es esto?LeeksReaper()
¿Cómo construye una función un objeto lógico estratégico?
LeeksReaper()
La función comienza y declara un objeto vacío.var self = {}
¿Qué es esto?LeeksReaper()
El proceso de ejecución de la función añade gradualmente algunos métodos, propiedades a este objeto vacío, finalmente termina la construcción de este objeto, y finalmente devuelve este objeto (es decir,main()
En el interior de la función.var reaper = LeeksReaper()
En este paso, el objeto devuelto se asigna.reaper
)。
self
Objeto añadidoAhora, a continuación.self
Se han añadido muchas propiedades, y a continuación describo cada una de ellas para comprender rápidamente las propiedades, el uso, la intención y la estrategia de las variables, evitando ver el montón de código envuelto en una nube de niebla.
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étodos para agregar objetosY después de añadir estas propiedades a self, comenzamos a darleself
Objetos que añaden métodos para que este objeto pueda hacer algunas tareas y tener algunas funciones.
La primera función añadida es:
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
El papel de esta función es obtener datos de transacciones de mercado más recientes y realizar cálculos y registros basados en los datos, que se utilizan en la lógica de seguimiento de la estrategia.
Los comentarios en línea los escribí directamente en el código de arriba.
En cuanto a_.reduce
Los estudiantes que no tienen conocimientos de programación pueden estar confundidos, pero aquí se trata de una simple explicación._.reduce
Sí, es cierto.Underscore.jsLa función de esta biblioteca, la política FMZJS, la apoya, por lo que es muy fácil de usar para el cálculo iterativo.Underscore.js资料链接
El significado es muy sencillo, por ejemplo:
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
}
Es decir, el conjunto.[1, 2, 3, 4]
Cada uno de los números se suma.trades
La suma de los valores de cada registro de transacciones en el conjunto de datos es la suma total de los registros de transacciones más recientes.self.vol = 0.7 * self.vol + 0.3 * _.reduce(...)
Por favor, permítanme usarlo....
En lugar de ese montón de código.self.vol
El cálculo de la tasa de cambio también es un promedio ponderado; es decir, la transacción más reciente tiene un peso del 30% del total de transacciones y la transacción resultante del último cálculo ponderado tiene un peso del 70%; esta proporción es establecida por el autor de la estrategia y puede estar relacionada con la observación de la ley del mercado.
En cuanto a lo que me preguntas, ¿qué pasa si la interfaz que obtiene los datos de transacciones recientes me devuelve datos antiguos que se repiten, y los datos que obtengo son erróneos y tienen sentido?
if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
...
}
Este juicio puede basarse en el ID de transacción en el registro de transacciones, y solo se activa el acumulador cuando el ID es mayor que el ID registrado la última vez, o si la interfaz del exchange no proporciona el ID.trade.Id == 0
En el caso de las transacciones, el valor de las transacciones se calcula a partir de la fecha en la que se realizan.self.lastTradeId
Lo que se almacena es el timestamp de los registros de transacciones, no el ID.
La segunda función añadida es:
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))
}
¿Qué es lo que está pasando?updateOrderBook
Esta función, como se puede ver en el significado literal del nombre de la función, tiene el papel de actualizar la orden fina. Sí, pero no solo actualizar la orden fina. La función comienza a llamar a la función API de FMZ.GetDepth()
Obtener los datos de las órdenes delgadas en el mercado actual (vender una... vender n, comprar una... comprar n) y registrar los datos delgadas enself.orderBook
En segundo lugar, si la orden es de datos bajos, la orden de compra o de venta es inferior a 3 filas, la función de invalididad se devuelve directamente.
Después, se hicieron dos cálculos:
Calculación del precio de compra El precio del recibo también se calcula utilizando una media ponderada, para calcular el pago, el derecho de compra es más importante que el 61.8% ((0.618), y el de venta representa el 38.2% del peso restante ((0.382) El precio de venta y venta es el mismo, y el precio de venta y venta es más importante. Por qué es 0.618, es posible que el autor prefiera la proporción de separación de oro. El precio de un punto más y menos en el último punto ((0.01) es para desviarse un poco más hacia el centro de la factura.
Actualización de la orden en la secuencia de tiempo de las primeras tres filas con precio promedio ponderado Para los primeros tres grados de compra y venta de pedidos, el precio de venta se calcula en promedio ponderado, el peso del primer grado es de 0.7, el peso del segundo de 0.2, el peso del tercero de 0.1. En el caso de los países en vías de desarrollo, la mayoría de los países en vías de desarrollo están en vías de desarrollo.
(买一 + 卖一) * 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 de aquí se puede ver que el precio final calculado es en realidad la posición de los precios de los tres intermediarios de mercado en el mercado anterior.
Y luego, con este precio calculado, actualiza.self.prices
El conjunto, sacando un dato más antiguo (((shift()
Función), actualizar a un dato más reciente ((porpush()
Las funciones, shift, push son métodos de objetos de la matriz del lenguaje JS, que pueden consultarse en la base de datos de JS).self.prices
Un conjunto es un flujo de datos con una secuencia de tiempo.
Cough, cough, drink, before dissection, here, we'll see you next time, hasta luego, hasta luego, hasta luego.
El yo que se hace más fuerteMe gustaría preguntarle. self.prices preencha los 15 precios de transacción históricos, y luego rellena los primeros tres precios promedio ponderados.
- ¿ Qué es eso?Quiero darle un saludo a Dream.
M0606Lamentablemente, muchos comerciantes de los mercados han reducido el precio de compra y venta a un solo tick, por lo que no tiene sentido insertar en la estrategia la operación de compra y venta en el medio.
Mamá.Gracias, escribí una versión de Python que se ejecutó en el casco, ¡qué cosechadora de tareas!
¿Qué es eso?¡Qué maravilloso, no entiendo completamente sin una explicación completa de los sueños, gracias a la paciencia de Dafu!
¿ Qué quieres decir?Se utiliza Fibonacci para dividir el oro en 0.618 0.382 Los sueños de las vacas
¿ Qué es eso?Es cierto que las vacas son muy detalladas.
Eván1987Muchas gracias por la respuesta tan detallada.
¿Qué es eso?El sueño de las vacas p
el mismo¡El sueño es todo, las vacas! Aunque con comentarios, parece muy complicado...
Nueve soles¡Dream total, mucho ganado!
Los inventores cuantifican - sueños pequeñosSí, es cierto.
Los inventores cuantifican - sueños pequeñosLa idea principal es aprender ideas y tener una idea de las oportunidades de comercio que pueden ser de alta frecuencia.
Los inventores cuantifican - sueños pequeñosSin importar, puedes leer el artículo de análisis de principios de estrategia escrito por Grasshan, la estrategia de alta frecuencia necesita algo de apoyo.
Los inventores cuantifican - sueños pequeñosGracias por el apoyo. Si te gusta, ayúdame a compartirlo.
Los inventores cuantifican - sueños pequeños¡Gracias por el apoyo!
Los inventores cuantifican - sueños pequeños¡Gracias por el apoyo!
Los inventores cuantifican - sueños pequeños¡Gracias por el apoyo!
Los inventores cuantifican - sueños pequeñosCuando entré a la escuela, aprendí esta proporción de división de oro y me acordé muy bien de que era el rectángulo más hermoso pero no sé por qué.
Los inventores cuantifican - sueños pequeñosGracias por su apoyo.
Los inventores cuantifican - sueños pequeñosEn realidad, no es complicado, este comentario es más complicado y se trata de describirlo en la forma más fácil posible.