Estrategia cuantitativa dinámica de stop-profit y stop-loss con cruce de medias móviles dobles

EMA SMA
Fecha de creación: 2024-12-11 11:23:54 Última modificación: 2024-12-11 11:23:54
Copiar: 0 Número de Visitas: 441
1
Seguir
1617
Seguidores

Estrategia cuantitativa dinámica de stop-profit y stop-loss con cruce de medias móviles dobles

Descripción general

Se trata de una estrategia de trading cuantitativa basada en señales de cruce de dos líneas equiláreas, para juzgar la tendencia del mercado a través de cruces de promedios móviles de índices rápidos (EMA) y promedios móviles de índices lentos (EMA), mientras que se combina con un control de stop loss dinámico para administrar el riesgo. La estrategia adopta la gestión de posiciones porcentual, el uso por defecto del 10% de los fondos para operar, para proteger las ganancias y controlar el riesgo al establecer precios de stop loss y stop loss dinámicos.

Principio de estrategia

La lógica central de la estrategia es identificar los cambios de tendencia mediante la supervisión de la intersección de los promedios móviles indicativos (EMA) de 20 y 50 períodos. Cuando un EMA rápido se eleva a través de un EMA lento, el sistema genera múltiples señales. Cada vez que se abre una posición, el sistema establece automáticamente un precio de parada (STOP) y un precio de pérdida (STOP) en función del precio de entrada.

Ventajas estratégicas

  1. La estabilidad de la señal es fuerte - utiliza EMA en lugar de una simple media móvil (SMA) para responder más rápidamente a los cambios en los precios y, al mismo tiempo, puede filtrar parte del ruido del mercado.
  2. Gestión de riesgos perfecta - Se utiliza un mecanismo de stop loss dinámico, el precio de stop loss se ajusta a los cambios en el precio de entrada.
  3. La gestión de fondos es razonable - El uso de la gestión de posiciones de proporción fija evita el alto riesgo de operaciones de posición completa.
  4. Alto grado de automatización - desde la generación de señales hasta la gestión de posiciones, con una menor intervención humana.
  5. Adaptabilidad - Las estrategias pueden adaptarse a diferentes entornos de mercado y los parámetros se pueden ajustar con flexibilidad según las circunstancias reales.

Riesgo estratégico

  1. Retraso en la línea media - Aunque la EMA reacciona más rápidamente, todavía hay un cierto retraso, que puede causar un ligero retraso en el tiempo de entrada.
  2. No se aplica a los mercados convulsivos - puede haber frecuentes falsas brechas cuando los mercados se mueven horizontalmente.
  3. Detención de pérdidas con un parámetro de multiplicador fijo - El parámetro de pérdidas con un parámetro de multiplicador fijo puede no ser adecuado para todos los entornos de mercado.
  4. Riesgo de retirada - En un mercado muy volátil, un stop loss del 5% puede no ser suficiente para hacer frente a grandes fluctuaciones.

Dirección de optimización de la estrategia

  1. Introducción de indicadores de volatilidad - Se recomienda la adición de indicadores de ATR para ajustar dinámicamente el multiplicador de stop loss para adaptarlo mejor a la volatilidad del mercado.
  2. Aumento de la confirmación de tendencias - Indicadores como el RSI, el MACD pueden ser combinados para filtrar señales de comercio y aumentar la probabilidad de ganar.
  3. Optimización de la gestión de las posiciones - Se puede ajustar el tamaño de las posiciones en función de la fluctuación dinámica del mercado, para un control de riesgo más preciso.
  4. Añadir filtros de tiempo - Considere la posibilidad de agregar un límite a la ventana de tiempo de negociación para evitar períodos de mayor volatilidad.
  5. Mecanismos de frenado mejorados - permite el frenado móvil, obteniendo más ganancias si la situación continúa mejorando.

Resumir

Se trata de una estrategia de seguimiento de tendencias de diseño razonable, lógica clara, captura de tendencias mediante el cruce de dos líneas equiláneas, y el uso de stop loss dinámico para administrar el riesgo. La ventaja de la estrategia es que las reglas de operación son claras, el riesgo es controlable y se adapta a la estructura básica del sistema de negociación a medio y largo plazo.

