sobota, 26 października 2024

Za mała próba czy zły model?

 I znowu model zawiódł. Prawdopodobieństwo wzrostu WIG20 w poprzednim tygodniu szacowałem na ponad 0,6. Jednak indeks spadł. Diagnostyka nie wykryła jakichś nieprawidłowości, szczególnie model wybrany zgodnie z kryterium DAC w próbie testowej 30 obserwacji wydawał się całkiem poprawny. Doszedłem do wniosku, że problem może leżeć w zbyt niskim DAC po reestymacji. Wówczas dostałem 63,5% poprawnych odpowiedzi, co było niewystarczające dla 30 danych (nieistotne), tak że ciągle był to raczej przypadek. O tym żeby uznać to za coś więcej, przekonała mnie korelacja 0,67 między reestymowanymi prognozami kroczącymi na podstawie różnych kryteriów (HQC i DAC-of-sample). Mógł to być oczywiście pech, ale trzeba wziąć pod uwagę inne powody.

Pierwsza sprawa to wielkość próby. Użyte 200 obserwacji w próbie uczącej to nie jest za dużo jak na taki model. Wybrałem 200 tygodni ad hoc na takiej zasadzie, żeby z jednej strony obliczenia nie trwały za długo, a z drugiej, żeby uwzględnić przynajmniej jeden cykl giełdowy, który mniej więcej tyle trwa. Może być to jednak za mało. Z drugiej strony rodzi się problem reestymacji, który szczegółowo omawiałem poprzednio. Jeżeli uznajemy, że prawdziwy model nie istnieje i należy przez cały czas go reestymować, to lepiej aby próba testowa nie była za duża. Nie chodzi nawet o czasochłonność obliczeniową, ale o to, że po wielu obserwacjach testowych, sama specyfikacja (model teoretyczny) się zmienia. Rzecz w tym, że nawet przy ponad 500 danych dodanie jednej tylko obserwacji zmienia optymalny model. Tak więc, gdybym użył 501 danych już bym miał inne optimum, 502 jeszcze inne itd. Teoretycznie przy kolejnej reestymacji parametry dla zbędnych rzędów powinny zostać przez algorytm ustawione na zero, ale to z kolei rodzi pytanie o ustawienie maksymalnego rzędu. Przykładowo przy założeniu max 15 i przy 400 obserwacjach próby uczącej dostałem  ARMA(12, 15)-EGARCH-ARCH_In_Mean dla HQC, co znaczy, że albo max rzędów jest za mały, albo próba jest za mała. 

Aby zminimalizować ten problem, zwiększam próbę uczącą do 600 obserwacji (tygodniowe log-stopy zwrotu WIG20), a maksimum rzędów ARMA do 20. Natomiast wielkość próby testowej wyniesie tylko 15.  Poza tym kod pozostaje ten sam. W ten sposób wg min. HQC otrzymałem:

                    ARMA(9, 9)-ARCH_In_Mean
HQC                                 -4.4735
DAC (in sample)                      0.6133 

DAC (out of sample) 0.6000 


Ciekawe, że DAC in-sample i out-of-sample są w tym modelu takie same. Może przypadek, może nie. Wykonajmy diagnostykę:

*---------------------------------*
*          GARCH Model Fit        *
*---------------------------------*

Conditional Variance Dynamics 	
-----------------------------------
GARCH Model	: eGARCH(1,1)
Mean Model	: ARFIMA(9,0,9)
Distribution	: snorm 

Optimal Parameters
------------------------------------
        Estimate  Std. Error      t value Pr(>|t|)
mu     -0.000611    0.000000   -72428.506        0
ar1    -1.017510    0.000786    -1295.050        0
ar2     0.510817    0.000964      529.650        0
ar3     0.688893    0.000020    34654.649        0
ar4     0.042736    0.000037     1150.044        0
ar5     0.031574    0.000027     1166.599        0
ar6     0.802410    0.000001  1581829.476        0
ar7     0.981174    0.000001   839176.987        0
ar8    -0.423505    0.000006   -74666.301        0
ar9    -0.723909    0.000000 -1840101.437        0
ma1     1.014618    0.000022    46381.828        0
ma2    -0.521428    0.000221    -2362.691        0
ma3    -0.732535    0.000111    -6598.699        0
ma4    -0.121873    0.000055    -2210.317        0
ma5    -0.089846    0.000052    -1728.580        0
ma6    -0.894943    0.000205    -4362.136        0
ma7    -1.067543    0.000164    -6508.449        0
ma8     0.517891    0.000070     7402.304        0
ma9     0.849641    0.000082    10319.607        0
archm   0.000132    0.000000      315.194        0
omega  -1.188795    0.005020     -236.822        0
alpha1 -0.261897    0.000051    -5159.294        0
beta1   0.842014    0.000210     4006.494        0
gamma1  0.009565    0.000146       65.525        0
skew    0.741782    0.000413     1796.442        0

Robust Standard Errors:
        Estimate  Std. Error     t value Pr(>|t|)
mu     -0.000611    0.000000  -14098.715        0
ar1    -1.017510    0.000898   -1132.916        0
ar2     0.510817    0.001865     273.969        0
ar3     0.688893    0.000038   18278.881        0
ar4     0.042736    0.000151     282.800        0
ar5     0.031574    0.000111     283.606        0
ar6     0.802410    0.000003  307956.247        0
ar7     0.981174    0.000004  279684.711        0
ar8    -0.423505    0.000019  -22631.465        0
ar9    -0.723909    0.000001 -628047.744        0
ma1     1.014618    0.000123    8251.585        0
ma2    -0.521428    0.000587    -887.549        0
ma3    -0.732535    0.000303   -2414.904        0
ma4    -0.121873    0.000083   -1475.789        0
ma5    -0.089846    0.000103    -870.758        0
ma6    -0.894943    0.000507   -1766.114        0
ma7    -1.067543    0.000361   -2955.822        0
ma8     0.517891    0.000449    1154.102        0
ma9     0.849641    0.000402    2111.852        0
archm   0.000132    0.000002      80.308        0
omega  -1.188795    0.020893     -56.900        0
alpha1 -0.261897    0.000597    -438.494        0
beta1   0.842014    0.000153    5495.866        0
gamma1  0.009565    0.000228      41.865        0
skew    0.741782    0.000726    1021.332        0

LogLikelihood : 1388 

Information Criteria
------------------------------------
                    
Akaike       -4.5448
Bayes        -4.3616
Shibata      -4.5481
Hannan-Quinn -4.4735

Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
                         statistic p-value
Lag[1]                      0.6302  0.4273
Lag[2*(p+q)+(p+q)-1][53]   21.5958  1.0000
Lag[4*(p+q)+(p+q)-1][89]   43.0403  0.6430
d.o.f=18
H0 : No serial correlation

Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
                        statistic     p-value
Lag[1]                     0.6644 0.415008325
Lag[2*(p+q)+(p+q)-1][5]   22.9348 0.000002998
Lag[4*(p+q)+(p+q)-1][9]   25.8744 0.000005721
d.o.f=2

Weighted ARCH LM Tests
------------------------------------
            Statistic Shape Scale P-Value
ARCH Lag[3]    0.2817 0.500 2.000  0.5956
ARCH Lag[5]    0.3440 1.440 1.667  0.9283
ARCH Lag[7]    0.7047 2.315 1.543  0.9563

Nyblom stability test
------------------------------------
Joint Statistic:  no.parameters>20 (not available)
Individual Statistics:              
mu     0.01828
ar1    0.04389
ar2    0.04270
ar3    0.02659
ar4    0.01544
ar5    0.01972
ar6    0.02274
ar7    0.01703
ar8    0.01997
ar9    0.01801
ma1    0.03099
ma2    0.03226
ma3    0.03189
ma4    0.02808
ma5    0.02623
ma6    0.01821
ma7    0.06530
ma8    0.03776
ma9    0.01859
archm  0.01796
omega  0.15436
alpha1 0.19570
beta1  0.14572
gamma1 0.05599
skew   0.14442

Asymptotic Critical Values (10% 5% 1%)
Individual Statistic:	 0.35 0.47 0.75

Sign Bias Test
------------------------------------
                   t-value    prob sig
Sign Bias          1.72190 0.08561   *
Negative Sign Bias 1.37158 0.17071    
Positive Sign Bias 0.06065 0.95166    
Joint Effect       4.79282 0.18761    


Adjusted Pearson Goodness-of-Fit Test:
------------------------------------
  group statistic p-value(g-1)
1    20     26.60      0.11432
2    30     38.30      0.11581
3    40     46.13      0.20113
4    50     62.50      0.09319


Słabo to wygląda. Test znaków i kwadratów reszt sugeruje, że model jest niewłaściwy.

I tak dochodzimy do drugiej kwestii - samej specyfikacji modelu. Jeszcze dla 300 obserwacji EGARCH(1, 1) był prawidłowy, ale 600 zmienia ten pogląd. Jest jeszcze wprawdzie możliwość, że zły rozkład, ale raczej nie o to chodzi.

Wszystko od początku

Całą analizę zacznijmy od początku. Na tych 600 danych treningowych i 15 testowych trzeba najpierw znaleźć najlepszy model ARMA (bez GARCH). Kod będzie bardzo podobny do omawianego poprzednio (wszystkie pakiety i zmienne te same), ale bez ARCH-In-Mean i użyć funkcji arfimaspec(), arfimafit(), arfimaforecast() zamiast ugarchspec(), ugarchfit(), ugarchforecast():

rozklad <- "snorm"

hqc <- list()

dacDopas <- list()

dacPrognoza <- list()

wyrzuc <- 15

wynik <- list()

# Ustawienie równoległego backendu do użycia wielu procesorów

ileRdzeni <- detectCores() - 1  # Użyj jednego mniej niż całkowita liczba rdzeni

klaster <- makeCluster(ileRdzeni)

registerDoParallel(klaster)

# Utworzenie siatki parametrów dla pętli bez k (ARCH-in-mean)

siatka_parametrow <- expand.grid(i = 0:20, j = 0:20)

# Użycie foreach do przetwarzania równoległego

