Stratégie de négociation quantitative basée sur l'inversion du pivot

Auteur:ChaoZhang est là., Date: 2023-12-12 11:07:46 Je vous en prie.
Les étiquettes:

img

Résumé

Il s'agit d'une stratégie de trading quantitative qui utilise les points pivots comme signaux d'entrée. Il calcule les points pivots en hausse et en baisse. Une fois que le prix franchit ces points pivots, il initiera des positions longues ou courtes.

Principe de stratégie

Cette stratégie est principalement basée sur la théorie de l'inversion du pivot. Elle calcule d'abord les points de pivot basés sur les barres N gauche et les barres M droite. Elle surveille ensuite en temps réel si le prix traverse ces points de pivot.

Lorsque le prix franchit le point de pivot ascendant, cela signifie que l'élan à la hausse n'est plus suffisant pour continuer à pousser le prix vers le haut. À ce moment-là, le short peut rapporter de bons rendements. Lorsque le prix franchit le point de pivot en baisse, cela signifie que l'élan à la baisse a été épuisé. À ce moment-là, le long peut obtenir de bons rendements.

Plus précisément, cette stratégie calcule les points pivots ascendants et descendants à l'aide des fonctions ta.pivothigh et ta.pivotlow. Elle compare ensuite si le prix le plus élevé actuel franchit le point pivot ascendant et si le prix le plus bas franchit le point pivot descendant. S'il y a une percée, la stratégie longue ou courte correspondante sera lancée.

En outre, cette stratégie utilise également le stop loss pour contrôler les risques. Plus précisément, lorsque le prix franchit le point de pivot, il place immédiatement un ordre tout en définissant le stop loss de l'autre côté du point de pivot. Cela peut minimiser la perte causée par un signal défaillant.

Analyse des avantages

Cette stratégie basée sur l'inversion du pivot présente les avantages suivants:

  1. Le signal d'inversion du pivot est assez fiable avec un taux de gain élevé
  2. Le risque est bien contrôlé avec un réglage raisonnable de stop loss
  3. Il est facile à mettre en œuvre avec un code concis
  4. Il est applicable à différents produits avec une bonne souplesse

Analyse des risques

Cette stratégie comporte également certains risques:

  1. Les points de pivotement peuvent échouer, ce qui entraîne des signaux incorrects
  2. Il pourrait y avoir un recul après avoir brisé le point de pivot, provoquant un stop loss déclenchant
  3. La fréquence de négociation peut être élevée, entraînant des coûts de négociation implicites
  4. Les performances dépendent de la sélection du produit et de la régulation des paramètres

Pour réduire les risques, les aspects suivants peuvent être pris en considération:

  1. Optimiser le nombre de barres gauche et droite pour assurer un calcul fiable des points de pivotement
  2. Réduire le stop-loss dans une certaine mesure pour éviter une sur-stress
  3. Fixer un objectif de profit minimum pour réduire les échanges fréquents aller-retour
  4. Test sur différents produits et paramètres pour trouver la configuration optimale

Directions d'optimisation

Cette stratégie peut être encore optimisée:

  1. Incorporer d'autres indicateurs pour juger de la fiabilité des percées pivot
  2. Ajouter des modèles d'apprentissage automatique pour déterminer les tendances des prix
  3. Utiliser les données haute fréquence pour améliorer la sensibilité du signal
  4. Introduire le module de dimensionnement de la position pour ajuster dynamiquement les positions
  5. Connectez le module de compte détaillé pour calculer le coût de négociation réel

Ces optimisations pourraient améliorer le taux de victoire, la rentabilité et la stabilité de la stratégie.

Conclusion

En résumé, il s'agit d'une stratégie de trading quantitative basée sur la théorie de l'inversion du pivot. Elle utilise les points pivots de rupture de prix comme signaux de trading tout en adoptant un stop loss pour contrôler les risques. Cette stratégie est facile à mettre en œuvre et largement applicable, ce qui en fait une stratégie de trading quantitative pratique. Mais elle comporte également certains risques et nécessite des tests et une optimisation supplémentaires pour trouver la configuration optimale dans le trading réel.


