0
Seguir
48
Seguidores

SuperTrend V.1--Sistema de línea de supertendencia

Creado el: 2020-04-20 22:10:36, Actualizado el: 2024-12-12 21:03:30
comments   28
hits   11392

SuperTrend V.1–Sistema de línea de supertendencia

1. Origen de la historia

Mi buen amigo Ran ha estado observando este indicador durante mucho tiempo y me lo recomendó antes de Año Nuevo para discutir si se puede convertir en una forma cuantitativa. Lamentablemente, sufrí de procrastinación y no lo ayudé a cumplir su deseo hasta ahora. De hecho, mi comprensión de los algoritmos ha avanzado mucho recientemente. Planeo escribir un traductor de pino algún día. Todo puede ser Python. . Bueno, sin más preámbulos, permítanos presentarle esta legendaria línea de súper tendencia. .

2. Introducción al sistema

La nueva generación del sistema de trading inteligente de CMC Markets: Supertrend Aquí hay un artículo que presenta este sistema. SuperTrend V.1–Sistema de línea de supertendencia

En la nueva generación del sistema de comercio inteligente de CMC Markets, seleccione “Super Trend Line” en los indicadores técnicos para activarlo. Como se muestra en la figura, puede ajustar el “color y el grosor” de las señales ascendentes y descendentes según sus preferencias. Entonces, ¿qué es el indicador de supertendencia? Antes de comprender la fórmula del indicador de supertendencia, es necesario comprender el ATR porque la supertendencia utiliza valores de ATR para calcular los valores del indicador.

El algoritmo principal también se presenta en la imagen siguiente. SuperTrend V.1–Sistema de línea de supertendencia

A primera vista, la descripción principal es un canal de HL2 (precio promedio de K-line) multiplicado por n veces ATR. Hacer una ruptura de tendencia. Pero el artículo es bastante breve. No existe un algoritmo detallado. Entonces pensé en la mejor comunidad Tradingview. No es sorprendente. Por supuesto que está ahí. SuperTrend V.1–Sistema de línea de supertendencia

A juzgar por el gráfico, es bastante consistente con la tendencia. Pero desafortunadamente es sólo una señal de alerta.

3. Estudia el código fuente

El código no parece demasiado largo, así que tradujémoslo y probémoslo. ! (¡Qué rico!) ¡Qué rico! SuperTrend V.1–Sistema de línea de supertendencia El código completo de pino es el anterior. .

4. Conversión de código

Aquí creamos una nueva estrategia en FMZ y la llamamos SuperTrade SuperTrend V.1–Sistema de línea de supertendencia

A continuación, establecemos dos parámetros Factor y Pd SuperTrend V.1–Sistema de línea de supertendencia

Para simplificar mejor el funcionamiento del código y hacerlo más fácil de entender, necesitamos utilizar el paquete de expansión de datos avanzado de Python.pandas

Durante el almuerzo, le pregunté al profesor Mengmeng si FMZ apoya esta biblioteca. Lo revisé por la tarde y realmente funcionaba. El maestro Mengmeng es realmente asombroso.

1. Necesitamos importar la biblioteca de tiempo de la biblioteca pandas 2. Configure el contrato trimestral en la función principal (ejecutando principalmente OKEX) 3. Establezca un bucle doTicker() para realizar la prueba una vez cada 15 minutos. Ejecute el código en un ciclo de 15 minutos A continuación escribimos la estrategia principal en doTicker().

import pandas as pd
import time

def main():
    exchange.SetContractType("quarter")
    preTime = 0
    Log(exchange.GetAccount())
    while True:
        records = exchange.GetRecords(PERIOD_M15)
        if records and records[-2].Time > preTime:
            preTime = records[-2].Time
            doTicker(records[:-1])
        Sleep(1000 *60)
        