wyniki <- foreach(param = 1:nrow(siatka_parametrow), .packages = c("rugarch")) %dopar% {

  i <- siatka_parametrow$i[param]

  j <- siatka_parametrow$j[param]

  rzad_modelu <- sprintf("ARMA(%d, %d)", i, j)

  tryCatch({

    dopasArma <- NULL

    mojArma <- arfimaspec(mean.model = list(armaOrder = c(i, j)), 

                          distribution.model = rozklad)

    dopasArma <- arfimafit(spec = mojArma, data = stopaZwrotu, out.sample = wyrzuc,

                           solver = "hybrid")

    stopaZwrotuDopas <- fitted(dopasArma)

    stopaZwrotuZgodna <- stopaZwrotuDopas + residuals(dopasArma)

    prognozaArma <- arfimaforecast(dopasArma, n.ahead = 1, n.roll = wyrzuc - 1)

    # Inicjalizacja wartości DAC

    dacDopas_wartosc <- NA

    dacPrognoza_wartosc <- NA

    # Sprawdzenie ARMA(0, 0) i pominięcie DACTest jeśli prawda

    if (!(i == 0 && j == 0)) {

      dacDopas_wartosc <- DACTest(forecast = as.numeric(stopaZwrotuDopas), 

                                  actual = as.numeric(stopaZwrotuZgodna), 

                                  test = "PT")$DirAcc

      dacPrognoza_wartosc <- as.numeric(fpm(prognozaArma)[3]) # Liczenie DAC dla prognozy

    }

    return(list(

      rzad_modelu = rzad_modelu,

      hqc = infocriteria(dopasArma)[4,],

      dacDopas = dacDopas_wartosc,

      dacPrognoza = dacPrognoza_wartosc

    ))

  }, error = function(e) {

    message("Błąd w ARMA(", i, ", ", j, "): ", conditionMessage(e))

    return(list(error = conditionMessage(e), rzad_modelu = rzad_modelu))

  })

}

# Zatrzymaj klaster po przetwarzaniu

stopCluster(klaster)


# Przetwórz wyniki i przechowaj je w oryginalnych listach

for (wynik in wyniki) {

  if (!is.null(wynik)) {

    for (nazwa_modelu in names(wynik)) {

      hqc[[wynik$rzad_modelu]] <- wynik$hqc

      dacDopas[[wynik$rzad_modelu]] <- wynik$dacDopas

      dacPrognoza[[wynik$rzad_modelu]] <- wynik$dacPrognoza

    }

  }

}


# kryterium inf HQC

hqcDf <- as.data.frame(do.call(cbind, hqc))

colnames(hqcDf) <- names(hqc)

rownames(hqcDf) <- "HQC" 

# DAC w próbie treningowej (uczącej)

dacDopasDf <- as.data.frame(do.call(cbind, dacDopas))

rownames(dacDopasDf) <- "DAC (in sample)" 

# DAC w próbie testowej

dacPrognozaDf <- as.data.frame(do.call(cbind, dacPrognoza))

rownames(dacPrognozaDf) <- "DAC (out of sample)" 

porownanie <- bind_rows(hqcDf, dacDopasDf, dacPrognozaDf)

kryterium_hqc <- porownanie[, which.min(porownanie[1, ]), drop=F]

kryterium_dacDopas <- porownanie[, which.max(porownanie[2, ]), drop=F]

kryterium_dacPrognoza <- porownanie[, which.max(porownanie[3, ]), drop=F]

# Znajdź wszystkie kolumny z minimalną wartością w pierwszym wierszu (HQC)

min_hqc <- min(round(porownanie[1, ], 4), na.rm = TRUE)

kryterium_hqc <- porownanie[, which(round(porownanie[1, ], 4) == min_hqc), drop = FALSE]

# Znajdź wszystkie kolumny z maksymalną wartością w drugim wierszu (DAC w próbie treningowej)

max_dacDopas <- max(round(porownanie[2, ], 4), na.rm = TRUE)

kryterium_dacDopas <- porownanie[, which(round(porownanie[2, ], 4) == max_dacDopas), drop = FALSE]

# Znajdź wszystkie kolumny z maksymalną wartością w trzecim wierszu (DAC w próbie testowej)

max_dacPrognoza <- max(round(porownanie[3, ], 4), na.rm = TRUE)

kryterium_dacPrognoza <- porownanie[, which(round(porownanie[3, ], 4) == max_dacPrognoza), drop = FALSE]


kryterium_hqc

kryterium_dacDopas

kryterium_dacPrognoza


Dostałem:
> kryterium_hqc
                    ARMA(7, 11)
HQC                     -4.3222
DAC (in sample)          0.6017
DAC (out of sample)      0.8667
> kryterium_dacDopas
                    ARMA(16, 11)
HQC                      -4.2633
DAC (in sample)           0.6283
DAC (out of sample)       0.6000
> kryterium_dacPrognoza
                    ARMA(6, 4) ARMA(6, 18)
HQC                    -4.2574      -4.264
DAC (in sample)         0.5417       0.570
DAC (out of sample)     1.0000       1.000


Dalej wyciągamy optymalne rzędy:

# parametry - HQC

wyciagnij_liczby <- function(wyr, x) {

  as.numeric(gsub("[^0-9]", "", substr(wyr, x, x+2), ))

}


rzadAR_hqc <- wyciagnij_liczby(colnames(kryterium_hqc), 6)

rzadMA_hqc <- wyciagnij_liczby(colnames(kryterium_hqc), 9)


# parametry - DAC out of sample

rzadAR_DAC <- wyciagnij_liczby(colnames(kryterium_dacPrognoza), 6)[2]

rzadMA_DAC <- wyciagnij_liczby(colnames(kryterium_dacPrognoza), 9)[2]


Wykonajmy diagnostykę, analogicznie jak poprzednio wg HQC:

dopasArma_hqc <- arfimafit(spec = mojArma_hqc, data = stopaZwrotu, out.sample = wyrzuc, solver="hybrid")

dopasArma_hqc

> dopasArma_hqc

*----------------------------------*
*          ARFIMA Model Fit        *
*----------------------------------*
Mean Model	: ARFIMA(7,0,11)
Distribution	: snorm 

Optimal Parameters
------------------------------------
       Estimate  Std. Error     t value Pr(>|t|)
mu    -0.000163    0.000001     -118.88        0
ar1    1.708939    0.000055    31171.71        0
ar2   -1.589454    0.000048   -33434.75        0
ar3    0.941900    0.000042    22383.28        0
ar4   -1.292676    0.000001 -1403760.91        0
ar5    1.688498    0.000060    28287.27        0
ar6   -1.389024    0.000042   -32791.53        0
ar7    0.337973    0.000043     7801.90        0
ma1   -1.803387    0.000060   -29855.67        0
ma2    1.876807    0.000059    31845.44        0
ma3   -1.393470    0.000044   -31363.61        0
ma4    1.874833    0.000068    27413.36        0
ma5   -2.382959    0.000069   -34491.56        0
ma6    2.152583    0.000074    29113.30        0
ma7   -1.013392    0.000043   -23773.12        0
ma8    0.566727    0.000027    20790.58        0
ma9   -0.357758    0.000017   -20796.19        0
ma10   0.187237    0.000010    18174.45        0
ma11  -0.066118    0.000014    -4891.35        0
sigma  0.026299    0.000060      439.52        0
skew   0.803218    0.001613      498.00        0

Robust Standard Errors:
       Estimate  Std. Error    t value Pr(>|t|)
mu    -0.000163    0.000001    -245.88        0
ar1    1.708939    0.000100   17094.56        0
ar2   -1.589454    0.000060  -26331.39        0
ar3    0.941900    0.000053   17769.70        0
ar4   -1.292676    0.000004 -321135.66        0
ar5    1.688498    0.000068   24959.04        0
ar6   -1.389024    0.000018  -75620.19        0
ar7    0.337973    0.000035    9540.15        0
ma1   -1.803387    0.000209   -8649.06        0
ma2    1.876807    0.000157   11965.81        0
ma3   -1.393470    0.000031  -45389.79        0
ma4    1.874833    0.000165   11333.48        0
ma5   -2.382959    0.000120  -19852.77        0
ma6    2.152583    0.000209   10290.45        0
ma7   -1.013392    0.000129   -7881.81        0
ma8    0.566727    0.000037   15484.79        0
ma9   -0.357758    0.000041   -8716.28        0
ma10   0.187237    0.000009   19894.66        0
ma11  -0.066118    0.000021   -3178.62        0
sigma  0.026299    0.000071     371.02        0
skew   0.803218    0.000424    1892.55        0

LogLikelihood : 1336 

Information Criteria
------------------------------------
                    
Akaike       -4.3821
Bayes        -4.2282
Shibata      -4.3844
Hannan-Quinn -4.3222

Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
                         statistic p-value
Lag[1]                       1.659 0.19768
Lag[2*(p+q)+(p+q)-1][53]    35.169 0.00000
Lag[4*(p+q)+(p+q)-1][89]    54.654 0.03086

H0 : No serial correlation

Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
                        statistic               p-value
Lag[1]                      1.762 0.1844147994362065335
Lag[2*(p+q)+(p+q)-1][2]    35.427 0.0000000004027803646
Lag[4*(p+q)+(p+q)-1][5]    57.765 0.0000000000000003331


ARCH LM Tests
------------------------------------
             Statistic DoF             P-Value
ARCH Lag[2]      67.45   2 0.00000000000000222
ARCH Lag[5]      67.76   5 0.00000000000029932
ARCH Lag[10]     70.05  10 0.00000000004330802

Nyblom stability test
------------------------------------
Joint Statistic:  no.parameters>20 (not available)
Individual Statistics:             
mu    0.05184
ar1   0.08997
ar2   0.06947
ar3   0.05230
ar4   0.02741
ar5   0.04652
ar6   0.07092
ar7   0.08179
ma1   0.09581
ma2   0.07342
ma3   0.05758
ma4   0.03403
ma5   0.03789
ma6   0.04300
ma7   0.07302
ma8   0.10824
ma9   0.07579
ma10  0.04847
ma11  0.03839
sigma 0.66621
skew  0.02743

Asymptotic Critical Values (10% 5% 1%)
Individual Statistic:	 0.35 0.47 0.75


I co się okazuje? Że występuje jednak efekt ARCH. Sama ARMA jest niedopuszczalna. 


Sprawdźmy jeszcze model wg kryterium DAC w próbie testowej:

> dopasArma_DAC

*----------------------------------*
*          ARFIMA Model Fit        *
*----------------------------------*
Mean Model	: ARFIMA(6,0,18)
Distribution	: snorm 

Optimal Parameters
------------------------------------
       Estimate  Std. Error   t value Pr(>|t|)
mu    -0.000484    0.000003   -181.01        0
ar1    1.652690    0.000052  31715.16        0
ar2   -0.569904    0.000021 -26571.17        0
ar3   -0.691440    0.000024 -29358.26        0
ar4   -0.370370    0.000016 -23705.84        0
ar5    1.335052    0.000028  46954.51        0
ar6   -0.810241    0.000022 -36509.08        0
ma1   -1.807455    0.000065 -27683.56        0
ma2    0.896344    0.000043  20628.63        0
ma3    0.479002    0.000017  28772.19        0
ma4    0.307185    0.000013  23760.96        0
ma5   -1.300847    0.000056 -23158.98        0
ma6    0.999150    0.000045  22099.33        0
ma7   -0.255216    0.000013 -19665.03        0
ma8    0.146197    0.000009  15602.83        0
ma9    0.010041    0.000005   1948.42        0
ma10  -0.192279    0.000009 -20638.50        0
ma11   0.181860    0.000009  19169.60        0
ma12  -0.126310    0.000007 -19332.60        0
ma13   0.129111    0.000008  15919.81        0
ma14  -0.135228    0.000010 -13197.50        0
ma15   0.080054    0.000005  16270.07        0
ma16  -0.257084    0.000014 -19028.90        0
ma17   0.334883    0.000013  24885.88        0
ma18  -0.170361    0.000002 -78823.90        0
sigma  0.026828    0.000039    684.76        0
skew   0.736525    0.000896    822.24        0