/*backtest
start: 2022-12-05 00:00:00
end: 2023-12-11 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=5
strategy('Weekly Returns with Benchmark', overlay=true, 
     default_qty_type=strategy.percent_of_equity, default_qty_value=25, 
     commission_type=strategy.commission.percent, commission_value=0.1)

////////////
// Inputs //

// Pivot points inputs
leftBars   = input(2, group = "Pivot Points")
rightBars  = input(1, group = "Pivot Points")

// Styling inputs
prec       = input(1, title='Return Precision',                            group = "Weekly Table")
from_date  = input(timestamp("01 Jan 3000 00:00 +0000"), "From Date", group = "Weekhly Table")
prof_color = input.color(color.green, title = "Gradient Colors", group = "Weeky Table", inline = "colors")
loss_color = input.color(color.red,   title = "",                group = "Weeky Table", inline = "colors")

// Benchmark inputs
use_cur    = input.bool(true,        title = "Use current Symbol for Benchmark", group = "Benchmark")
symb_bench = input('BTC_USDT:swap', title = "Benchmark",                        group = "Benchmark")
disp_bench = input.bool(false,       title = "Display Benchmark?",               group = "Benchmark")
disp_alpha = input.bool(false,       title = "Display Alpha?",                   group = "Benchmark")

// Pivot Points Strategy
swh = ta.pivothigh(leftBars, rightBars)
swl = ta.pivotlow (leftBars, rightBars)

hprice = 0.0
hprice := not na(swh) ? swh : hprice[1]

lprice = 0.0
lprice := not na(swl) ? swl : lprice[1]

le = false
le := not na(swh) ? true : le[1] and high > hprice ? false : le[1]

se = false
se := not na(swl) ? true : se[1] and low < lprice ? false : se[1]

if le
    strategy.entry('PivRevLE', strategy.long, comment='PivRevLE', stop=hprice + syminfo.mintick)

if se
    strategy.entry('PivRevSE', strategy.short, comment='PivRevSE', stop=lprice - syminfo.mintick)

plot(hprice, color=color.new(color.green, 0), linewidth=2)
plot(lprice, color=color.new(color.red, 0), linewidth=2)


///////////////////
// WEEKLY TABLE //

new_week = weekofyear(time[1]) != weekofyear(time)
new_year = year(time) != year(time[1])

eq       = strategy.equity
bench_eq = close

// benchmark eq
bench_eq_htf = request.security(symb_bench, timeframe.period, close)

if (not use_cur)
    bench_eq := bench_eq_htf

bar_pnl   = eq / eq[1] - 1
bench_pnl = bench_eq / bench_eq[1] - 1



// Current Weekly P&L
cur_week_pnl  = 0.0
cur_week_pnl := bar_index == 0 ? 0 : 
                 time >= from_date and (time[1] < from_date or new_week) ? bar_pnl : 
                 (1 + cur_week_pnl[1]) * (1 + bar_pnl) - 1

// Current Yearly P&L
cur_year_pnl  = 0.0
cur_year_pnl := bar_index == 0 ? 0 : 
                 time >= from_date and (time[1] < from_date or new_year) ? bar_pnl : 
                 (1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1


// Current Weekly P&L - Bench
bench_cur_week_pnl  = 0.0
bench_cur_week_pnl := bar_index == 0 or (time[1] < from_date and time >= from_date) ? 0 : 
                       time >= from_date and new_week ? bench_pnl : 
                       (1 + bench_cur_week_pnl[1]) * (1 + bench_pnl) - 1 

// Current Yearly P&L - Bench
bench_cur_year_pnl  = 0.0
bench_cur_year_pnl := bar_index == 0 ? 0 : 
                       time >= from_date and (time[1] < from_date  or new_year) ? bench_pnl : 
                       (1 + bench_cur_year_pnl[1]) * (1 + bench_pnl) - 1




var week_time = array.new_int(0)
var year_time = array.new_int(0)

var week_pnl = array.new_float(0)
var year_pnl = array.new_float(0)

var bench_week_pnl = array.new_float(0)
var bench_year_pnl = array.new_float(0)


// Filling weekly / yearly pnl arrays
if array.size(week_time) > 0
    if weekofyear(time) == weekofyear(array.get(week_time, array.size(week_time) - 1))
        array.pop(week_pnl)
        array.pop(bench_week_pnl)
        array.pop(week_time)


if array.size(year_time) > 0
    if year(time) == year(array.get(year_time, array.size(year_time) - 1))
        array.pop(year_pnl)
        array.pop(bench_year_pnl)
        array.pop(year_time)


if (time >= from_date)
    array.push(week_time, time)
    array.push(year_time, time)
    
    array.push(week_pnl, cur_week_pnl)
    array.push(year_pnl, cur_year_pnl)
    
    array.push(bench_year_pnl, bench_cur_year_pnl)
    array.push(bench_week_pnl, bench_cur_week_pnl)


// Weekly P&L Table  

table_size = size.tiny
var weekly_table = table(na)

if array.size(year_pnl) > 0 and barstate.islastconfirmedhistory

    weekly_table := table.new(position.bottom_right, 
                 columns=56, rows=array.size(year_pnl) * 3 + 5, border_width=1)

// Fill weekly performance
    table.cell(weekly_table, 0, 0,  'Perf', 
                 bgcolor = #999999, text_size= table_size)


    for numW = 1 to 53 by 1
        table.cell(weekly_table, numW, 0,  str.tostring(numW), 
                 bgcolor= #999999, text_size= table_size)


    table.cell(weekly_table, 54, 0, ' ',    
                 bgcolor = #999999, text_size= table_size)
    table.cell(weekly_table, 55, 0, 'Year', 
                 bgcolor = #999999, text_size= table_size)

    max_abs_y = math.max(math.abs(array.max(year_pnl)), math.abs(array.min(year_pnl)))
    max_abs_m = math.max(math.abs(array.max(week_pnl)), math.abs(array.min(week_pnl)))

    
    for yi = 0 to array.size(year_pnl) - 1 by 1
        table.cell(weekly_table, 0,  yi + 1,
                 str.tostring(year(array.get(year_time, yi))), 
                 bgcolor=#cccccc, text_size=table_size)
                 
        table.cell(weekly_table, 53, yi + 1, ' ',   
                 bgcolor=#999999, text_size=table_size)
                 
        table.cell(weekly_table, 54, yi + 1, ' ',   
                 bgcolor=#999999, text_size=table_size)

        y_color = color.from_gradient(array.get(year_pnl, yi), -max_abs_y, max_abs_y, loss_color, prof_color) 

        table.cell(weekly_table, 55, yi + 1, 
                 str.tostring(math.round(array.get(year_pnl, yi) * 100, prec)), 
                 bgcolor=y_color, text_size=table_size)
    
    int iw_row= na
    int iw_col= na

    for wi = 0 to array.size(week_time) - 2 by 1
        w_row   = year(array.get(week_time, wi)) - year(array.get(year_time, 0)) + 1
        w_col   = weekofyear(array.get(week_time, wi))

        w_color = color.from_gradient(array.get(week_pnl, wi), -max_abs_m, max_abs_m, loss_color, prof_color)
        
        if iw_row + 1 == w_row and iw_col + 1 == w_col
            table.cell(weekly_table, w_col, w_row-1,
                 str.tostring(math.round(array.get(week_pnl, wi) * 100, prec)), 
                 bgcolor=w_color, text_size=table_size)
        else
            table.cell(weekly_table, w_col, w_row,
                 str.tostring(math.round(array.get(week_pnl, wi) * 100, prec)), 
                 bgcolor=w_color, text_size=table_size)
        
        
        iw_row:= w_row
        iw_col:= w_col


    // Fill benchmark performance
    next_row =  array.size(year_pnl) + 1  

    if (disp_bench)
    
        table.cell(weekly_table, 0,  next_row, 'Bench', 
                 bgcolor=#999999, text_size=table_size)
        
        for numW = 1 to 53 by 1
            table.cell(weekly_table, numW, next_row,  str.tostring(numW), 
                 bgcolor= #999999, text_size= table_size)

        table.cell(weekly_table, 54, next_row, ' '   ,   
                 bgcolor = #999999, text_size=table_size)
        table.cell(weekly_table, 55, next_row, 'Year',   
                 bgcolor = #999999, text_size=table_size)
    
        max_bench_abs_y = math.max(math.abs(array.max(bench_year_pnl)), math.abs(array.min(bench_year_pnl)))
        max_bench_abs_w = math.max(math.abs(array.max(bench_week_pnl)), math.abs(array.min(bench_week_pnl)))
    
        for yi = 0 to array.size(year_time) - 1 by 1
            table.cell(weekly_table, 0,  yi + 1 + next_row + 1, 
                 str.tostring(year(array.get(year_time, yi))), 
                 bgcolor=#cccccc, text_size=table_size)

            table.cell(weekly_table, 53, yi + 1 + next_row + 1, ' ',   
                 bgcolor=#999999, text_size=table_size)
            
            table.cell(weekly_table, 54, yi + 1 + next_row + 1, ' ', 
                 bgcolor=#999999, text_size=table_size)
                 
            y_color = color.from_gradient(array.get(bench_year_pnl, yi), -max_bench_abs_y, max_bench_abs_y, loss_color, prof_color)
            table.cell(weekly_table, 55, yi + 1 + next_row + 1, 
                 str.tostring(math.round(array.get(bench_year_pnl, yi) * 100, prec)), 
                 bgcolor=y_color, text_size=table_size)
     
    
        int iw_row1= na
        int iw_col1= na

        for wi = 0 to array.size(week_time) - 1 by 1
            w_row   = year(array.get(week_time, wi)) - year(array.get(year_time, 0)) + 1
            w_col   = weekofyear(array.get(week_time, wi))
        
            w_color = color.from_gradient(array.get(bench_week_pnl, wi), -max_bench_abs_w, max_bench_abs_w, loss_color, prof_color)
    
            if iw_row1 + 1 == w_row and iw_col1 + 1 == w_col
                table.cell(weekly_table, w_col, w_row  + next_row    , 
                 str.tostring(math.round(array.get(bench_week_pnl, wi) * 100, prec)),
                 bgcolor=w_color, text_size=table_size)
            else
                table.cell(weekly_table, w_col, w_row  + next_row + 1, 
                 str.tostring(math.round(array.get(bench_week_pnl, wi) * 100, prec)), 
                 bgcolor=w_color, text_size=table_size)
                
            iw_row1:= w_row
            iw_col1:= w_col
    
    
// Fill Alpha
    if (disp_alpha)
    
        // columns
        next_row :=  array.size(year_pnl) * 2 + 3   
        table.cell(weekly_table, 0,  next_row, 'Alpha', 
                 bgcolor=#999999, text_size= table_size)


        for numW = 1 to 53 by 1
            table.cell(weekly_table, numW, next_row,  str.tostring(numW), 
                 bgcolor= #999999, text_size= table_size)


        table.cell(weekly_table, 54, next_row, ' '   ,  
                 bgcolor=#999999, text_size= table_size)
        table.cell(weekly_table, 55, next_row, 'Year',  
                 bgcolor=#999999, text_size= table_size)
        
        
        
        max_alpha_abs_y = 0.0
        for yi = 0 to array.size(year_time) - 1 by 1
            if (math.abs(array.get(year_pnl, yi)  - array.get(bench_year_pnl, yi)) > max_alpha_abs_y)
                max_alpha_abs_y := math.abs(array.get(year_pnl, yi)  - array.get(bench_year_pnl, yi))
    
        max_alpha_abs_w = 0.0
        for wi = 0 to array.size(week_pnl) - 1 by 1
            if (math.abs(array.get(week_pnl, wi) - array.get(bench_week_pnl, wi)) > max_alpha_abs_w)
                max_alpha_abs_w := math.abs(array.get(week_pnl, wi) - array.get(bench_week_pnl, wi))
    
    
        for yi = 0 to array.size(year_time) - 1 by 1
            table.cell(weekly_table, 0,  yi + 1 + next_row + 1, 
                 str.tostring(year(array.get(year_time, yi))), 
                 bgcolor=#cccccc, text_size= table_size)
                 
            table.cell(weekly_table, 53, yi + 1 + next_row + 1, ' ',   
                 bgcolor=#999999, text_size= table_size)
                 
            table.cell(weekly_table, 54, yi + 1 + next_row + 1, ' ',   
                 bgcolor=#999999, text_size= table_size)

            y_color = color.from_gradient(array.get(year_pnl, yi)  - array.get(bench_year_pnl, yi), -max_alpha_abs_y, max_alpha_abs_y, loss_color, prof_color)
            table.cell(weekly_table, 55, yi + 1 + next_row + 1,
                 str.tostring(math.round((array.get(year_pnl, yi)  - array.get(bench_year_pnl, yi)) * 100, prec)), 
                 bgcolor=y_color, text_size= table_size)
     
     
        int iw_row2= na
        int iw_col2= na
        
        for wi = 0 to array.size(week_time) - 1 by 1
            w_row   = year(array.get(week_time, wi)) - year(array.get(year_time, 0)) + 1
            w_col   = weekofyear(array.get(week_time, wi))
            w_color = color.from_gradient(array.get(week_pnl, wi) - array.get(bench_week_pnl, wi), -max_alpha_abs_w, max_alpha_abs_w, loss_color, prof_color)
    
            if iw_row2 + 1 == w_row and iw_col2 + 1 == w_col
                table.cell(weekly_table, w_col, w_row  + next_row , 
                     str.tostring(math.round((array.get(week_pnl, wi) - array.get(bench_week_pnl, wi)) * 100, prec)), 
                     bgcolor=w_color, text_size= table_size)
            else
                table.cell(weekly_table, w_col, w_row  + next_row + 1 , 
                     str.tostring(math.round((array.get(week_pnl, wi) - array.get(bench_week_pnl, wi)) * 100, prec)), 
                     bgcolor=w_color, text_size= table_size)
        
            iw_row2:= w_row
            iw_col2:= w_col


Plus de