Código Fuente de la Estrategia
/*backtest
start: 2019-12-23 08:00:00
end: 2024-12-09 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify

//======================================================================//
//                    ____  _            _  __                          //
//                   |  _ \(_)_ __   ___(_)/ _|_   _                    //
//                   | |_) | | '_ \ / _ \ | |_| | | |                   //
//                   |  __/| | | | |  __/ |  _| |_| |                   //
//                   |_|   |_|_| |_|\___|_|_|  \__, |                   //
//                                             |___/                    //
//======================================================================//

//@version=5
strategy(title="TQQQ EMA Strategy", overlay=true)

//#region —————————————————————————————————————————————————— Common Dependence

p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_unix_time = na
    int end_unix_time = na
    int start_time_hour = na
    int start_time_minute = na
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        // Format: hh:mm-hh:mm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        // Format: hhmm-hhmm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
    end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
    [start_unix_time, end_unix_time]

p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_time_hour = na
    int start_time_minute = na
    if str.length(time_range) == 11
        // Format: hh:mm-hh:mm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
    else if str.length(time_range) == 9
        // Format: hhmm-hhmm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)

p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)

p_comm_timeframe_to_seconds(simple string tf) =>
    float seconds = 0
    tf_lower = str.lower(tf)
    value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
    if str.endswith(tf_lower, 's')
        seconds := value
    else if str.endswith(tf_lower, 'd')
        seconds := value * 86400
    else if str.endswith(tf_lower, 'w')
        seconds := value * 604800
    else if str.endswith(tf_lower, 'm')
        seconds := value * 2592000
    else
        seconds := str.tonumber(tf_lower) * 60
    seconds

p_custom_sources() =>
    [open, high, low, close, volume]

//#endregion —————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Ta Dependence


//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Constants

// Input Groups
string P_GP_1      =      ""

//#endregion —————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Inputs

// Default
int p_inp_1        =      input.int(defval=20, title="Fast EMA Length", group=P_GP_1)
int p_inp_2        =      input.int(defval=50, title="Slow EMA Length", group=P_GP_1)
float p_inp_3      =      input.float(defval=1.3, title="Take Profit Price Multiplier", group=P_GP_1, step=0.01)
float p_inp_4      =      input.float(defval=0.95, title="Stop Loss Price Multiplier", group=P_GP_1, step=0.01)


//#endregion ———————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Price Data



//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicators

p_ind_1      =      ta.ema(close, p_inp_1) // Fast EMA
p_ind_2      =      ta.ema(close, p_inp_2) // Slow EMA


//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Conditions

p_cond_1      =      (ta.crossover(p_ind_1, p_ind_2))


//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Strategy

// Strategy Order Variables
string p_st_name_1                       =      "Entry"
string p_st_name_2                       =      "Exit"
var float p_st_name_2_tp                 =      na
var bool p_st_name_2_tp_can_drawing      =      true
var float p_st_name_2_sl                 =      na
var bool p_st_name_2_sl_can_drawing      =      true

// Strategy Global
open_trades_number = strategy.opentrades
pre_bar_open_trades_number = na(open_trades_number[1]) ? 0 : open_trades_number[1]
var p_entry_order_id = 1
p_can_place_entry_order() =>
    strategy.equity > 0
get_entry_id_name(int current_order_id, string name) =>
    "[" + str.tostring(current_order_id) + "] " + name
is_entry_order(string order_id, string name) =>
    str.startswith(order_id, "[") and str.endswith(order_id, "] " + name)
get_open_trades_entry_ids() =>
    int p_open_trades_count = strategy.opentrades
    string[] p_entry_ids = array.new_string(0, "")
    if p_open_trades_count > 0
        for i = 0 to p_open_trades_count - 1
            array.push(p_entry_ids, strategy.opentrades.entry_id(i))
    p_entry_ids

// Entry (Entry)
if p_cond_1 and p_can_place_entry_order()
    p_st_name_1_id = get_entry_id_name(p_entry_order_id, p_st_name_1)
    p_entry_order_id := p_entry_order_id + 1
    string entry_message = ""
    strategy.entry(id=p_st_name_1_id, direction=strategy.long, alert_message=entry_message, comment=p_st_name_1_id)
    
    // TP/SL Exit (Exit)
    float p_st_name_2_limit = close * p_inp_3
    if p_st_name_2_tp_can_drawing
        p_st_name_2_tp_can_drawing := false
        p_st_name_2_tp := p_st_name_2_limit
    float p_st_name_2_stop = close * p_inp_4
    if p_st_name_2_sl_can_drawing
        p_st_name_2_sl_can_drawing := false
        p_st_name_2_sl := p_st_name_2_stop
    string p_st_name_2_alert_message = ""
    strategy.exit(id=p_st_name_1_id + "_0", from_entry=p_st_name_1_id, qty_percent=100, limit=p_st_name_2_limit, stop=p_st_name_2_stop, comment_profit=p_st_name_2 + " - TP", comment_loss=p_st_name_2 + " - SL", alert_message=p_st_name_2_alert_message)
    

if high >= p_st_name_2_tp or (pre_bar_open_trades_number > 0 and open_trades_number == 0)
    p_st_name_2_tp_can_drawing := true
    p_st_name_2_sl_can_drawing := true
    p_st_name_2_tp := na
    p_st_name_2_sl := na
plot(p_st_name_2_tp, title="Exit - TP", color=color.rgb(0, 150, 136, 0), linewidth=1, style = plot.style_circles)
if low <= p_st_name_2_sl or (pre_bar_open_trades_number > 0 and open_trades_number == 0)
    p_st_name_2_sl_can_drawing := true
    p_st_name_2_tp_can_drawing := true
    p_st_name_2_sl := na
    p_st_name_2_tp := na
plot(p_st_name_2_sl, title="Exit - SL", color=color.rgb(244, 67, 54, 0), linewidth=1, style = plot.style_circles)
//#endregion —————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicator Plots

// Fast EMA
plot(p_ind_1, "Fast EMA", color.rgb(33, 150, 243, 0), 1)

// Slow EMA
plot(p_ind_2, "Slow EMA", color.rgb(255, 82, 82, 0), 1)

//#endregion ————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Custom Plots

//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Alert

//#endregion ——————————————————————————————————————————————————————