Robust Standard Errors:
       Estimate  Std. Error    t value Pr(>|t|)
mu    -0.000484    0.000005    -88.585        0
ar1    1.652690    0.000225   7358.377        0
ar2   -0.569904    0.000030 -18787.065        0
ar3   -0.691440    0.000020 -33985.665        0
ar4   -0.370370    0.000007 -53955.284        0
ar5    1.335052    0.000061  22010.081        0
ar6   -0.810241    0.000078 -10327.488        0
ma1   -1.807455    0.000418  -4325.866        0
ma2    0.896344    0.000228   3928.523        0
ma3    0.479002    0.000019  25779.508        0
ma4    0.307185    0.000031  10005.522        0
ma5   -1.300847    0.000312  -4166.365        0
ma6    0.999150    0.000079  12603.512        0
ma7   -0.255216    0.000037  -6950.382        0
ma8    0.146197    0.000019   7556.457        0
ma9    0.010041    0.000026    388.235        0
ma10  -0.192279    0.000006 -33015.132        0
ma11   0.181860    0.000014  12830.990        0
ma12  -0.126310    0.000003 -39589.998        0
ma13   0.129111    0.000016   8046.554        0
ma14  -0.135228    0.000022  -6183.826        0
ma15   0.080054    0.000032   2482.382        0
ma16  -0.257084    0.000028  -9035.050        0
ma17   0.334883    0.000027  12423.452        0
ma18  -0.170361    0.000007 -26122.951        0
sigma  0.026828    0.000057    474.242        0
skew   0.736525    0.003314    222.255        0

LogLikelihood : 1329 

Information Criteria
------------------------------------
                    
Akaike       -4.3406
Bayes        -4.1427
Shibata      -4.3444
Hannan-Quinn -4.2636

Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
                          statistic p-value
Lag[1]                        5.469 0.01936
Lag[2*(p+q)+(p+q)-1][71]     35.802 0.63160
Lag[4*(p+q)+(p+q)-1][119]    62.706 0.30838

H0 : No serial correlation

Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
                        statistic           p-value
Lag[1]                     0.6252 0.429113495341529
Lag[2*(p+q)+(p+q)-1][2]   28.6335 0.000000024213054
Lag[4*(p+q)+(p+q)-1][5]   45.7761 0.000000000001031


ARCH LM Tests
------------------------------------
             Statistic DoF           P-Value
ARCH Lag[2]      55.77   2 0.000000000000775
ARCH Lag[5]      57.73   5 0.000000000035680
ARCH Lag[10]     58.90  10 0.000000005842875

Nyblom stability test
------------------------------------
Joint Statistic:  no.parameters>20 (not available)
Individual Statistics:             
mu    0.07987
ar1   0.12280
ar2   0.10719
ar3   0.05115
ar4   0.04223
ar5   0.07941
ar6   0.11593
ma1   0.11203
ma2   0.06554
ma3   0.03642
ma4   0.07160
ma5   0.11188
ma6   0.12608
ma7   0.10324
ma8   0.05420
ma9   0.03809
ma10  0.07410
ma11  0.11379
ma12  0.12568
ma13  0.09919
ma14  0.05052
ma15  0.03972
ma16  0.07790
ma17  0.11582
ma18  0.12465
sigma 0.63364
skew  0.06743

Asymptotic Critical Values (10% 5% 1%)
Individual Statistic:	 0.35 0.47 0.75


To samo - mamy tu do czynienia z efektami ARCH. Aby model poprawnie działał, musimy je uwzględnić.


Wiemy już na pewno, że sama ARMA jest błędna. Dochodzimy do wniosku, że musi być to jakiś model GARCH, ale nie EGARCH. Sprawdziłem więc standardowy GARCH(1,1) oznaczany "sGARCH" - ale także okazał się niewłaściwy. Trzeba szukać dalej. Wiadomo tylko, że rzędy części GARCH (1, 1) są prawidłowe, bo gdyby nie były, to w resztach pojawiłyby się efekty ARCH nie uwzględnione przez model - a ich nie było w EGARCH (ani w sGARCH jak sprawdzałem). 

Przychodzą mi dwie możliwości, które trzeba po kolei sprawdzić. Po pierwsze może być to inny model, jak TGARCH, APARCH, GJR-GARCH, NGARCH, NAGARCH, iGARCH i csGARCH. Po drugie EGARCH może być prawidłowy, ale może występować jakaś niestabilność czy zmiana struktury wariancji, której test Nybloma nie uwzględnił. Stąd może być potrzebny zupełnie inny model albo bardziej zaawansowany GARCH, jak np. TV-GARCH (time-varying GARCH), którego nie ma obecnie w rugarch.

Jak dotąd sprawdziłem we wszystkich konfiguracjach sGARCH, TGARCH, GJR-GARCH, IGARCH i APARCH. Mimo użycia przetwarzania równoległego poszukiwanie optimum dla każdego zajmuje wiele godzin. Okazało się, że żaden model wcale nie był lepszy od EGARCH. Sprawdziłem dodatkowo - ale już bez ARMA - wiele modeli z GARCH innych rzędów, czyli GARCH(1, 2), GARCH(2, 1), GARCH(2, 2). Sprawdzałem pod tym kątem też NGARCH i NAGARCH. Nic to nie dało.

Jedną z potencjalnych przyczyn trudności modelowania warunkowej wariancji, jest zmienność jej parametrów w czasie. Test Nybloma w rugarch nie wykryje tej zmienności, jeśli zmiana nie będzie systematyczna, ale za to będzie systematycznie zanikać w czasie. Moglibyśmy użyć innych testów. Dotychczas sprawdzałem zmienność wariancji przy pomocy testu Goldfelda-Quandta, Breuscha-Pagana, Harrisona-McCabe (zob. tu) oraz CUSUM (tu i tu). Niektóre z tych testów mogą jednak pomylić warunkową heteroskedastyczność z niestabilnością wariancji niewarunkowej. 

TV-GARCH

Rozwiązaniem problemu jest pakiet tvgarch, służący do symulacji, estymacji i analizy procesu TV-GARCH (Time Varying GARCH). Pakiet zawiera funkcję tvgarchTest(), która testuje proces TV-GARCH(1,1). Instalujemy i ładujemy pakiet:

if (require("tvgarch")==FALSE) {

  install.packages("tvgarch")

}

library("tvgarch")


Najpierw przeprowadzimy kilka symulacji sGARCH i eGarch z rozkładem skośno-normalnym i wykonamy test:

set.seed(30)

sym <- ugarchspec(variance.model = list(model = "sGARCH", garchOrder = c(1, 1)), mean.model = list(armaOrder = c(1, 1)), 

                   distribution.model = "snorm", fixed.pars = list(omega = 0.5, 

                                                                   alpha1 = 0.6,

                                                                   beta1 = 0.3,

                                                                   mu = 0.01,

                                                                   ar1 = 0.6,

                                                                   ma1 = 0.2,

                                                                   skew = 0.7))

symGarch <- ugarchpath(sym, n.sim = 600)

symGarch <- symGarch@path$seriesSim

ts.plot(symGarch)

I włączamy funkcję:

tvGarchTest <- tvgarchTest(y = as.numeric(symGarch))

tvGarchTest

Efekt:

Testing GARCH(1,1), i.e., the model under H0, against 
  TV-GARCH(1,1), i.e., the model under H1: 

Estimation results for model under H0:
Model: GARCH(1,1) 

            intercept.h  arch1  garch1
Estimate:        0.5632 0.8630 0.28048
Std. Error:      0.1232 0.1134 0.04657
                     
Log-likelihood: -1309


Transition variable in TV-GARCH(1,1): time 

Results from the Non-Robust TR^2 Test: 

                 NonRobTR2 p-value
H0:B3=B2=B1=0       3.7317  0.2919
H03:B3=0            0.7273  0.3937
H02:B2=0|B3=0       2.2520  0.1334
H01:B1=0|B3=B2=0    0.7589  0.3837

Results from the Robust TR^2 Test: 

                 RobTR2 p-value
H0:B3=B2=B1=0     6.268  0.0993
H03:B3=0          1.393  0.2378
H02:B2=0|B3=0     2.195  0.1385
H01:B1=0|B3=B2=0  2.694  0.1007

                                Single
No. of locations (alpha = 0.05)      0


Są tu dwie wersje testu: nieodporny (non-robust) i odporny (robust), podobnie jak to było w rugarch. Odporność należy tu rozumieć jako odporność na odchylenia od rozkładu normalnego. Ten odporny i tak będzie ważniejszy, więc tylko na niego patrzymy. Parametr B1 można interpretować jako średnią wariancji niewarunkowej. Parametr B2 - jako wariancję wariancji niewarunkowej. Parametr B3 - jako skośność wariancji niewarunkowej. Najważniejsza jest pierwsza hipoteza. Ponieważ wskazuje p-value > 0,05 , to znaczy, że nie ma podstaw do odrzucenia hipotezy stałego GARCH. Tak też wskazuje ostatnia statystyka  No. of locations = 0 liczbę punktów przejść z jednego modelu do drugiego.


Dla set.seed(1100)


Dostałem podobne statystyki. 

Teraz symulacja EGARCH(1, 1):

sym <- ugarchspec(variance.model = list(model = "eGARCH", garchOrder = c(1, 1)), mean.model = list(armaOrder = c(1, 1)), 

                   distribution.model = "snorm", fixed.pars = list(omega = 0.5, 

                                                                   alpha1 = 0.6,

                                                                   beta1 = 0.3,

                                                                   mu = 0.01,

                                                                   ar1 = 0.6,

                                                                   ma1 = 0.2,

                                                                   gamma1 = 0.8,

                                                                   skew = 0.7))

set.seed(1100)

symGarch <- ugarchpath(sym, n.sim = 600)

symGarch <- symGarch@path$seriesSim

ts.plot(symGarch)


Tak samo test odrzucił tu TV-GARCH.

To teraz nasze (logarytmiczne) stopy zwrotu WIG20

plot(stopaZwrotu)


tvGarchTest <- tvgarchTest(y = as.numeric(stopaZwrotu))

tvGarchTest

Testing GARCH(1,1), i.e., the model under H0, against 
  TV-GARCH(1,1), i.e., the model under H1: 

Estimation results for model under H0:
Model: GARCH(1,1) 

            intercept.h   arch1  garch1
Estimate:     0.0001704 0.15324 0.63531
Std. Error:   0.0003021 0.09059 0.05965
                    
Log-likelihood: 1346


Transition variable in TV-GARCH(1,1): time 

Results from the Non-Robust TR^2 Test: 

                 NonRobTR2 p-value
