En el último artículoEnseñar a escribir estrategias -- trasplantar una estrategia MyLanguage, una estrategia simple de MyLanguage ha sido probada para el trasplante. Si es una estrategia de MyLanguage más compleja, ¿cómo se puede trasplantar en una estrategia de lenguaje JavaScript? ¿Qué habilidades hay?
Veamos primero la estrategia para trasplantar:
(*backtest
start: 2019-05-01 00:00:00
end: 2019-11-12 00:00:00
period: 1d
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
args: [["SlideTick",10,126961],["ContractType","quarter",126961]]
*)
N1:=10;
N2:=21;
AP:=(HIGH+LOW+CLOSE)/3;
ESA:=EMA(AP,N1);
D:=EMA(ABS(AP-ESA),N1);
CI:=(AP-ESA)/(0.015*D);
TCI:=EMA(CI,N2);
WT1:TCI;
WT2:SMA(WT1,4,1);
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
El(* backtest... *)
al principio de la estrategia MyLanguage está el código de configuración para la configuración de backtesting. Para facilitar la comparación, se establece una configuración de backtesting unificada. Esta estrategia también es aleatoria, que no es demasiado compleja (más compleja que la del artículo anterior). Es una estrategia representativa. Para trasplantar una estrategia MyLanguage, primero debe mirar toda la estrategia. El código de la estrategia es conciso, y puede tener una cierta comprensión de la estrategia general. Para esta estrategia, hemos visto que varias funciones de indicadorEMA
, SMA
se han utilizado:
La EMA
La función de indicador, hay funciones de biblioteca de indicadores ya hechas disponibles directamente en la plataforma FMZ cuando se escriben estrategias en lenguaje JavaScript.TA.MA
.
La SMA
Lo que tenemos que hacer esSMA
indicador, que encontramos en la biblioteca TA de FMZ no admite la función de indicador SMA, y hay diferencias entre el indicador SMA en la biblioteca talib y el de MyLanguage.
Como podemos ver, la sección de parámetros tiene un parámetro de pesos además del parámetro de período.
La función de indicador SMA en la biblioteca talib en la documentación de la API FMZ se describe como:
Se puede ver quetalib.SMA
es un simple indicador de media móvil.
De esta manera, solo podemos implementar un SMA por nosotros mismos. Como desarrollador que usa el lenguaje JavsScript para escribir estrategias, esta también es una de las habilidades necesarias. Después de todo, si no hay una rueda lista, el programa todavía necesita ejecutarse, solo construya una.
En realidad, no hay mucha investigación sobre indicadores, etc. Generalmente, la gente busca información si no la entiende.
Parece que el proceso algorítmico de esta teoría es bastante confiable, y la implementación es la siguiente:
function SMA (arr, n, m) {
var sma = []
var currSMA = null
for (var i = 0; i < arr.length; i++) {
if (arr[i] && !isNaN(arr[i])) {
if (!currSMA) {
currSMA = arr[i]
sma.push(currSMA)
continue
}
// [M*C2+(N-M)*S1]/N
currSMA = (m * arr[i] + (n - m) * currSMA) / n
sma.push(currSMA)
} else {
sma.push(NaN)
}
}
return sma
}
El marco estratégico utiliza el mismo marco que en el artículoEnseñar a escribir estrategias -- trasplantar una estrategia MyLanguagey se llena principalmente en dos partes:
En primer lugar, hacer el procesamiento de datos del ticker y el cálculo del índice.
Tomemos esta parte de MyLanguage una oración a la vez, función por función:
AP:=(HIGH+LOW+CLOSE)/3;
Se puede entender que el precio más alto, el precio más bajo y el precio de cierre de cada BAR en los datos de la línea K deben sumarse y luego dividirse por 3 para calcular el valor medio, y luego guardarse como una matriz, correspondiente a cada BAR uno por uno. Se puede procesar de la siguiente manera:
function CalcAP (r) { // AP:=(HIGH+LOW+CLOSE)/3;
var arrAP = [] // Declare an empty array
for (var i = 0; i < r.length; i++) { // r is the incoming K-line data, which is an array, use for to traverse this array.
v = (r[i].High + r[i].Low + r[i].Close) / 3 // Calculate the average value.
arrAP.push(v) // Add to the end of the arrAP array, the end is the first when arrAP is empty.
}
return arrAP // Returns this average array, i.e., the AP calculated in the MyLanguage
}
Esta función se puede llamar en el bucle principal de la función OnTick, por ejemplo:
// Calculation of indicators
// AP
var ap = CalcAP(records)
ESA:=EMA(AP,N1);
:Aquí, vamos a utilizar los datos de AP calculados en el paso anterior para calcular el SEC. De hecho, el SEC es el
function CalcESA (ap, n1) { // ESA:=EMA(AP,N1);
if (ap.length <= n1) { // If the AP length is less than the indicator parameter, valid data cannot be calculated. At this time, let the function return false.
return false
}
return TA.EMA(ap, n1)
}
D:=EMA(ABS(AP-ESA),N1);
Utilice el cálculoAP
, ESA
para calcular los datosD
¿ Qué pasa?
Los comentarios del código aquí se pueden leer para obtener algunos consejos sobre cómo calcular los indicadores.
function CalcD (ap, esa, n1) { // D:=EMA(ABS(AP-ESA),N1);
var arrABS_APminusESA = []
if (ap.length != esa.length) {
throw "ap.length != esa.length"
}
for (var i = 0; i < ap.length; i++) {
// When calculating the value of the indicator, it is necessary to determine the validity of the data, because the first few EMA calculations may be the beginning of the array of data is NaN, or null.
// So it must be judged that the data involved in the calculation are all valid values to proceed, and if there are any invalid values, they are filled with NaN to arrABS_APminusESA.
// The data thus calculated, each position corresponds to the previous data one by one, without misalignment.
if (ap[i] && esa[i] && !isNaN(ap[i]) && !isNaN(esa[i])) {
v = Math.abs(ap[i] - esa[i]) // According to ABS(AP-ESA), the specific value is calculated and put into the arrABS_APminusESA array.
arrABS_APminusESA.push(v)
} else {
arrABS_APminusESA.push(NaN)
}
}
if (arrABS_APminusESA.length <= n1) {
return false
}
return TA.EMA(arrABS_APminusESA, n1) // Calculate the EMA indicator of the array arrABS_APminusESA and get the data D (array structure).
}
CI:=(AP-ESA)/(0.015*D);
El método de cálculo es similar al paso 1, y el código se publica directamente.function CalcCI (ap, esa, d) { // CI:=(AP-ESA)/(0.015*D);
var arrCI = []
if (ap.length != esa.length || ap.length != d.length) {
throw "ap.length != esa.length || ap.length != d.length"
}
for (var i = 0; i < ap.length; i++) {
if (ap[i] && esa[i] && d[i] && !isNaN(ap[i]) && !isNaN(esa[i]) && !isNaN(d[i])) {
v = (ap[i] - esa[i]) / (0.015 * d[i])
arrCI.push(v)
} else {
arrCI.push(NaN)
}
}
if (arrCI.length == 0) {
return false
}
return arrCI
}
function CalcTCI (ci, n2) { // TCI:=EMA(CI,N2);
if (ci.length <= n2) {
return false
}
return TA.EMA(ci, n2)
}
En este último paso, se utiliza la función SMA de la rueda que construimos antes.
function CalcWT2 (wt1) { // WT2:SMA(WT1,4,1);
if (wt1.length <= 4) {
return false
}
return SMA(wt1, 4, 1) // The SMA indicator for wt1 is calculated by using our own implementation of the SMA function.
}
La transmisión de señales comerciales es muy simple.
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
Después de leer estos códigos de MyLanguage, podemos ver que la Cruz Dorada y el Cruce Oscuro de WT1 y WT2 se utilizan como condiciones de apertura. Usando el backtest de la estrategia de MyLanguage directamente, observamos que:
Se puede ver a partir de la observación del funcionamiento real de la estrategia MyLanguage que cuando se detecta una señal en la posición de apertura, en realidad es para detectar si la posición de la barra en el punto de apertura contando 2 bares hacia adelante es una Cruz de Oro.
El código de relleno de la parte de detección de señal puede escribirse como:
if ((_State == IDLE || _State == SHORT) && wt1[wt1.length - 4] < wt2[wt2.length - 4] && wt1[wt1.length - 3] > wt2[wt2.length - 3]) {
if (_State == IDLE) {
_State = OPENLONG
Log("OPENLONG") // test
}
if (_State == SHORT) {
_State = COVERSHORT
Log("COVERSHORT") // test
}
isOK = false
}
if ((_State == IDLE || _State == LONG) && wt1[wt1.length - 4] > wt2[wt2.length - 4] && wt1[wt1.length - 3] < wt2[wt2.length - 3]) {
if (_State == IDLE) {
_State = OPENSHORT
Log("OPENSHORT") // test
}
if (_State == LONG) {
_State = COVERLONG
Log("COVERLONG") // test
}
isOK = false
}
Aquí puede pensar en por qué las instrucciones SPK y BPK de MyLanguage pueden ser implementadas con el código anterior.
Configuración de pruebas de retroceso:
Prueba de retroceso en MyLanguage
Backtest en la versión de JavaScript:
El código al comienzo de la función OnTick se utiliza para hacer la backtesting más rápida. Se utiliza para ejecutar la estrategia basada en el modelo de Bar. Si está interesado, puede analizarlo en detalle.
function OnTick(){
// The ticker processing part of the driving strategy.
var records = _C(exchange.GetRecords)
if (records[records.length - 1].Time == preTime) {
if (isOK) {
Sleep(500)
return
}
} else {
preTime = records[records.length - 1].Time
}
...
..
.
El código completo de la estrategia de enseñanza:https://www.fmz.com/strategy/174457