4. Necesitamos recuperar el OHCLV de la línea K, así que usemos GetRecords() 5. Importamos los datos recuperados a pandas M15 = pd.DataFrame(records) 6. Necesitamos modificar la etiqueta del encabezado de la tabla. M15.columnas =[‘time’,‘open’,‘high’,‘low’,‘close’,‘volume’,‘OpenInterest’] De hecho, solo cambia las primeras letras de ‘open’, ‘high’, ‘low’ y ‘close’ a minúsculas, de modo que sea más fácil escribir código más tarde sin alternar entre mayúsculas y minúsculas.

def doTicker(records):
    M15 = pd.DataFrame(records)
    M15.columns = ['time','open','high','low','close','volume','OpenInterest']  

7. Agregue una columna hl2 al conjunto de datos hl2=(high+low)/2

#HL2
M15['hl2']=(M15['high']+M15['low'])/2

8. A continuación, calculemos el ATR Debido a que el cálculo de ATR requiere la importación de una longitud variable, su valor es Pd

Luego nos remitimos al manual del lenguaje Mai, y los pasos del algoritmo del promedio de volatilidad real de ATR son los siguientes: TR : MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW)); ATR : RMA(TR,N)

El valor TR es la mayor de las tres diferencias siguientes. 1. La fluctuación entre el precio más alto y el precio más bajo del día de negociación actual ALTO-BAJO 2. La fluctuación entre el precio de cierre del día de negociación anterior y el precio más alto del día de negociación actual (REF(CLOSE,1)-HIGH) 3. La fluctuación entre el precio de cierre del día de negociación anterior y el precio más bajo del día de negociación actual (REF(CLOSE,1)-LOW) Entonces TR: MAX(MAX((ALTO-BAJO),ABS(REF(CERRAR,1)-ALTO)),BS(REF(CERRAR,1)-BAJO));

En los cálculos de Python

M15['prev_close']=M15['close'].shift(1)

Primero, configure un prev_close para obtener los datos de cierre en la fila anterior, es decir, mueva el cierre hacia la derecha 1 cuadrícula para formar un nuevo parámetro

ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]

A continuación, defina una variable intermedia para registrar la matriz de tres valores de comparación de TR. (ALTO-BAJO)(alto-prev_close)(bajo-prev_close)

M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)

Definimos una nueva columna denominada TR en el conjunto de datos. El valor de TR es el valor absoluto máximo de la variable intermedia. Utilizamos las funciones abs() y max().

    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

Finalmente, necesitamos calcular el valor de ATR, ATR: RMA (TR, N). Se ha descubierto que el algoritmo RMA es en realidad una variante de valor fijo del algoritmo EMA. N es la variable que importamos, donde el parámetro predeterminado para ATR es 14. Aquí importamos alfa = el recíproco de la longitud.

===

Luego use el algoritmo EWM para calcular la EMA El proceso completo de cálculo del ATR es el siguiente

    #ATR(PD)
    length=Pd
    M15['prev_close']=M15['close'].shift(1)
    ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
    M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

9 Comience a calcular Up y Dn

    M15['Up']=M15['hl2']-(Factor*M15['atr'])
    M15['Dn']=M15['hl2']+(Factor*M15['atr'])

Up=hl2 -(Factor * atr) Dn=hl2 +(Factor * atr) ¿No es sencillo?

El siguiente es el segmento de código central de las líneas 15 a 21 en TV

TrendUp=close[1]>TrendUp[1]? max(Up,TrendUp[1]) : Up
TrendDown=close[1]<TrendDown[1]? min(Dn,TrendDown[1]) : Dn

Trend = close > TrendDown[1] ? 1: close< TrendUp[1]? -1: nz(Trend[1],1)
Tsl = Trend==1? TrendUp: TrendDown

linecolor = Trend == 1 ? green : red

El propósito principal de este párrafo es expresar, Si está en una fase alcista, (línea inferior) TrendUp = max(Up,TrendUp[1]) Si está en la etapa descendente, (línea superior) TrendDown=min(Dn,TrendDown[1]) Es decir, en una tendencia, el valor ATR ha estado utilizando una técnica similar a la estrategia Bandit Bollinger. Sigue estrechando el otro lado del canal.