H0:B3=B2=B1=0       5.7505  0.1244
H03:B3=0            3.7567  0.0526
H02:B2=0|B3=0       0.0233  0.8787
H01:B1=0|B3=B2=0    1.9829  0.1591

Results from the Robust TR^2 Test: 

                  RobTR2 p-value
H0:B3=B2=B1=0    10.2591  0.0165
H03:B3=0          3.5790  0.0585
H02:B2=0|B3=0     0.0203  0.8867
H01:B1=0|B3=B2=0  6.4987  0.0108

                                Single
No. of locations (alpha = 0.05)      1


Test rzeczywiście odrzuca stabilność wariancji niewarunkowej. Towarzyszy temu niskie p-value. Jednocześnie wyłapał 1 punkt zmiany struktury modelu wariancji. Dostajemy dowód, że albo należy posługiwać się mniejszą próbą, albo właśnie TV-GARCH. 

Chociaż jest jeszcze jedna możliwość - nawet bardziej naturalna. Można użyć nieparametrycznego GARCH, analogicznie do regresji nieparametrycznej. Skoro to takie naturalne, to po co w ogóle parametryczne modele? Głównie z powodu dostępności i tak jak w przypadku rugarch z całą paletą diagnostyczną. Drugim powodem jest to, że powinny być one zawsze punktem wyjścia - bo przecież mogą się okazać lepsze od wersji nieparametrycznej. Poza tym używanie skomplikowanych metod bez zrozumienia podstaw jest bez sensu. 

czwartek, 17 października 2024

Dwa rodzaje prognoz kroczących w rugarch (język R)

Każdy model prognostyczny może być rozumiany na dwa sposoby. Pierwszy wynika z założenia, że istnieje "prawdziwy model", którym można prognozować pewne wielkości w kolejnych okresach. Nie chodzi tu o prawdziwy model w ścisłym znaczeniu tego słowa, ale o model wystarczająco dobry w pewnym oknie czasowym. Z tego założenia wywodzi się sama możliwość użycia próby testowej (out-of-sample) w ten sposób, że do modelu podstawiamy albo prognozy z przeszłości (argument n.ahead w rugarch omówiony w poprzednim artykule) albo faktyczne wartości, których model "nie widział" podczas estymacji parametrów (n.roll dla prognozy kroczącej). W przypadku prognozy kroczącej (rolling forecast) przedłużamy niejako model w przyszłość, używając możliwie najbardziej aktualnych danych, ale przy zachowaniu starych parametrów, i to się czasem nazywa rolowaniem prognozy. 

Drugi sposób modelowania wynika z założenia, że nie istnieje prawdziwy model i to czym dysponujemy należy przez cały czas reestymować. W świecie ekonomii i giełdy jest to naturalne założenie, a nawet stanowi coś w rodzaju aksjomatu. Problem z nim jest taki, że dużo trudniej jest testować taki model. Próba testowa jest równa treningowej, bo każda kolejna obserwacja jest dodawana do próby uczącej, która z kolei wykorzystywana jest do nowej estymacji parametrów. 

W praktyce obydwa sposoby mieszane są ze sobą lub używane równorzędnie. I tak najczęściej prognoza krocząca zaczyna się dopiero od jakiegoś dalszego okresu, a reestymacja nie odbywa się co okres, ale np. co kilka okresów, co przyspiesza proces obliczeń. Równoległe zaś użycie obu metod umożliwia ocenę pogarszania się modelu w czasie. Jeżeli bowiem zakładamy, że parametry modelu są zmienne w czasie - są dynamiczne, to jeśli nasza prognoza też jest dynamiczna, to powinna być lepsza niż statyczna (czyli niż bez reestymacji). Jeżeli zatem widzimy, że prognoza dynamiczna jest gorsza od statycznej, to dostajemy dowód, że model jest błędny - przypadkowo się dostroił do próby testowej. 

Inaczej mówiąc, o ile użycie kryterium informacyjnego (AIC, BIC, HQC) może dać złudzenie uzyskania dobrego modelu z powodu sztucznego dopasowania go do próby uczącej, o tyle prognoza krocząca bez reestymacji może dać złudzenie uzyskania dobrego modelu z powodu sztucznego dopasowania do próby testowej. I tak jak prognoza krocząca bez reestymacji jest sposobem, żeby odkryć pierwsze złudzenie, o tyle prognoza z reestymacją jest sposobem, by odkryć drugie złudzenie. 

Prognozę bez reestymacji omówiłem wcześniej. Teraz zajmę się drugim przypadkiem. Prognozę kroczącą z reestymacją można oczywiście samodzielnie zakodować, ale rugarch oferuje specjalną funkcję: arfimaroll() i ugarchroll(). Ta druga jest bardziej ogólna, dlatego to nią się posłużymy.

Funkcja ma następujące argumenty:

spec - teoretyczny model GARCH,

data - dane najlepiej w xts,

forecast.length - długość prognozy dla próby testowej (tj. out-of-sample), która liczona jest końca całej próby

n.start - można użyć zamiast forecast.length, zamiast liczyć od końca można po prostu określić punkt startowy dla prognozy

refit.every - określa, co którą obserwację w próbie testowej ponownie estymować; jeśli więc chcemy aby dla każdej następnej obserwacji reestymować, wpisujemy 1,

refit.window - wybór czy reestymować od początku próby do ostatniej dostępnej obserwacji ("recursive"), czy też reestymować w oknie o stałej liczebności, czyli tak jak np. średnia 30-okresowa ("moving"),

window.size - jeżeli wybraliśmy refit.window = "moving", wtedy ustawiamy tu długość okna, w którym estymowane są parametry; w przeciwnym wypadku dajemy NULL,

solver - zazwyczaj wybieram "hybrid", bo jest najbardziej wszechstronny

cluster - również tu stosujemy przetwarzanie równoległe, aby przyspieszyć obliczenia (na podstawie pakietu parallel)


Mimo dynamiczności kroczącej prognozy, trzeba mieć na względzie, że model teoretyczny w sensie jego rodzaju nie zmienia postaci podczas rolowania prognozy. Szukamy więc najlepszego modelu. Dotychczas posługiwałem się funkcją auto.arfima(). Ponieważ jednak zdecydowałem posłużyć się ogólniejszym modelem, tj. ARMA-GARCH(1, 1), musiałem zbudować funkcję, która będzie iteracyjnie obliczać statystyki dla każdego rzędu ARMA. jak widać rzędy GARCH pozostawiam na poziomie (1, 1). Sprawdzanie kolejnych rzędów do samego GARCH bardzo wydłużyłoby analizę. Z moich dotychczasowych prób wynikało, że GARCH(1, 1) jest całkowicie wystarczający, ale kiedyś można będzie to lepiej ocenić. 

Funkcja będzie poszukiwać najlepszego modelu wg trzech kryteriów: najniższego HQIC, najwyższego DAC w próbie uczącej i najwyższego DAC w próbie testowej. Aby cały proces przyspieszyć trzeba użyć przetwarzania równoległego. O ile rugarch importuje sam pakiet parallel, ja użyję innego - doParallel. Przydatny będzie też w tym celu pakiet foreach. Poza tym zastosuję funkcję bind_rows() z pakietu dplyr. Pakiet ten jest częścią większego pakietu tidyverse. Reszta jak wcześniej (czyli xts i rugarch). Cała analiza jest kontynuacją poprzednich (1, 2, 3, 4). W sumie zaczynamy od:

# Przedwstęp

if (require("tidyverse")==FALSE) {

  install.packages("tidyverse")

}

library("dplyr")

if (require("xts")==FALSE) {

  install.packages("xts")

}

library("xts")

if (require("rugarch")==FALSE) {

  install.packages("rugarch")

}

library("rugarch")

if (require("foreach")==FALSE) {

  install.packages("foreach")

}

library("foreach")

if (require("doParallel")==FALSE) {

  install.packages("doParallel")

}

library("doParallel")

#precyzja i sposób zapisu liczb: 

options(scipen = 100, digits = 4)


Kontynuuję analizę tygodniowych (logarytmicznych) stóp zwrotu WIG20, ale tym razem 230 ostatnich danych - tak żeby mieć 200 poza próbą testową (źródło: stooq.pl). Ostatnie notowanie 11.10.2024:

# Wstęp

nazwaPliku = "wig20_d.csv"

Obs <- 231

plik <- read.csv(nazwaPliku)

plik$Data <- as.Date(plik$Data)

tabela <- tail(to.weekly(as.xts(plik), indexAt="endof", OHLC=FALSE), Obs)

cena <- tabela$Zamkniecie

stopaZwrotu <- diff.xts(log(cena))

tabela$stopaZwrotu <- stopaZwrotu

tabela <- na.omit(tabela)

stopaZwrotu <- tabela$stopaZwrotu


 Używam modelu EGARCH(1, 1) o rozkładzie skośno-normalnym. 

Może najpierw pokażę prosty i krótki kod na szukanie optimum, bez obliczeń równoległych:

# stare

rozklad <- "snorm"

hqc <- list()

dacDopas <- list()

dacPrognoza <- list()

model_Garch <- "eGARCH"

submodel_Garch <- NULL

wyrzuc <- 30

for (k in 0:1) {

  for (i in 0:15) {

    for (j in 0:15) {

      tryCatch({

        dopasGarch <- NULL

        rzad_modelu <- sprintf("ARMA(%d, %d)%s", i, j, ifelse(k == 1, "-ARCH_In_Mean", ""))

        mojGarch <- ugarchspec(variance.model = list(model = model_Garch, submodel = submodel_Garch,

                                                     garchOrder = c(1, 1)), 

                               mean.model = list(armaOrder = c(i, j), archm = as.logical(k)), 

                               distribution.model = rozklad)

        dopasGarch <- ugarchfit(spec = mojGarch, data = stopaZwrotu, out.sample=wyrzuc, 

                                #solver = "nlminb")

                                solver = "hybrid")

        # HQC

        hqc[[rzad_modelu]] <- infocriteria(dopasGarch)[4,]

        # DAC in sample

        stopaZwrotuDopas <- fitted(dopasGarch)

        stopaZwrotuZgodna <- stopaZwrotuDopas + residuals(dopasGarch)

        dacDopas[[rzad_modelu]] <- DACTest(forecast = as.numeric(stopaZwrotuDopas), 

                                actual = as.numeric(stopaZwrotuZgodna), 

                                test = "PT")$DirAcc

        # wg DAC out of sample

        prognozaGarch <- ugarchforecast(dopasGarch, n.ahead = 1, n.roll = wyrzuc-1)

        dacPrognoza[[rzad_modelu]] <- fpm(prognozaGarch)[3]

        names(dacPrognoza[[rzad_modelu]]) <- rzad_modelu

      }, error = function(e) {

        message("Błąd w ARMA(", i, ", ", j, ") z k = ", k, ": ", conditionMessage(e))

        return(list(error = conditionMessage(e), rzad_modelu = rzad_modelu))

      })

    }

  }

}


