El propósito de este artículo es describir alguna experiencia en el desarrollo de estrategias, así como algunos consejos, que permitirán a los lectores comprender rápidamente el punto clave del desarrollo de estrategias comerciales.
Cuando encuentres detalles similares en algún diseño de estrategia, puedes llegar inmediatamente a una solución razonable.
Usamos la plataforma FMZ Quant como ejemplo para la explicación, las pruebas y la práctica.
Estrategia lenguaje de programación vamos a utilizar JavaScript
Para el objetivo de negociación, tomamos el mercado de activos blockchain (BTC, ETH, etc.) como nuestro objeto
Por lo general, dependiendo de la lógica de la estrategia, puede utilizar las siguientes interfaces diferentes para obtener datos de mercado, la mayoría de las lógicas de estrategia son impulsadas por datos de mercado (por supuesto, algunas estrategias no se preocupan por los datos de precios, como una estrategia de inversión fija).
Obtener citas en tiempo real.
Generalmente se utiliza para obtener rápidamente el último precio actual,
Obtener la profundidad de orden de la libreta de pedidos. Generalmente se utiliza para obtener el precio de cada capa de la profundidad de la cartera de órdenes y el tamaño de las órdenes pendientes.
GetTrade: Obtener el último registro de transacciones del mercado. Generalmente se utiliza para analizar el comportamiento del mercado en un ciclo corto de tiempo y analizar cambios microscópicos en el mercado.
GetRecords: Obtener datos de la línea K del mercado, usualmente utilizados para estrategias de seguimiento de tendencias y para calcular indicadores.
Al diseñar la estrategia, el principiante generalmente ignora los diversos errores e intuitivamente cree que los resultados de cada parte de la estrategia están establecidos.
Por ejemplo, algunas interfaces de mercado devuelven datos no ejecutados:
var depth = exchange.GetDepth()
// depth.Asks[0].Price < depth.Bids[0].Price "Selling 1" price is lower than "buying 1" price, this situation cannot exist on the market.
// Because the selling price is lower than the buying price, the order must have been executed.
// depth.Bids[n].Amount = 0 Order book buying list "nth" layer, order quantity is 0
// depth.Asks[m].Price = 0 Order book selling list "mth" layer, the order price is 0
O directamente exchange.GetDepth() devuelve un valor nulo.
En el caso de los sistemas de procesamiento de datos, el procesamiento de los datos en el sistema de procesamiento de datos es el procesamiento de los datos en el sistema de procesamiento.
La forma normal de manejar las fallas es desechar los datos y recuperarlos.
Por ejemplo:
function main () {
while (true) {
onTick()
Sleep(500)
}
}
function GetTicker () {
while (true) {
var ticker = exchange.GetTicker()
if (ticker.Sell > ticker.Buy) { // Take the example of fault-tolerant processing that detects whether the "Selling 1" price is less than the "Buying 1" price.
// Exclude this error, the current function returns "ticker".
Return ticker
}
Sleep(500)
}
}
function onTick () {
var ticker = GetTicker() // Make sure the "ticker" you get doesn't exist the situation that "Selling 1" price is less than the "Buying 1" price.
// ... specific strategy logic
}
Un enfoque similar puede utilizarse para otros procesos previsibles de tolerancia a fallos.
El principio de diseño es que nunca puedes usar la lógica equivocada para conducir la lógica estratégica.
Adquisición de datos de línea K, llamada:
var r = exchange.GetRecords()
Los datos de línea K obtenidos son una matriz, como esta:
[
{"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
{"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
...
{"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]
Puedes ver que cada correa rizada contiene tiempo, precio de apertura, precio más alto, precio más bajo, precio de cierre y volumen.
Esta es una barra de la línea K. Los datos generales de la línea K se utilizan para calcular indicadores como promedios móviles, MACD y así sucesivamente.
Los datos de la línea K se pasan como un parámetro (datos de materia prima), y luego los parámetros del indicador se establecen para calcular la función de los datos del indicador, que llamamos función del indicador.
Hay muchas funciones de indicadores en la plataforma de negociación cuantitativa FMZ Quant.
Por ejemplo, calculamos el indicador de la media móvil. De acuerdo con el ciclo de los datos de la línea K, calculamos el promedio móvil del ciclo correspondiente.
Por ejemplo, los datos de la línea K que pasan (una barra de la línea K representa un día), calcula la línea promedio diaria, lo mismo, si los datos de la línea K de la función del indicador de la media de paso es un ciclo de 1 hora, entonces el indicador calculado es la media móvil de 1 hora.
Por lo general, a menudo ignoramos un problema al calcular el indicador.
var r = exchange.GetRecords(PERIOD_D1) // Pass parameters to the "GetRecords" function "PERIOD_D1" specifies the day K line to be acquired.
// Specific function using method can be seen at: https://www.fmz.com/api#GetRecords
Con los datos diarios de la línea K, podemos calcular el indicador de la media móvil. si queremos calcular la media móvil de 5 días, entonces tenemos que establecer el parámetro del indicador de la función del indicador a 5.
var ma = TA.MA(r, 5) // "TA.MA()" is the indicator function used to calculate the moving average indicator. The first parameter sets the daily K-line data r just obtained.
// The second parameter is set to 5. The calculated 5-day moving average is the same as the other indicators.
Hemos pasado por alto un problema potencial. si el número de K barra de línea en los datos K-línea es menor de 5, ¿qué podemos hacer para calcular un medio móvil válido de 5 días?
La respuesta es que no puedes hacer nada.
Porque el indicador de la media móvil es el promedio de los precios de cierre de un cierto número de barras de la línea K.
Por consiguiente, antes de utilizar los datos de la línea K y la función del indicador para calcular los datos del indicador, es necesario determinar si el número de barras de la línea K en los datos de la línea K cumple las condiciones para el cálculo del indicador (parámetros del indicador).
Así que antes de calcular el promedio móvil de 5 días, usted tiene que comprobarlo primero.
function CalcMA () {
var r = _C(exchange.GetRecords, PERIOD_D1) // _C() is a fault-tolerant function, the purpose is to avoid r being null, you can get more information at: https://www.fmz.com/api#_C
if (r.length > 5) {
Return TA.MA(r, 5) // Calculate the moving average data with the moving average indicator function "TA.MA", return it as a function return value.
}
Return false
}
function main () {
var ma = CalcMA()
Log(ma)
}
Muestra de pruebas posteriores:
[null,null,null,null,4228.7,4402.9400000000005, ... ]
Puedes ver el indicador de la media móvil de 5 días calculado. Los primeros cuatro son nulos, porque el número de barras de la línea K es menor que 5, y el promedio no se puede calcular. Cuando llegues a la 5a barra de la línea K, puedes calcularlo.
Cuando escribimos la estrategia, a menudo tenemos tal escenario, como la estrategia necesita procesar algunas operaciones cuando cada ciclo de línea K se completa, o imprimir algunos registros.
¿Cómo implementamos estas funciones? Para principiantes que no tienen experiencia en programación, puede ser un problema problemático. Aquí le damos las soluciones.
Cómo juzgar que un ciclo de barras de K-línea se ha completado. Podemos comenzar con el atributo de tiempo en los datos de K-línea. Cada vez que obtenemos los datos de K-línea, juzgaremos si el atributo de tiempo de la última barra de K-línea de estos datos de K-línea cambia o no. Si se cambia, significa que se genera una nueva barra de K-línea (probando que se ha completado el ciclo de barra de K-línea anterior de la barra de K-línea recién generada), si no hay cambio, significa que no se genera una nueva barra de K-línea (el último ciclo de barra de K-línea actual aún no se ha completado).
Así que necesitamos una variable para registrar el tiempo de la última barra de línea K de los datos de línea K.
var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // "lastTime" used to record the last K-line bar time.
En la práctica, este es generalmente el caso:
function main () {
var lastTime = 0
while (true) {
var r = _C(exchange.GetRecords)
if (r[r.length - 1].Time != lastTime) {
Log ("New K-line bar generated")
lastTime = r[r.length - 1].Time // Be sure to update "lastTime", this is crucial.
// ... other processing logic
// ...
}
Sleep(500)
}
}
Se puede ver que en el backtest, el ciclo de la línea K se establece en el diario (el parámetro no se especifica cuando elexchange.GetRecords
Cuando se llama la función, y el ciclo de línea K establecido de acuerdo con la prueba de retroceso es el parámetro predeterminado).
Si desea tener una cierta visualización o control sobre el tiempo que tarda la estrategia en acceder a la interfaz de intercambio, puede usar el siguiente código:
function main () {
while (true) {
var beginTime = new Date().getTime()
var ticker = exchange.GetTicker()
var endTime = new Date().getTime()
LogStatus(_D(), "GetTicker() function time-consuming:", endTime - beginTime, "millisecond")
Sleep(1000)
}
}
En pocas palabras, la marca de tiempo registrada después de llamar a laGetTicker
La función se resta de la marca de tiempo antes de la llamada, y se calcula el número de milisegundos experimentados, es decir, el tiempo que tarda elGetTicker
función desde la ejecución hasta la devolución.
Por ejemplo, en el proceso de colocación de una orden de venta, el importe de la orden de venta no debe ser mayor que el número de monedas en la cuenta. Porque si es mayor que el número de monedas disponibles en la cuenta, el pedido causará errores.
Lo controlamos así:
Por ejemplo, planeamos vender en corto 0.2 monedas.
var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)
Esto garantiza que el número de pedidos realizados no exceda el número de monedas disponibles en la cuenta.
Por la misma razón,Math.max
se utiliza para asegurar el límite inferior de un valor.
Generalmente, el intercambio normal tiene un límite mínimo de envío de pedidos para ciertos pares comerciales. Si es inferior a la cantidad mínima, el pedido será rechazado. Esto también causará el fracaso del programa.
Suponiendo que el BTC normalmente tiene una cantidad mínima de orden de colocación de 0,01.
Las estrategias de negociación a veces pueden dar lugar a menos de 0,01 cantidades de orden, por lo que podemos utilizarMath.max
para garantizar la cantidad mínima de pedido.
La precisión se puede controlar mediante el_N()
la función o elSetPrecision
function.
ElSetPrecision()
La función solo necesita ser establecida una vez, y el número de decimales en la cantidad de orden y el valor del precio se truncan automáticamente en el sistema.
El_N()
La función es realizar el truncamiento de puntos decimales (control de precisión) para un cierto valor.
Por ejemplo:
var pi = _N(3.141592653, 2)
Log(pi)
El valor de pi es truncado por el lugar decimal, y 2 lugares decimales están reservados, que es: 3.14
Consulte la documentación de la API para más detalles.
Puede utilizar un mecanismo de este tipo para utilizar el método de detección de marca de tiempo para determinar la marca de tiempo actual menos la marca de tiempo de la última vez que se ejecutó la tarea programada, y calcular el tiempo transcurrido en tiempo real. Cuando el tiempo transcurrido excede una cierta duración de tiempo establecida. Después de eso, se realiza una nueva operación.
Por ejemplo, utilizado en una estrategia de inversión fija.
var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // number of milliseconds a day
function main () {
while (true) {
var nowTime = new Date().getTime()
if (nowTime - lastActTime > waitTime) {
Log ("Execution Fixed")
// ... specific fixed investment operation, buying operation.
lastActTime = nowTime
}
Sleep(500)
}
}
Este es un ejemplo simple.
Utilizando el FMZ Quant_G()
Función, y saliendo de la función de guardar, es conveniente diseñar una estrategia para salir del progreso de almacenamiento y reiniciar el estado de recuperación automática.
var hold = {
Price : 0,
Amount : 0,
}
function main () {
if (_G("hold")) {
var ret = _G("hold")
hold.price = ret.price
hold.amount = ret.amount
Log("restore hold:", hold)
}
var count = 1
while (true) {
// ... strategy logic
// ... In the strategy operation, it is possible that when opening a position, then assign the position price of the open position to "hold.price", and the amount of open positions is assigned to "hold.amount" to record the position information.
hold.price = count++ // simulate some values
hold.amount = count/10 // Simulate some values
Sleep(500)
}
}
function onexit () { // Click the stop button on the robot to trigger the execution of this function. After the execution, the robot stops.
_G("hold", hold)
Log("save hold:", JSON.stringify(hold))
}
Se puede ver que los datos en elhold
objeto se guarda cada vez que el robot se detiene. y cuando cada vez que los datos se reinicia, los datos se lee y el valor de lahold
se restablece en el estado anterior a la parada.
Por supuesto, lo anterior es un ejemplo simple. Si se utiliza en una estrategia de negociación real, debe diseñarse de acuerdo con los datos clave que deben ser restaurados en la estrategia (generalmente son información de la cuenta, posición, valor de ganancia, dirección de negociación, etc.).
Además, también puede establecer algunas otras condiciones para restaurar.
Estos son algunos consejos para desarrollar una estrategia comercial, y espero que pueda ayudar a los principiantes!
¡El entrenamiento práctico es la forma más rápida de mejorarse a uno mismo! Les deseo a todos buena suerte.