S&P500 하이브리드 계절 거래 전략

저자:차오장, 날짜: 2024-01-22 11:59:58
태그:

img

전반적인 설명

S&P500 하이브리드 계절 거래 전략은 계절 패턴에 따라 주식을 거래하는 양적 전략이다. 향상된 구매 및 보유 시스템, 기술 지표 조건 및 볼륨 흐름 지표가 결합되어 일 년의 가장 좋은 달과 최악의 달 사이에 회전합니다.

전략 논리

주요 거래 신호와 규칙은 다음과 같습니다.

  1. 매해 10월 첫 거래일에 오픈 시장을 파는 겁니다.
  2. VIX가 60% 이상 또는 15일 ATR가 90% 이상인 경우, 월 또는 년 후반에 변동성이 줄어들 때까지 계절 거래를 중단합니다.
  3. 매해 8월 첫 거래일에 오픈에서 장기/단계 출구
  4. 또한 VIX가 120%를 넘거나 VFI가 10일 MA가 하락하는 동안 -20 이하로 넘어가면 긴/단순으로 빠져나가야 합니다.
  5. 선택적으로 단축 판매가 가능해졌어요

이 전략은 주식 시장의 불평등한 성과를 활용하여 10~4월에 장기간 거래하여 통계적으로 우월한 성과를 거두며 5~9월의 열악한 성과 달 동안 수익 또는 단축을 취하고 있습니다. 위험은 또한 VIX 및 ATR와 같은 지표로 측정되는 변동성이 급증할 때 거래를 중단함으로써 관리됩니다.

이점 분석

S&P500 하이브리드 계절 거래 전략은 다음과 같은 주요 장점을 가지고 있습니다.

  1. 레버리지는 S&P500 지수의 일 년 동안 경험적으로 관찰 된 불균형 한 달 성과에 기초한 안정적인 계절 패턴을 설정했습니다.
  2. VIX, ATR 및 VFI와 같은 여러 필터 조건을 통합하여 노이즈를 효과적으로 필터링하고 더 신뢰할 수있는 거래 신호를 생성합니다.
  3. 테스트와 최적화를 용이하게 하는 계절 입출에 대한 긴/단기 및 사용자 정의 기간을 위한 구성 가능한 거래 규칙.
  4. VIX 및 ATR 임계값과 같은 변동성 조치를 통한 내장된 리스크 피방 메커니즘을 통해 강력한 시장 변동의 영향을 우회합니다.
  5. 시장 참여의 잠재적 변화를 반영하는 볼륨 흐름 지표의 추가 신호 입력.

위험 분석

몇 가지 잠재적 인 위험은 다음과 같습니다.

  1. 역사적인 패턴의 무효화 위험. 시장은 스토카스틱하게 진화하기 때문에 역사적인 경향은 항상 지속되지 않을 수 있습니다.
  2. 기술 지표의 잘못된 신호 위험 VIX, ATR 및 VFI 또한 잘못된 신호를 생성 할 수 있습니다.
  3. 최저 매개 변수 위험. 현재 값이 전 세계적으로 최적화되지 않을 수 있기 때문에 추가 매개 변수 테스트 및 조정이 가능합니다.
  4. 제한 없는 손실과 같은 추가적인 단축 위험.

위험은 보다 엄격한 위험 통제, 지표의 조합, 매개 변수 조정, 기계 학습 등을 통해 완화될 수 있습니다.

더 나은 기회

최적화 가능점:

  1. 더 많은 훈련 데이터를 위해 더 긴 백테스트 기간.
  2. 거래당 손실을 통제하기 위해 스톱 로스 메커니즘을 도입합니다.
  3. VIX, ATR, VFI와 같은 지표의 매개 변수를 세밀하게 조정하여 최적의 조합을 찾습니다.
  4. 기계 학습 모델을 배포하여 적응적 최적화를 가능하게 합니다.
  5. 상관관계가 없도록 시스템적 시장 위험을 줄이기 위한 전략을 집계한다.