Gdy jednak ustawiamy max AR i MA na więcej niż 10, czas mocno się wydłuża, szczególnie dla rozkładu niegaussowskiego. Zoptymalizowałem więc kod, tak by używał przetwarzania równoległego, jak już było wspomniane. Powyższy kod może wyglądać wtedy następująco:

# to samo co wyżej z parallel

rozklad <- "snorm"

hqc <- list()

dacDopas <- list()

dacPrognoza <- list()

model_Garch <- "eGARCH"

submodel_Garch <- NULL

wyrzuc <- 30


# Ustawienie równoległego backendu do użycia wielu procesorów

ileRdzeni <- detectCores() - 1  # Użyj jednego mniej niż całkowita liczba rdzeni

klaster <- makeCluster(ileRdzeni)

registerDoParallel(klaster)


# Utworzenie siatki parametrów dla pętli

siatka_parametrow <- expand.grid(k = 0:1, i = 0:15, j = 0:15)


# Użycie foreach do przetwarzania równoległego

wyniki <- foreach(param = 1:nrow(siatka_parametrow), .packages = c("rugarch")) %dopar% {

  k <- siatka_parametrow$k[param]

  i <- siatka_parametrow$i[param]

  j <- siatka_parametrow$j[param]

  

  rzad_modelu <- sprintf("ARMA(%d, %d)%s", i, j, ifelse(k == 1, "-ARCH_In_Mean", ""))

  

  tryCatch({

    dopasGarch <- NULL

    

    mojGarch <- ugarchspec(variance.model = list(model = model_Garch, submodel = submodel_Garch,

                                                 garchOrder = c(1, 1)), 

                           mean.model = list(armaOrder = c(i, j), archm = as.logical(k)), 

                           distribution.model = rozklad)

    dopasGarch <- ugarchfit(spec = mojGarch, data = stopaZwrotu, out.sample=wyrzuc,

                            solver = "hybrid")

    

    stopaZwrotuDopas <- fitted(dopasGarch)

    stopaZwrotuZgodna <- stopaZwrotuDopas + residuals(dopasGarch)

    

    prognozaGarch <- ugarchforecast(dopasGarch, n.ahead = 1, n.roll = wyrzuc - 1)

    

    # Inicjalizacja wartości DAC

    dacDopas_wartosc <- NA

    dacPrognoza_wartosc <- NA

    

    # Sprawdzenie ARMA(0, 0) i pominięcie DACTest jeśli prawda

    if (!(i == 0 && j == 0 && k == 0)) {

      dacDopas_wartosc <- DACTest(forecast = as.numeric(stopaZwrotuDopas), 

                                  actual = as.numeric(stopaZwrotuZgodna), 

                                  test = "PT")$DirAcc

      dacPrognoza_wartosc <- as.numeric(fpm(prognozaGarch)[3]) # Liczenie DAC dla prognozy

    }

    

    return(list(

      rzad_modelu = rzad_modelu,

      hqc = infocriteria(dopasGarch)[4,],

      dacDopas = dacDopas_wartosc,

      dacPrognoza = dacPrognoza_wartosc

    ))

    

  }, error = function(e) {

    message("Błąd w ARMA(", i, ", ", j, ") z k = ", k, ": ", conditionMessage(e))

    return(list(error = conditionMessage(e), rzad_modelu = rzad_modelu))

  })

}


# Zatrzymaj klaster po przetwarzaniu

stopCluster(klaster)


# Przetwórz wyniki i przechowaj je w oryginalnych listach

for (wynik in wyniki) {

  if (!is.null(wynik)) {

    for (nazwa_modelu in names(wynik)) {

      hqc[[wynik$rzad_modelu]] <- wynik$hqc

      dacDopas[[wynik$rzad_modelu]] <- wynik$dacDopas

      dacPrognoza[[wynik$rzad_modelu]] <- wynik$dacPrognoza

    }

  }

}


Tak więc staje się dużo bardziej złożony. Dalsza część to już tylko odpowiednie wyciągnięcie danych:

# kryterium inf HQC
hqcDf <- as.data.frame(do.call(cbind, hqc))
colnames(hqcDf) <- names(hqc)
rownames(hqcDf) <- "HQC" 

# DAC w próbie treningowej (uczącej)
dacDopasDf <- as.data.frame(do.call(cbind, dacDopas))
rownames(dacDopasDf) <- "DAC (in sample)" 

# DAC w próbie testowej
dacPrognozaDf <- as.data.frame(do.call(cbind, dacPrognoza))
rownames(dacPrognozaDf) <- "DAC (out of sample)" 

porownanie <- bind_rows(hqcDf, dacDopasDf, dacPrognozaDf)

kryterium_hqc <- porownanie[, which.min(porownanie[1, ]), drop=F]
kryterium_dacDopas <- porownanie[, which.max(porownanie[2, ]), drop=F]
kryterium_dacPrognoza <- porownanie[, which.max(porownanie[3, ]), drop=F]

# Znajdź wszystkie kolumny z minimalną wartością w pierwszym wierszu (HQC)
min_hqc <- min(round(porownanie[1, ], 4), na.rm = TRUE)
kryterium_hqc <- porownanie[, which(round(porownanie[1, ], 4) == min_hqc), drop = FALSE]

# Znajdź wszystkie kolumny z maksymalną wartością w drugim wierszu (DAC w próbie treningowej)
max_dacDopas <- max(round(porownanie[2, ], 4), na.rm = TRUE)
kryterium_dacDopas <- porownanie[, which(round(porownanie[2, ], 4) == max_dacDopas), drop = FALSE]

# Znajdź wszystkie kolumny z maksymalną wartością w trzecim wierszu (DAC w próbie testowej)
max_dacPrognoza <- max(round(porownanie[3, ], 4), na.rm = TRUE)
kryterium_dacPrognoza <- porownanie[, which(round(porownanie[3, ], 4) == max_dacPrognoza), drop = FALSE]

kryterium_hqc
kryterium_dacDopas
kryterium_dacPrognoza

Efekt:


Najważniejsze jest pierwsze i ostatnie. Natomiast DAC w próbie uczącej (in sample) służy do porównania z DAC w próbie testowej (out of sample)  - czy się pogorszyło, czy poprawiło i o ile.

Rodzą się dwa spostrzeżenia. Po pierwsze wszystkie 3 kryteria dały podobne optima, co jest dobrym znakiem. W przypadku HQC i DAC dla próby uczącej dostajemy to samo optimum, ARMA(13, 10). Szybko jednak zauważymy, że ten model daje zupełnie przypadkowe wyniki z punktu widzenia DAC w próbie testowej. Nie znaczy to jeszcze, że model jest zupełnie bezużyteczny, bo znak stopy zwrotu jest jednym z prognozowanych elementów, a ponadto nie uwzględniliśmy w ogóle reestymacji. Za to najlepszy model dla DAC na próbie testowej to ARMA(12, 8)-Arch_In_Mean. Na 30 obserwacji ok. 23 jest poprawnie prognozowanych (76,67%).  Dalej wyciągamy parametry:

# parametry - HQC

wyciagnij_liczby <- function(wyr, x) {

  as.numeric(gsub("[^0-9]", "", substr(wyr, x, x+2), ))

}


rzadAR_hqc <- wyciagnij_liczby(colnames(kryterium_hqc), 6)

rzadMA_hqc <- wyciagnij_liczby(colnames(kryterium_hqc), 9)

czy_archm_hqc <- grepl("ARCH_In_Mean", colnames(kryterium_hqc))


# parametry - DAC out of sample


wyciagnij_liczby <- function(wyr, x) {

  as.numeric(gsub("[^0-9]", "", substr(wyr, x, x+2), ))

}


rzadAR_DAC <- wyciagnij_liczby(colnames(kryterium_dacPrognoza), 6)[1]

rzadMA_DAC <- wyciagnij_liczby(colnames(kryterium_dacPrognoza), 9)[1]

czy_archm_DAC <- grepl("ARCH_In_Mean", colnames(kryterium_dacPrognoza))[1]


Budujemy modele wg tych dwóch kryteriów.

1. wg HQIC

# budowa modelu wg HQC
mojGarch_hqc <- ugarchspec(variance.model = list(model = model_Garch, submodel = submodel_Garch,
                                             garchOrder = c(1, 1)), 
                         mean.model = list(armaOrder = c(rzadAR_hqc, rzadMA_hqc), archm = czy_archm_hqc), 
                       distribution.model = rozklad)


dopasGarch_hqc <- ugarchfit(spec = mojGarch_hqc, data = stopaZwrotu, out.sample = wyrzuc, solver="hybrid")
dopasGarch_hqc    

> dopasGarch_hqc

*---------------------------------*
*          GARCH Model Fit        *
*---------------------------------*

Conditional Variance Dynamics 	
-----------------------------------
GARCH Model	: eGARCH(1,1)
Mean Model	: ARFIMA(13,0,10)
Distribution	: snorm 

Optimal Parameters
------------------------------------
        Estimate  Std. Error   t value Pr(>|t|)
mu      0.001778    0.000001    3187.4        0
ar1     0.613189    0.000060   10174.9        0
ar2    -0.230097    0.000038   -6082.9        0
ar3     0.222988    0.000043    5243.7        0
ar4     0.217702    0.000065    3368.1        0
ar5    -0.327908    0.000031  -10717.8        0
ar6    -0.045269    0.000000 -106355.2        0
ar7     0.204282    0.000025    8227.4        0
ar8    -0.295914    0.000029  -10116.2        0
ar9     0.914463    0.000003  327459.8        0
ar10   -0.282518    0.000051   -5548.4        0
ar11    0.049116    0.000005   10504.4        0
ar12   -0.271441    0.000008  -32509.3        0
ar13    0.137039    0.000020    6840.4        0
ma1    -0.231903    0.000043   -5375.9        0
ma2     0.200755    0.000037    5378.9        0
ma3    -0.006765    0.000002   -4222.9        0
ma4    -0.347592    0.000053   -6613.8        0
ma5     0.336234    0.000058    5793.3        0
ma6     0.136536    0.000055    2460.6        0
ma7    -0.346302    0.000069   -4984.0        0
ma8     0.448850    0.000068    6615.9        0
ma9    -1.371759    0.000196   -7006.0        0
ma10   -0.033945    0.000011   -3077.0        0
omega  -0.947041    0.000183   -5171.6        0
alpha1 -0.386418    0.000083   -4648.3        0
beta1   0.875887    0.000131    6680.6        0
gamma1 -0.303940    0.000138   -2201.8        0
skew    0.721121    0.000567    1270.8        0

Robust Standard Errors:
        Estimate  Std. Error   t value Pr(>|t|)