Aquí, cada cálculo de TrendUp y TrendDown debe iterarse automáticamente. Es decir, cada paso debe calcularse en función del paso anterior. Entonces necesitamos recorrer el conjunto de datos.

Aquí primero debemos crear nuevos campos TrendUp, TrendDown, Trend y linecolor para el conjunto de datos. Y darles un valor inicial Luego use la sintaxis fillna(0) para llenar los datos con valores nulos en los resultados calculados previamente con 0

    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)

Iniciar un bucle for Uso de operaciones ternarias de Python en bucles

    for x in range(len(M15)):

Cálculo de TrendUp TrendUp = MAX(Up,TrendUp[-1]) if close[-1]>TrendUp[-1] else Up El significado general es que si el cierre anterior > el TrendUp anterior, si es verdadero, toma el valor máximo de Up y el TrendUp anterior, si no es verdadero, toma el valor de Up y lo pasa al TrendUp actual.

        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]

De manera similar, calcule TrendDown TrendDown=min(Dn,TrendDown[-1]) if close[-1]

        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]

A continuación se muestra la bandera para calcular la dirección de control. Simplifiqué el pseudocódigo. Trend= 1 if (close > TrendDown[-1]) else (x) x = -1 if (close< TrendUp[-1]) else Trend[-1]

El significado es que si el precio de cierre > la tendencia bajista anterior, entonces tome 1 (alcista) si no, tome x Si el precio de cierre es menor que la tendencia alcista anterior, se toma -1 (posición corta). Si no, se toma la tendencia anterior (lo que significa que permanece sin cambios). Traducido al lenguaje gráfico, significa romper el riel superior para cambiar la bandera a alcista, romper el riel inferior para cambiar la bandera a bajista y los demás tiempos permanecen sin cambios.

        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]

Calcular Tsl y Linecolor Tsl= rendUp if (Trend==1) else TrendDown Tsl se utiliza para representar el valor de SuperTrend en el gráfico. Esto significa que cuando eres alcista, marca la pista inferior en el gráfico, y cuando eres bajista, marca la pista superior en el gráfico. linecolor= ‘green’ if (Trend==1) else ‘red’ El significado de linecolor es: si eres alcista, marca la línea verde; si eres bajista, marca el color vacío (se usa principalmente para la visualización de Tradingview)

        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else  'red'

Las siguientes líneas 23-30 son principalmente para trazar un gráfico, que no se explicará en detalle aquí.