결론

S&P500 하이브리드 계절 거래 전략은 잘 정해진 계절 경향, 기술적 타이밍 지표 및 현금 흐름 측정을 합성합니다. 효과적인 변동성 게팅으로 보완된 해의 최악의 성과 달을 피하고 계절적으로 강한 달에 위치함으로써 프레임워크는 일관된 알파를 얻을 수 있습니다. 적응 가능한 구조는 또한 연습자가 테스트하고 최적화하고 구축 할 수있는 유용한 모듈 구성 요소를 제공합니다. 추가 데이터, 중지 손실, 매개 변수 조정 및 앙상블은 성과를 향상시킬 수있는 추가 기회를 제공합니다.


/*backtest
start: 2023-12-01 00:00:00
end: 2023-12-31 23:59:59
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//  TASC Issue: April 2022 - Vol. 40, Issue 4
//     Article: Sell In May? Stock Market Seasonality                    
//  Article By: Markos Katsanos
//    Language: TradingView's Pine Script v5
// Provided By: PineCoders, for tradingview.com

//@version=5
strategy(title             = "TASC 2022.04 S&P500 Hybrid Seasonal System", 
         shorttitle        = "HSS v2.0",
         overlay           = true, 
         default_qty_type  = strategy.percent_of_equity, 
         default_qty_value = 10, 
         initial_capital   = 100000,
         currency          = currency.USD,
         commission_type   = strategy.commission.percent,
         commission_value  = 0.01
         )

// Helper Functions:

// @function Returns the ratio to max/min of a sample period
// @param src float, data source.
// @param length int, period of the sample.
// @returns [float, float] tuple.
volatility (float src, int length) =>
    [(src / ta.highest(src, length)[1] - 1.0) * 100.0,
     (src / ta.lowest (src, length)[1] - 1.0) * 100.0]

// @function Volume Flow Indicator.
// @param Period int, period of the data sample.
// @param VCoef float, Volume Volatility Coefficient.
// @param Coef float, Cutoff Coefficient.
// @returns float.
// ref: https://mkatsanos.com/volume-flow-vfi-indicator/
vfi (int Period = 130, float VCoef = 2.5, float Coef = 0.2) =>
    lastHLC3 = nz(hlc3[1], hlc3)
	MF     = hlc3  - lastHLC3
    Vinter = ta.stdev(math.log(hlc3) - math.log(lastHLC3), 30)
    Vave   = ta.sma(volume, Period)[1]
    Cutoff = Coef * close * Vinter
    VC     = math.min(volume, Vave * VCoef)
    VCP    = MF >  Cutoff ?  VC :
		     MF < -Cutoff ? -VC : 0.0
    VFI1 = nz(math.sum(VCP, Period) / Vave)
    VFI = ta.ema(VFI1, 3)


// inputs:
// optional strategy obserservation window parameters:
string ig_ow      = 'Observation Window:'
bool i_Sdate      = input(  title   = 'Start date:', 
                                 defval  = timestamp('2021-01-01'), 
                                 inline  = 'Sdate', 
                                 group   = ig_ow
                                 ) < time //
bool i_useSdate   = input.bool(  title   = '', 
                                 defval  = false, 
                                 group   = ig_ow, 
                                 inline  = 'Sdate', 
                                 tooltip = 'Optional start date to clamp strategy observation window.'
                                 ) //
bool i_Edate      = input(  title   = 'End date:', 
                                 defval  = timestamp('2022-01-01'), 
                                 inline  = 'Edate', 
                                 group   = ig_ow
                                 ) > time //
bool i_useEdate   = input.bool(  title   = '', 
                                 defval  = false, 
                                 group   = ig_ow, 
                                 inline  = 'Edate', 
                                 tooltip = 'Optional end date to clamp strategy observation window.'
                                 ) //
//
string ig_ro      = 'Lookback Options:'
int  i_lback      = input.int(   title   = 'Lookback Shift:', 
                                 defval  = 0, minval = 0,
                                 group   = ig_ro,
                                 tooltip = 'Optional, inspect previous signal values.'
                                 ) //
//
string ig_so      = 'Signal Options:'
bool i_onlyL      = input.bool(      title   = 'Long Only:', 
                                     defval  = true,
                                     group   = ig_so, 
                                     tooltip = 'If switched off, short entries are initiated by sell signals.'
                                     ) //
int  i_sMonth     = input.int(       title   = 'Sell Month:', 
                                     defval  = 8, minval = 1, maxval = 12, step = 1,
                                     group   = ig_so, 
                                     tooltip = 'The worst performing month, originally clamped between months 5 and 8.'
                                     ) //
int  i_maxVI      = input.int(       title   = 'Max VIX up:', 
                                     defval  = 60, minval = 50, maxval = 60, step = 5,
                                     group   = ig_so, 
                                     tooltip = 'Volatility maximum threshold.'
                                     ) //
int  i_critVFI    = input.int(       title   = 'Critical VFI Sell:', 
                                     defval  = -20, minval = -20, maxval = -15, step = 5, 
                                     group   = ig_so, 
                                     tooltip = 'Critical money float (VFI) threshold for sell signal.'
                                     ) //
float i_K         = input.float(     title   = 'ATR/VIX Ratio:', 
                                     defval  = 1.5, minval = 1.3, maxval = 1.7, step = 0.2, 
                                     group   = ig_so, 
                                     tooltip = 'ATR to VIX ratio for sell signal.'
                                     ) //
// 
string i_VIticker = input(    title   = 'Volatility Index:',
                                     defval  = 'VIX', 
                                     group   = ig_so,
                                     tooltip = 'Volatility Index Ticker.'
                                     ) //
string i_VItf     = input.timeframe( title   = '',
                                     defval  = 'D', 
                                     group   = ig_so,
                                     tooltip = 'Volatility Index Timeframe.'
                                     ) //
int i_VIiperiod   = input.int(       title   = 'Implied Volatility period:', 
                                     defval  = 25, 
                                     group   = ig_so
                                     ) //
int i_VIhperiod   = input.int(       title   = 'Historical Volatility period:', 
                                     defval  = 15, 
                                     group   = ig_so
                                     ) //
//
int i_VFIperiod   = input.int(   title   = 'VFI period:', 
                                 defval  = 130, 
                                 group   = ig_so, inline = 'VFI1'
                                 ) //
int i_VFIMperiod  = input.int(   title   = 'MA:', 
                                 defval  = 10, 
                                 group   = ig_so, inline = 'VFI1',
                                 tooltip = 'VFI and Moving Average sampling period.'
                                 ) //
float i_VFIcoef   = input.float( title   = 'VFI Coef Cuttoff:', 
                                 defval  = 0.2, 
                                 group   = ig_so, inline = 'VFI2'
                                 ) //
float i_VFIvcoef  = input.float( title   = 'Volat.:',
                                 defval  = 2.5, 
                                 group   = ig_so, inline = 'VFI2',
                                 tooltip = 'VFI Cutoff and Volatility coefficient.'
                                 ) //
int i_ATRperiod   = input.int(   title   = 'ATR length:',
                                 defval  = 15, 
                                 group = ig_so, inline = 'ATR',
                                 tooltip = 'ATR length.'
                                 ) // 
//
string ig_to = 'Table Options:'
bool i_showT =      input.bool(  title   = 'Show Table:',
                                 defval  = false, 
                                 group   = ig_to, 
                                 tooltip = 'Optional toggle.'
                                 ) //
string i_Tpos =     input.string(title   = 'Position:',
                                 defval  = position.middle_right, 
                                 options = [    position.top_left,     position.top_center,    position.top_right, 
                                             position.middle_left,  position.middle_center, position.middle_right,
                                             position.bottom_left,  position.bottom_center, position.bottom_right   ],
                                 group   = ig_to) //
int i_Ttransp  =      input.int( title   = 'Transparency:',
                                 defval  = 0, minval = 1, maxval = 99, 
                                 group   = ig_to
                                 ) //
//
color i_Tcframe =   input.color( title   = 'Table Colors:', 
                                 defval  = #000000, 
                                 group   = ig_to, inline = 'table color'
                                 ) //
color i_Tcrowe =    input.color( title   = '', 
                                 defval  = #d6dae3, 
                                 group   = ig_to, inline = 'table color'
                                 ) //
color i_Tcrowo =    input.color( title   = '', 
                                 defval  = #cccccc, 
                                 group   = ig_to, inline = 'table color', 
                                 tooltip = 'Table background colors, in order: frame, even row, odd row.'
                                 ) //
string i_Ttsize =   input.string(title   = 'Table Text:', 
                                 defval  = size.small,
                                 options = [size.auto, size.huge, size.large, size.normal, size.small, size.tiny],
                                 group   = ig_to, inline = 'table text'
                                 ) //
color i_Tcdeft =    input.color( title   = 'Text Colors:', 
                                 defval  = #000000, 
                                 group   = ig_to, inline = 'table text'
                                 ) //
color i_Tcsigt =    input.color( title   = '', 
                                 defval  = color.red, 
                                 group   = ig_to, inline = 'table text'
                                 ) //
color i_Tctitt =    input.color( title   = '', 
                                 defval  = color.navy, 
                                 group   = ig_to, inline = 'table text', 
                                 tooltip = 'Table text size and colors, in order: default, short signal, title.'
                                 ) //

// Comparison Index
float VIX      = request.security(i_VIticker, i_VItf, close)
[VIdn, VIup]   = volatility(VIX, i_VIiperiod)                   // Implied
[ATRdn, ATRup] = volatility(ta.atr(i_VIhperiod), i_VIiperiod)   // Historical

float VFI   = vfi(i_VFIperiod, i_VFIvcoef, i_VFIcoef)
float VFI10 = ta.sma(VFI, i_VFIMperiod)


//
bool VFIatCrit = VFI > i_critVFI
bool lowVolat  = (VIup < i_maxVI) or (ATRup < (i_K * i_maxVI))
bool VolatC    = VFIatCrit ? lowVolat : false
bool Long      = ((month >= 10) or (month < i_sMonth)) and VolatC[1]
bool Sseasonal = month == i_sMonth                                   // SEASONAL EXIT/SHORT
bool Svol      = VIup > (2.0 * i_maxVI)                              // VOLATILITY EXIT/SHORT
bool Scrit     = ta.cross(i_critVFI, VFI) and (VFI10 < VFI10[1])     // VFI EXIT/SHORT
bool Short     = Sseasonal or Svol[1] or Scrit[1]

bool withinObsWindow = true
//
if withinObsWindow and strategy.equity > 0
    _L = strategy.long
    _S = strategy.short
    strategy.entry('L'               , direction = _L,      when = Long      )
    if i_onlyL
        strategy.close('L', comment = 'EXIT SEASONAL'  ,    when = Sseasonal )
        strategy.close('L', comment = 'EXIT VOLATILITY',    when = Svol[1]   )
        strategy.close('L', comment = 'EXIT MF'        ,    when = Scrit[1]  )
    else
        strategy.entry('S Seasonal'  , direction = _S,      when = Sseasonal )
        strategy.entry('S Volatility', direction = _S,      when = Svol[1]   )
        strategy.entry('S MF Crit.'  , direction = _S,      when = Scrit[1]  )
else
    strategy.close_all()

string SIGNAL = switch
    (Long)                      => 'Long Seasonal'
    (Sseasonal and i_onlyL)     => 'Exit Seasonal'
    (Svol[1]   and i_onlyL)     => 'Exit Volatility'
    (Scrit[1]  and i_onlyL)     => 'Exit Money Flow'
    (Sseasonal and not i_onlyL) => 'Short Seasonal'
    (Svol[1]   and not i_onlyL) => 'Short Volatility'
    (Scrit[1]  and not i_onlyL) => 'Short Money Flow Bearish'
    =>                             'none'

string date = str.format(
  '{0,number,0000}-{1,number,00}-{2,number,00}', 
  year, month, dayofmonth
  )

var table dTable = table.new(position    = i_Tpos, 
                             columns     = 2, 
                             rows        = 17, 
                             frame_color = color.new(#000000, i_Ttransp), 
                             frame_width = 4
                             ) //


// @function Helper to populate the table rows.
tRow(tableId, idx, left, right, tcol=0) =>
    color _bg = color.new(idx % 2 ? i_Tcrowo : i_Tcrowe, i_Ttransp)
    color _tx = switch (tcol)
        (1)  => color.new(i_Tcsigt, i_Ttransp)
        (2)  => color.new(i_Tctitt, i_Ttransp)
        =>      color.new(i_Tcdeft, i_Ttransp)
    // table.cell(  table_id=tableId, 
    //              column=0, row=idx, 
    //              text=left, text_color=_tx, text_halign=text.align_right, text_size=i_Ttsize, 
    //              bgcolor=_bg) //
    // table.cell(  table_id=tableId, 
    //              column=1, row=idx, 
    //              text=str.tostring(right), text_color=_tx, text_halign=text.align_left, text_size=i_Ttsize, 
    //              bgcolor=_bg) //


if i_showT
    float _atr10 = ta.atr(10)[i_lback]
    string _nf = '0.00'
    string _aru = '🔼 ', string _ard = '🔽 '
    //      id | idx |                   left label  |                      right label  |               conditional color |
    tRow(dTable,   00, 'S&P500 Hybrid Seasonal '     , ''                                , 2                               )
    tRow(dTable,   01, 'Created By: Markos Katsanos' , ''                                , 2                               )
    tRow(dTable,   02, 'Date:'                       , date[i_lback]                                                       )
    tRow(dTable,   03, 'Signal:'                     , SIGNAL[i_lback]                                                     )
    tRow(dTable,   04, 'Price:'                      , open[i_lback]                                                       )
    tRow(dTable,   05, 'VIX:'                        , str.tostring(  VIX[i_lback], _nf)                                   )
    tRow(dTable,   06, 'VFI:'                        , str.tostring(  VFI[i_lback], _nf) , VFIatCrit ? 1 : 0               )
    tRow(dTable,   07, 'ATR:'                        , str.tostring(        _atr10, _nf)                                   )
    tRow(dTable,   08, 'VIup%:'                      , str.tostring( VIup[i_lback], _nf) , VIup > i_maxVI ? 1 : 0          )
    tRow(dTable,   09, 'ATRup%:'                     , str.tostring(ATRup[i_lback], _nf) , ATRup > i_K * i_maxVI ? 1 : 0   )
    tRow(dTable,   10, 'VIdn%:'                      , str.tostring( VIdn[i_lback], _nf)                                   )
    tRow(dTable,   11, 'ATRdn%:'                     , str.tostring(ATRdn[i_lback], _nf)                                   )
    tRow(dTable,   12, _aru + 'Long Seasonal:'       , Long[i_lback]                                                       )
    tmp = 12
    if not i_onlyL
        tmp := 13
        tRow(dTable, 13, _ard + 'Short:'             , Short[i_lback]                    , Short[i_lback] ? 1 : 0          )
    tRow(dTable,  tmp+1, _ard + 'Seasonal:'          , Sseasonal[i_lback]                , Sseasonal[i_lback] ? 1 : 0      )
    tRow(dTable,  tmp+2, _ard + 'Volatility:'        , Svol[1+i_lback]                   , Svol[1 + i_lback] ? 1 : 0       )
    tRow(dTable,  tmp+3, _ard + 'Money Flow:'        , Scrit[i_lback]                    , Scrit[i_lback] ? 1 : 0          )


더 많은