mu      0.001778    0.000003    572.79        0
ar1     0.613189    0.000158   3888.48        0
ar2    -0.230097    0.000052  -4414.02        0
ar3     0.222988    0.000133   1678.64        0
ar4     0.217702    0.000236    923.73        0
ar5    -0.327908    0.000005 -62812.59        0
ar6    -0.045269    0.000011  -4310.29        0
ar7     0.204282    0.000040   5126.05        0
ar8    -0.295914    0.000116  -2561.42        0
ar9     0.914463    0.000487   1878.31        0
ar10   -0.282518    0.000185  -1528.32        0
ar11    0.049116    0.000007   6918.33        0
ar12   -0.271441    0.000099  -2749.41        0
ar13    0.137039    0.000032   4308.67        0
ma1    -0.231903    0.000103  -2252.38        0
ma2     0.200755    0.000146   1378.90        0
ma3    -0.006765    0.000004  -1662.03        0
ma4    -0.347592    0.000118  -2938.63        0
ma5     0.336234    0.000067   5008.16        0
ma6     0.136536    0.000331    412.02        0
ma7    -0.346302    0.000170  -2038.25        0
ma8     0.448850    0.000028  15997.18        0
ma9    -1.371759    0.000816  -1681.73        0
ma10   -0.033945    0.000046   -735.19        0
omega  -0.947041    0.001079   -877.72        0
alpha1 -0.386418    0.000124  -3120.09        0
beta1   0.875887    0.000227   3858.24        0
gamma1 -0.303940    0.000381   -798.69        0
skew    0.721121    0.000122   5933.58        0

LogLikelihood : 486 

Information Criteria
------------------------------------
                    
Akaike       -4.5698
Bayes        -4.0915
Shibata      -4.6051
Hannan-Quinn -4.3762

Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
                          statistic p-value
Lag[1]                        2.234  0.1350
Lag[2*(p+q)+(p+q)-1][68]     22.335  1.0000
Lag[4*(p+q)+(p+q)-1][114]    38.545  0.9999
d.o.f=23
H0 : No serial correlation

Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
                        statistic p-value
Lag[1]                      3.788 0.05163
Lag[2*(p+q)+(p+q)-1][5]     4.638 0.18454
Lag[4*(p+q)+(p+q)-1][9]     5.538 0.35487
d.o.f=2

Weighted ARCH LM Tests
------------------------------------
            Statistic Shape Scale P-Value
ARCH Lag[3]   0.01662 0.500 2.000  0.8974
ARCH Lag[5]   1.46976 1.440 1.667  0.6003
ARCH Lag[7]   1.97098 2.315 1.543  0.7235

Nyblom stability test
------------------------------------
Joint Statistic:  no.parameters>20 (not available)
Individual Statistics:               
mu     0.026996
ar1    0.011957
ar2    0.012313
ar3    0.011328
ar4    0.013683
ar5    0.015958
ar6    0.013125
ar7    0.007785
ar8    0.008588
ar9    0.007616
ar10   0.006301
ar11   0.006253
ar12   0.008755
ar13   0.009341
ma1    0.013380
ma2    0.007712
ma3    0.016855
ma4    0.013099
ma5    0.007400
ma6    0.009297
ma7    0.013352
ma8    0.013728
ma9    0.013263
ma10   0.007913
omega  0.386518
alpha1 0.245545
beta1  0.405043
gamma1 0.825370
skew   0.776156

Asymptotic Critical Values (10% 5% 1%)
Individual Statistic:	 0.35 0.47 0.75

Sign Bias Test
------------------------------------
                   t-value   prob sig
Sign Bias            1.027 0.3056    
Negative Sign Bias   1.329 0.1853    
Positive Sign Bias   1.295 0.1968    
Joint Effect         5.945 0.1143    


Adjusted Pearson Goodness-of-Fit Test:
------------------------------------
  group statistic p-value(g-1)
1    20      24.6      0.17413
2    30      33.7      0.25041
3    40      56.8      0.03258
4    50      63.0      0.08625


Parametry skośności i odpowiedzialne za efekt EGARCH są trochę niestabilne (wartość powyżej 0,47), ale poza tym jest ok - nie ma autokorelacji w resztach, więc uwzględniliśmy wszystkie autokorelacje, także w kwadratach reszt, a wszystkie parametry są istotne. Jedynie dopasowanie w grupie 3 w teście dobroci Pearsona jest nieco za słabe - prawdopodobnie rozkład reszt jest nieco inny niż wybrano "snorm", ale jest to duże odchylenie.

Dla upewnienia się czy nie ma gdzieś pomyłki, możemy użyć testu na DAC:

> # DAC w próbie
> dopas_hqc <- fitted(dopasGarch_hqc)
> stopaZwrotuZgodne <- dopas_hqc + residuals(dopasGarch_hqc)
> DACTest(forecast=dopas_hqc, actual=stopaZwrotuZgodne, test="PT")
$Test [1] "Pesaran and Timmermann" $Stat [1] 5.854 $p.value [1] 0.000000002401 $H0 [1] "Independently Distributed" $Decision [1] "Reject H0" $DirAcc [1] 0.71

 0,71 to tyle samo co wcześniej otrzymaliśmy (zmienna kryterium_hqc), więc wszystko się zgadza. Możemy dodatkowo sprawdzić jak wygląda DAC dla ostatnich 50 danych:

> Ost <- 50
> DACTest(forecast = tail(as.numeric(dopas_hqc), Ost), actual = tail(as.numeric(stopaZwrotuZgodne), Ost), test = "AG")
$Test
[1] "Anatolyev and Gerko"

$Stat
[1] 4.756

$p.value
[1] 0.0000009883

$H0
[1] "No Predictability"

$Decision
[1] "Reject  H0"

$DirAcc
[1] 0.78


Akurat tutaj użyłem drugiego rodzaju (AG), który daje najczęściej takie same wartości jak PT. W próbie uczącej model radzi sobie przez cały czas całkiem dobrze, ponieważ systematycznie hipoteza braku prognozowalności zostaje odrzucona.

Przejdziemy teraz stopniowo do próby testowej. Najpierw obliczamy DAC dla całej próby (ucząca + testowa). Estymujemy więc najpierw uzyskany dotychczas model dla całej próby za pomocą ugarchfit():

> dopasGarchCalaProba_hqc <- tryCatch(
+   {
+     ugarchfit(spec = mojGarch_hqc, data = stopaZwrotu, solver = "hybrid")
+   }, warning = function(w) {
+     message("Ostrzeżenie: ", conditionMessage(w))
+     ugarchfit(spec = mojGarch_hqc, data = stopaZwrotu, solver = "hybrid")
+   }
+ )
Ostrzeżenie: prawdopodobny problem zbieżności: 'optim' zwrócił kod= 1
Komunikat ostrzegawczy:
W poleceniu 'arima(data, order = c(modelinc[2], 0, modelinc[3]), include.mean = modelinc[1], ':
  prawdopodobny problem zbieżności: 'optim' zwrócił kod= 1
> dopasGarchCalaProba_hqc
*---------------------------------* * GARCH Model Fit * *---------------------------------* Conditional Variance Dynamics ----------------------------------- GARCH Model : eGARCH(1,1) Mean Model : ARFIMA(13,0,10) Distribution : snorm Optimal Parameters ------------------------------------ Estimate Std. Error t value Pr(>|t|) mu 0.000876 0.000000 3244.73 0 ar1 -0.527835 0.000196 -2694.35 0 ar2 0.684617 0.000062 11063.85 0 ar3 -0.142261 0.000398 -357.86 0 ar4 -0.820883 0.000061 -13496.80 0 ar5 0.293763 0.000021 14042.21 0 ar6 0.452103 0.000086 5227.05 0 ar7 -0.232687 0.000099 -2340.97 0 ar8 -0.070984 0.000108 -656.74 0 ar9 0.357832 0.000037 9762.27 0 ar10 0.020505 0.000048 427.04 0 ar11 -0.185835 0.000043 -4301.92 0 ar12 0.003256 0.000011 298.65 0 ar13 0.069660 0.000171 408.22 0 ma1 0.571280 0.000127 4507.56 0 ma2 -0.660489 0.000091 -7260.13 0 ma3 0.161063 0.000917 175.66 0 ma4 0.835642 0.000242 3456.81 0 ma5 -0.327134 0.000012 -26400.84 0 ma6 -0.538823 0.000154 -3503.44 0 ma7 0.192344 0.000114 1683.00 0 ma8 0.130609 0.000157 833.86 0 ma9 -0.234300 0.000108 -2175.97 0 ma10 -0.237432 0.000026 -9220.62 0 omega -0.765569 0.000111 -6870.67 0 alpha1 -0.254834 0.000028 -9192.27 0 beta1 0.893351 0.000117 7658.60 0 gamma1 -0.320261 0.000257 -1247.88 0 skew 0.815441 0.001631 500.05 0 Robust Standard Errors: Estimate Std. Error t value Pr(>|t|) mu 0.000876 0.000002 414.684 0 ar1 -0.527835 0.001263 -417.874 0 ar2 0.684617 0.000229 2984.727 0 ar3 -0.142261 0.001288 -110.408 0 ar4 -0.820883 0.000460 -1784.982 0 ar5 0.293763 0.000459 640.153 0 ar6 0.452103 0.000029 15504.063 0 ar7 -0.232687 0.000452 -514.996 0 ar8 -0.070984 0.000447 -158.903 0 ar9 0.357832 0.000662 540.440 0 ar10 0.020505 0.000189 108.288 0 ar11 -0.185835 0.000216 -861.202 0 ar12 0.003256 0.000051 64.180 0 ar13 0.069660 0.001136 61.315 0 ma1 0.571280 0.000962 593.674 0 ma2 -0.660489 0.000891 -741.613 0 ma3 0.161063 0.004921 32.733 0 ma4 0.835642 0.001461 571.974 0 ma5 -0.327134 0.000133 -2452.617 0 ma6 -0.538823 0.000077 -6953.614 0 ma7 0.192344 0.000347 554.317 0 ma8 0.130609 0.000598 218.284 0 ma9 -0.234300 0.000451 -519.405 0 ma10 -0.237432 0.000342 -694.913 0 omega -0.765569 0.000321 -2386.021 0 alpha1 -0.254834 0.000660 -386.185 0 beta1 0.893351 0.001459 612.425 0 gamma1 -0.320261 0.000021 -15025.018 0 skew 0.815441 0.004400 185.319 0 LogLikelihood : 512.7 Information Criteria ------------------------------------ Akaike -4.2057 Bayes -3.7722 Shibata -4.2330 Hannan-Quinn -4.0309 Weighted Ljung-Box Test on Standardized Residuals ------------------------------------ statistic p-value Lag[1] 1.434 0.2310 Lag[2*(p+q)+(p+q)-1][68] 25.530 1.0000 Lag[4*(p+q)+(p+q)-1][114] 42.082 0.9988 d.o.f=23 H0 : No serial correlation Weighted Ljung-Box Test on Standardized Squared Residuals ------------------------------------ statistic p-value Lag[1] 1.396 0.2374 Lag[2*(p+q)+(p+q)-1][5] 3.374 0.3430 Lag[4*(p+q)+(p+q)-1][9] 6.010 0.2971 d.o.f=2 Weighted ARCH LM Tests ------------------------------------ Statistic Shape Scale P-Value ARCH Lag[3] 2.544 0.500 2.000 0.1107 ARCH Lag[5] 3.226 1.440 1.667 0.2586 ARCH Lag[7] 5.209 2.315 1.543 0.2042 Nyblom stability test ------------------------------------ Joint Statistic: no.parameters>20 (not available) Individual Statistics: mu 0.02266 ar1 0.02024 ar2 0.02111 ar3 0.02888 ar4 0.01956 ar5 0.02272 ar6 0.02255 ar7 0.15612 ar8 0.02344 ar9 0.02388 ar10 0.02475 ar11 0.02319 ar12 0.02538 ar13 0.02162 ma1 0.02144 ma2 0.02181 ma3 0.03071 ma4 0.02657 ma5 0.02350 ma6 0.06516 ma7 0.02290 ma8 0.02397 ma9 0.02463 ma10 0.02744 omega 0.02269 alpha1 0.02531 beta1 0.18295 gamma1 0.06803 skew 0.03629 Asymptotic Critical Values (10% 5% 1%) Individual Statistic: 0.35 0.47 0.75 Sign Bias Test ------------------------------------ t-value prob sig Sign Bias 0.0987 0.9215 Negative Sign Bias 1.3704 0.1719 Positive Sign Bias 1.1653 0.2451 Joint Effect 3.2359 0.3567 Adjusted Pearson Goodness-of-Fit Test: ------------------------------------ group statistic p-value(g-1) 1 20 20.96 0.3392 2 30 37.65 0.1303 3 40 46.52 0.1903 4 50 46.96 0.5563

 