Finalmente, hay 2 líneas de código para el control de señales de compra y venta. En Tradingview, significa dar una señal después de revertir la bandera. Convertir declaraciones condicionales a Python. Si la bandera de tendencia anterior cambia de -1 a 1, significa que se ha roto la resistencia superior. Abra una posición larga. Si la bandera de tendencia anterior cambia de 1 a -1, significa que se ha roto el soporte bajista. Abra una posición corta.

    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
        Log('SuperTrend V.1 Alert Long',"Create Order Buy)
    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long',"Create Order Sell)

El código completo para esta sección es el siguiente:

    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)
    
    for x in range(len(M15)):
        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
        M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else  'red'
        
    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
        Log('SuperTrend V.1 Alert Long',"Create Order Buy)
        Log('Tsl=',Tsl)
    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long',"Create Order Sell)
        Log('Tsl=',Tsl)

SuperTrend V.1–Sistema de línea de supertendencia

SuperTrend V.1–Sistema de línea de supertendencia

5. Todos los códigos

Ajusté la estructura general del código. Y fusionar las instrucciones de órdenes relacionadas largas y cortas en la estrategia. Aquí está el código completo

'''backtest
start: 2019-05-01 00:00:00
end: 2020-04-21 00:00:00
period: 15m
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
'''

import pandas as pd
import time

def main():
    exchange.SetContractType("quarter")
    preTime = 0
    Log(exchange.GetAccount())
    while True:
        records = exchange.GetRecords(PERIOD_M15)
        if records and records[-2].Time > preTime:
            preTime = records[-2].Time
            doTicker(records[:-1])
        Sleep(1000 *60)

       
def doTicker(records):
    #Log('onTick',exchange.GetTicker())
    M15 = pd.DataFrame(records)

    #Factor=3
    #Pd=7
    
    M15.columns = ['time','open','high','low','close','volume','OpenInterest']  
    
    #HL2
    M15['hl2']=(M15['high']+M15['low'])/2

    #ATR(PD)
    length=Pd
    M15['prev_close']=M15['close'].shift(1)
    ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
    M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()


    M15['Up']=M15['hl2']-(Factor*M15['atr'])
    M15['Dn']=M15['hl2']+(Factor*M15['atr'])
    
    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)

    for x in range(len(M15)):
        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
        M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'Long' if ( M15['Trend'].values[x]==1) else  'Short'
 

    linecolor=M15['linecolor'].values[-2]
    close=M15['close'].values[-2]
    Tsl=M15['Tsl'].values[-2] 


    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):

        Log('SuperTrend V.1 Alert Long','Create Order Buy')
        Log('Tsl=',Tsl)
        position = exchange.GetPosition()
        if len(position) > 0:
            Amount=position[0]["Amount"]
            exchange.SetDirection("closesell")
            exchange.Buy(_C(exchange.GetTicker).Sell*1.01, Amount);
        
        exchange.SetDirection("buy")
        exchange.Buy(_C(exchange.GetTicker).Sell*1.01, vol);

    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long','Create Order Sell')
        Log('Tsl=',Tsl)
        position = exchange.GetPosition()
        if len(position) > 0:
            Amount=position[0]["Amount"]
            exchange.SetDirection("closebuy")
            exchange.Sell(_C(exchange.GetTicker).Buy*0.99,Amount);
        exchange.SetDirection("sell")
        exchange.Sell(_C(exchange.GetTicker).Buy*0.99, vol*2);

Enlace de estrategia pública: https://www.fmz.com/strategy/200625

6. Backtesting y resumen

Seleccionamos datos del año pasado para realizar pruebas retrospectivas. Utilice el contrato trimestral de OKEX con un ciclo de 15 minutos. Los parámetros establecidos son, Factor=3 Pd=45 vol=100 (100 tickets por pedido) La rentabilidad anualizada es de aproximadamente el 33%. En general, el retroceso no es muy grande. La principal causa de esto es el accidente del 312 que tuvo un impacto significativo en el sistema. Si no existiera el 312 los rendimientos serían mejores.

SuperTrend V.1–Sistema de línea de supertendencia

6. Reflexiones finales

SuperTrend es un muy buen sistema de trading

El principio principal del sistema SuperTrend es utilizar la estrategia de ruptura del canal ATR (similar al canal Kent) Pero el cambio principal radica en el uso de la estrategia de estrechamiento de Bandit Bollinger, o el principio de Donchian inverso. Los canales superior e inferior se estrechan constantemente durante el funcionamiento del mercado. Para lograr la operación de ruptura y giro del canal. (Una vez que el canal se rompe, los rieles superior e inferior vuelven a sus valores iniciales)

Grafiqué TrendUp TrendDn por separado en TradingView Esto te ayudará a comprender mejor esta estrategia. Claro de un vistazo SuperTrend V.1–Sistema de línea de supertendencia

También hay una versión js en github. No entiendo muy bien js, pero a juzgar por la declaración if, parece haber algún problema. La dirección eshttps://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js

Finalmente encontré la versión original. Fue publicado el 29/05/2013. Escrito por Rajandran R Código C++ publicado en el foro Mt4https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4 Entiendo aproximadamente el significado de C++ y lo reescribiré cuando tenga la oportunidad.

Espero que todos puedan aprender la esencia de esto. Es difícil. ~!