Kryteria informacyjne pogorszyły się. 

W końcu dochodzimy do prognozy kroczącej z reestymacją.

# Prognoza z reestymacją

klaster <- makeCluster(detectCores() - 1)

prognozaGarchReest_hqc <- ugarchroll(spec = mojGarch_hqc, data = stopaZwrotu, forecast.length = wyrzuc,

                                n.start = NULL, refit.every = 1, refit.window = "recursive",

                                window.size = NULL, solver = "hybrid", calculate.VaR = TRUE, 

                                VaR.alpha = c(0.01, 0.05),

                                cluster = klaster)

prognozaGarchReest_hqc <- resume(prognozaGarchReest_hqc)

stopCluster(klaster)


Jak widać znów stosujemy klaster. Funkcja resume() natomiast stosowana po to, by w razie braku konwergencji podczas obliczeń, jeszcze raz oszacować parametry przy pomocy innego algorytmu.

Po zakończeniu dostajemy obiekt, na którym możemy wykonać różne funkcje, jak plot(), jednakże lepiej samemu utworzyć taką funkcję, którą łatwiej sterować. Najpierw wyciągamy wartości oczekiwane:

prognozaReest_hqc <- prognozaGarchReest_hqc@forecast$density$Mu

i obliczymy jej DAC:

> DACTest(forecast=prognozaReest_hqc, 
+         actual=tail(as.numeric(stopaZwrotu), wyrzuc),
+         test="PT")
$Test
[1] "Pesaran and Timmermann"

$Stat
[1] 1.576

$p.value
[1] 0.05755

$H0
[1] "Independently Distributed"

$Decision
[1] "Fail to Reject H0"

$DirAcc
[1] 0.6333


Chociaż prognoza nie zdała testu prognozowalności kierunku, to otrzymane 63% poprawnych odpowiedzi jest to sporo więcej niż bez reestymacji (było 53%). Niemniej przy 30 danych (dla przypomnienia jest to prognoza próby testowej) musimy dostać min. 65%, żeby uznać wynik za coś więcej jak przypadek.

Wyciągnijmy daty utworzonej prognozy (są to daty próby testowej) i zamieńmy w xts:

datyTest <- as.Date(rownames(prognozaGarchReest_hqc@forecast$density))
 

Konwertujemy prognozę z reestymacją na obiekt xts:

prognozaReest_xts <- xts(prognozaReest_hqc, order.by = datyTest)


Mamy już prognozę z reestymacją, ale jeszcze potrzebujemy bez reestymacji, a więc ten sam kod, co stosowany poprzednio:

prognozaGarch_hqc <- ugarchforecast(dopasGarch_hqc, n.ahead = 1, n.roll = wyrzuc - 1)
prognoza_hqc <- as.numeric(fitted(prognozaGarch_hqc))
prognoza_xts <- xts(prognoza_hqc, order.by = datyTest)

Zauważmy tylko dwie rzeczy:
(1) n.roll zostało ustawione na 29 danych (wyrzuc = 30), dlatego że pierwsza prognoza nie dotyczy rolowania (n.ahead = 1). 
(2) obiekt xts dla prognozy bez reestymacji ma te same daty co z reestymacją, dlatego je tutaj znów wykorzystujemy. W tym miejscu jedna mała uwaga. Sama funkcja fitted() na obiekcie ugarchforecast:

> prognoza_hqc <- fitted(prognozaGarch_hqc)
> prognoza_hqc
    2024-03-15 2024-03-22 2024-03-28 2024-04-05 2024-04-12 2024-04-19 2024-04-26 2024-05-02
T+1   0.004522   -0.02022    0.02879   -0.01329     0.0068   0.001236   0.006209   -0.02933
    2024-05-10 2024-05-17 2024-05-24 2024-05-31 2024-06-07 2024-06-14 2024-06-21 2024-06-28
T+1   -0.02474    0.03675   -0.03833  -0.005701   -0.02924    0.02577   -0.01997    0.02392
    2024-07-05 2024-07-12 2024-07-19 2024-07-26 2024-08-02 2024-08-09 2024-08-16 2024-08-23
T+1   -0.01662   -0.06338    0.02571   -0.02336   -0.01967   -0.07568    0.09247   -0.05133
    2024-08-30 2024-09-06 2024-09-13 2024-09-20 2024-09-27 2024-10-04
T+1   0.009721   -0.02933   -0.04628    0.01524  -0.006271    0.05237


jest tablicą, której nazwy kolumn są datami, ale nie dotyczącymi okresu realizacji prognozy, tylko powstania prognozy. Nazwy wierszy dopiero pokazują okres, którego dotyczy prognoza. tak że "T+1"  oznacza datę z kolumny powiększoną o 1 okres, czyli tutaj jeden tydzień. Dlatego właśnie ostatnia data kończy się na 04.10.2024, a nie 11.10.2024. Stąd nie możemy bezpośrednio wyciągnąć stąd dat i potraktować jako daty prognozy. Inaczej sprawa wygląda w przypadku obiektu ugarchroll:

> prognozaGarchReest_hqc

*-------------------------------------*
*              GARCH Roll             *
*-------------------------------------*
No.Refits		: 30
Refit Horizon	: 1
No.Forecasts	: 30
GARCH Model		: eGARCH(1,1)
Distribution	: snorm 

Forecast Density:
                Mu  Sigma   Skew Shape Shape(GIG) Realized
2024-03-22  0.0045 0.0115 0.7211     0          0   0.0113
2024-03-28  0.0004 0.0195 0.9559     0          0   0.0242
2024-04-05  0.0055 0.0188 0.9982     0          0   0.0141
2024-04-12 -0.0166 0.0130 0.8980     0          0  -0.0100
2024-04-19  0.0207 0.0110 0.9934     0          0   0.0073
2024-04-26  0.0036 0.0229 0.7858     0          0   0.0061

..........................
                Mu  Sigma   Skew Shape Shape(GIG) Realized
2024-09-06  0.0229 0.0184 2.5994     0          0  -0.0397
2024-09-13 -0.0048 0.0302 0.8268     0          0   0.0079
2024-09-20 -0.0065 0.0379 0.7288     0          0  -0.0245
2024-09-27  0.0121 0.0353 0.8327     0          0   0.0442
2024-10-04  0.0075 0.0290 0.8249     0          0  -0.0391
2024-10-11  0.0048 0.0327 0.8784     0          0   0.0183

 
Tutaj ostatnia data faktycznie kończy się 11.10.2024, a prognozy są od razu porównywalne z prawdziwymi obserwacjami. Dlatego te daty mogliśmy od razu wyciągnąć i użyć.

Mamy zatem prognozę krocząca z reestymacją (prognozaReest_xts) oraz prognozę kroczącą bez reestymacji (prognoza_xts). Porównajmy je w końcu na wykresie. Aby zobaczyć je w szerszej perspektywie, dodamy też faktyczną stopę zwrotu:

plot(prognoza_xts, col = "blue", lwd = 2)
lines(prognozaReest_xts, col = "red", lwd = 2)
lines(stopaZwrotu, col="gray", lwd=2)
addLegend(legend.loc = "topleft", legend.names = c("Prognoza krocząca bez reestymacji", 
                                                   "Prognoza krocząca z reestymacją", 
                                                   "Tygodniowa stopa zwrotu WIG20"),
          lwd=3, bty="o", cex=1.05, col=c("blue", "red", "gray"))




Wygląda na to, że rzeczywiście prognoza reestymowana (czerwona) jest lepsza. 

Brakuje tu jeszcze prognozy na kolejny tydzień. Niestety ten artykuł długo piszę, więc dotyczyć będzie ona już kończącego się tygodnia. Chcemy stworzyć prognozę dla obu rodzajów. W przypadku niereestymowanym sytuacja jest prosta - wystarczy, że dodamy jeden okres do n.roll w ugarchforecast():

prognozaGarch_hqc <- ugarchforecast(dopasGarch_hqc, n.ahead = 1, n.roll = wyrzuc)
prognoza_hqc <- as.numeric(fitted(prognozaGarch_hqc))

Jednak zamiana na xts jest już troszkę trudniejsza, bo nie mamy nigdzie ustawionej daty z przyszłości:

czest <- "week"
nowaData <- seq(from = datyTest[length(datyTest)], by = czest, length.out = 2)[-1]
datyWykres <- c(datyTest, nowaData)
prognoza_xts <- xts(prognoza_hqc, order.by = datyWykres)

W przypadku prognozy reestymowanej nie ma możliwości bezpośredniego stworzenia prognozy poza próbę testową. Stąd należy po prostu wykonać zwykłą prognozę na całej próbie (tylko próba ucząca). Efekt będzie ten sam, bo to nic innego jak reestymacja po całej próbie:

prognozaGarchCalaProba_hqc <- ugarchforecast(dopasGarchCalaProba_hqc, n.ahead = 1)
prognozaCalaProba_hqc <- as.numeric(fitted(prognozaGarchCalaProba_hqc))
prognozaReest_xts <- xts(c(prognozaReest_hqc, prognozaCalaProba_hqc), 
                         order.by = datyWykres)
  
I wtedy powtarzamy kod na wykres:

plot(prognoza_xts, col = "blue", lwd = 2)
lines(prognozaReest_xts, col = "red", lwd = 2)
lines(stopaZwrotu, col="gray", lwd=2)
addLegend(legend.loc = "topleft", legend.names = c("Prognoza krocząca bez reestymacji", 
                                                   "Prognoza krocząca z reestymacją", 
                                                   "Tygodniowa stopa zwrotu WIG20"),
          lwd=3, bty="o", cex=1.05, col=c("blue", "red", "gray"))



Biorąc pod uwagę, że ta z reestymacją jest bardziej wiarygodna, w tym tygodniu bardziej prawdopodobny jest wzrost.


2. Wg DAC w próbie testowej

# budowa modelu wg DAC
mojGarch_DAC <- ugarchspec(variance.model = list(model = model_Garch, submodel = submodel_Garch,
                                             garchOrder = c(1, 1)), 
                       mean.model = list(armaOrder = c(rzadAR_DAC, rzadMA_DAC), archm = czy_archm_DAC), 
                       distribution.model = rozklad)

dopasGarch_DAC <- ugarchfit(spec = mojGarch_DAC, data = stopaZwrotu, out.sample = wyrzuc, solver="hybrid")
dopasGarch_DAC

*---------------------------------*
*          GARCH Model Fit        *
*---------------------------------*

Conditional Variance Dynamics 	
-----------------------------------
GARCH Model	: eGARCH(1,1)
Mean Model	: ARFIMA(12,0,8)
Distribution	: snorm 

Optimal Parameters
------------------------------------
        Estimate  Std. Error   t value Pr(>|t|)
mu      0.001648    0.000000   4997.32        0
ar1    -0.663854    0.000077  -8578.89        0
ar2    -0.262185    0.000024 -10887.50        0
ar3    -0.051030    0.000004 -11599.32        0
ar4     0.309857    0.000032   9822.05        0
ar5     0.342351    0.000041   8407.70        0
ar6     0.359999    0.000043   8357.61        0
ar7     0.580322    0.000067   8602.21        0
ar8     0.010979    0.000001   8979.89        0
ar9     0.137562    0.000004  39254.41        0
ar10    0.026932    0.000003   7747.40        0
ar11    0.104187    0.000017   6313.71        0
ar12   -0.001168    0.000002   -514.54        0
ma1     0.921020    0.000120   7703.09        0
ma2     0.336957    0.000034  10038.87        0
ma3     0.097933    0.000013   7816.74        0
ma4    -0.370421    0.000018 -21002.82        0
ma5    -0.588972    0.000061  -9683.56        0
ma6    -0.596177    0.000061  -9768.79        0
ma7    -0.861720    0.000114  -7588.11        0
ma8    -0.046927    0.000005  -9285.27        0
archm  -0.029292    0.000004  -7966.21        0
omega  -0.808888    0.000100  -8114.56        0
alpha1 -0.311233    0.000853   -364.68        0
beta1   0.891072    0.000109   8198.37        0
gamma1 -0.364227    0.000340  -1070.40        0
skew    0.561526    0.002689    208.79        0

Robust Standard Errors:
        Estimate  Std. Error    t value Pr(>|t|)
mu      0.001648    0.000000  11903.419        0
ar1    -0.663854    0.000158  -4196.632        0
ar2    -0.262185    0.000105  -2494.059        0
ar3    -0.051030    0.000011  -4762.124        0
ar4     0.309857    0.000099   3118.566        0
ar5     0.342351    0.000054   6362.322        0
ar6     0.359999    0.000174   2072.837        0
ar7     0.580322    0.000191   3039.481        0
ar8     0.010979    0.000003   3309.169        0
ar9     0.137562    0.000056   2467.595        0
ar10    0.026932    0.000001  29139.146        0
ar11    0.104187    0.000020   5260.849        0
ar12   -0.001168    0.000003   -392.560        0
ma1     0.921020    0.000310   2975.374        0
ma2     0.336957    0.000039   8634.698        0
ma3     0.097933    0.000004  22486.872        0
ma4    -0.370421    0.000091  -4059.529        0
ma5    -0.588972    0.000149  -3952.367        0
ma6    -0.596177    0.000038 -15636.003        0
ma7    -0.861720    0.000496  -1736.513        0
ma8    -0.046927    0.000008  -6015.220        0
archm  -0.029292    0.000023  -1255.820        0
omega  -0.808888    0.000116  -6952.590        0
alpha1 -0.311233    0.002262   -137.605        0
beta1   0.891072    0.000602   1479.867        0
gamma1 -0.364227    0.000235  -1552.219        0
skew    0.561526    0.006557     85.641        0

LogLikelihood : 457 

Information Criteria
------------------------------------
                    
Akaike       -4.2996
Bayes        -3.8543
Shibata      -4.3305
Hannan-Quinn -4.1194

Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
                         statistic p-value
Lag[1]                     0.02059  0.8859
Lag[2*(p+q)+(p+q)-1][59]  21.65058  1.0000
Lag[4*(p+q)+(p+q)-1][99]  35.98965  0.9985
d.o.f=20
H0 : No serial correlation

Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
                        statistic p-value
Lag[1]                      0.565  0.4523
Lag[2*(p+q)+(p+q)-1][5]     5.352  0.1272
Lag[4*(p+q)+(p+q)-1][9]     6.283  0.2670
d.o.f=2

Weighted ARCH LM Tests
------------------------------------
            Statistic Shape Scale P-Value
ARCH Lag[3]     2.216 0.500 2.000  0.1366
ARCH Lag[5]     2.468 1.440 1.667  0.3768
ARCH Lag[7]     2.517 2.315 1.543  0.6090

Nyblom stability test
------------------------------------
Joint Statistic:  no.parameters>20 (not available)
Individual Statistics:              
mu     0.02688
ar1    0.03818
ar2    0.03886
ar3    0.03903
ar4    0.03875
ar5    0.03790
ar6    0.03681
ar7    0.18916
ar8    0.03516
ar9    0.03328
ar10   0.03355
ar11   0.02755
ar12   0.03261
ma1    0.03305
ma2    0.03679
ma3    0.03647
ma4    0.03476
ma5    0.01836
ma6    0.03795
ma7    0.35048
ma8    0.03726
archm  0.02762
omega  0.02795
alpha1 0.01723
beta1  0.04913
gamma1 0.07816
skew   0.20351

Asymptotic Critical Values (10% 5% 1%)
Individual Statistic:	 0.35 0.47 0.75

Sign Bias Test
------------------------------------
                   t-value   prob sig
Sign Bias           0.4007 0.6890    
Negative Sign Bias  1.0956 0.2746    
Positive Sign Bias  0.4800 0.6317    
Joint Effect        1.4322 0.6980    


Adjusted Pearson Goodness-of-Fit Test:
------------------------------------
  group statistic p-value(g-1)
1    20      12.2       0.8769
2    30      37.0       0.1462
3    40      34.0       0.6970
4    50      34.0       0.9491


Model jest poprawny.

Cały kod jest taki sam, a jedynie tam gdzie trzeba, należy zastąpić końcówkę "_hqc" przez "_DAC". 
W samej próbie prognoza kierunku jest stosunkowo słaba, bo jedynie 63,5% poprawnych odpowiedzi, ale test odrzucił losowość. Ostatnie 50 danych prognozowanych jest już lepiej (68%), jednak w przypadku HQC było 78%. Dopiero w próbie testowej dostaniemy poprawę (prawie 77%). Pytanie, czy jest to prawdziwa poprawa? Aby odpowiedzieć na to pytanie, tworzymy prognozę z reestymacją.


# Prognoza z reestymacją
klaster <- makeCluster(detectCores() - 1)
prognozaGarchReest_DAC <- ugarchroll(spec = mojGarch_DAC, data = stopaZwrotu, forecast.length = wyrzuc,
                                     n.start = NULL, refit.every = 1, refit.window = "recursive",
                                     window.size = NULL, solver = "hybrid", calculate.VaR = TRUE, 
                                     VaR.alpha = c(0.01, 0.05),
                                     cluster = klaster)
prognozaGarchReest_DAC <- resume(prognozaGarchReest_DAC)
stopCluster(klaster)

prognozaReest_DAC <- prognozaGarchReest_DAC@forecast$density$Mu

DACTest(forecast=prognozaReest_DAC, 
        actual=tail(as.numeric(stopaZwrotu), wyrzuc),
        test="PT")

Niestety DAC spada do 60% tracąc istotność. Czyli w żadnym przypadku nie uzyskałem modelu prognozującego znak stopy zwrotu w sposób wiarygodny. Zanim to przeanalizuję, robię ten wykres dla modelu wg DAC (out of sample):



Oba typy prognoz wskazują, że ten tydzień będzie rosnący. Predykcja zostaje wzmocniona przez HQC, którego prognoza reestymująca również sugeruje wzrost.  Między prognozami prognozaReest_hqc i prognozaReest_DAC występuje dość silna korelacja na poziomie 0,67. Załóżmy więc, że strategia będzie polegać na tym, że sygnałem będzie sytuacja, gdy obie prognozy mają ten sam znak. Sprawdzamy DAC dla tej sytuacji:

te_same_znaki <- which(sign(prognozaReest_hqc) ==  sign(prognozaReest_DAC))
DACTest(forecast = prognozaReest_hqc[te_same_znaki], actual = as.numeric(tail(stopaZwrotu, wyrzuc))[te_same_znaki],
        test="PT")


Otrzymałem DAC = 65,2% (p-value = 0.028), co przemawia za zdolnością strategii do prognozowania kierunku lepiej niż rzucanie monetą. 

W tym miejscu warto nadmienić, że użycie drugiego z dostępnych testów DAC - "AG" (Anatolyev-Gerko) przynosi wynik na granicy istotności statystycznej:

> DACTest(forecast = prognozaReest_hqc[te_same_znaki], actual = as.numeric(tail(stopaZwrotu, wyrzuc))[te_same_znaki],
+         test="AG")
$Test
[1] "Anatolyev and Gerko"

$Stat
[1] 1.618

$p.value
[1] 0.05288

$H0
[1] "No Predictability"

$Decision
[1] "Fail to Reject H0"

$DirAcc
[1] 0.6522


Różnica między PT a AG polega na tym, że ten pierwszy bada zdolność prognostyczną znaku zmiennej, a ten drugi średniej warunkowej. Tak więc o ile PT skupia się na badaniu prawdopodobieństwa odgadnięcia znaku stopy zwrotu, o tyle AG szacuje siłę zdolności do prognozowania oczekiwanej stopy zwrotu [Anatolyev, S., Gerko, A. 2005, "A trading approach to testing for predictability, Journal of Business and Economic Statistics, 23(4), 455–461]. Jest więc oczywiste, że zawsze ten drugi będzie miał wyższe p-value, bo po prostu trudniej szacować średnią niż sam jej znak. Test AG ma interpretację bardziej ekonomiczną niż statystyczną, bo nawet jeśli jesteśmy w stanie prognozować znak stopy zwrotu, to nie znaczy jeszcze, że nasze zyski są ponadprzeciętne.