рекомендации

суббота, 8 мая 2021 г.

Тестирование торговых стратегий на исторических данных с помощью Quantstrat. Часть 13. Оптимизация стоп-лоссов.

Первая часть
Вторая часть
Третья часть
Четвертая часть
Пятая часть
Шестая часть
Седьмая часть
Восьмая часть
Девятая часть
Десятая часть
Одиннадцатая часть
Двенадцатая часть

 

Из результатов, полученных в предыдущей части, ясно, что оптимальную величину стоп-лоссов необходимо подбирать так же, как оптимальные значения индикаторов. Этим мы и займемся сейчас.

 

Первая часть скрипта, где задаются первоначальные настройки, остается такой же, как в предыдущей части, поэтому здесь я ее для экономии места приводить не буду.

 

Однако при сохранении настроек в переменных вместо одного значения стоп-лосса в переменной .Stoploss хранится последовательность из 48 чисел от 0,005 до 0,03, то есть величина стоп-лосса варьируется от 0,5 до 3% от размера позиции.

 

# Сохраняем настройки в переменных

.fast <- 7

.slow <- 21

.orderqty <- 100

.txnfees <- -20

.StopLoss <- seq(0.005, 0.03, length.out = 48)

portfolio.st <- "Port.Luxor.Stop.Loss.Opt"

account.st <- "Acct.Luxor.Stop.Loss.Opt"

strategy.st <- "Strat.Luxor.Stop.Loss.Opt"

 

Далее стандартная инициализация объектов нашей стратегии:

 

# Удаляем остатки предыдущих запусков стратегий и очищаем значения нашего портфеля и счета

rm.strat(portfolio.st)

rm.strat(account.st) 

# Инициализация портфеля

initPortf(name = portfolio.st,

  symbols = symbols,

  initDate = init_date)

# Инициализация счета

initAcct(name = account.st,

  portfolios = portfolio.st,

  initDate = init_date)

# Инициализация ордеров

initOrders(portfolio = portfolio.st,

  initDate = init_date)

# Инициализация стратегии

strategy(strategy.st, store = TRUE)

 

Теперь добавляем сигналы:

 

# Сигнал на открытие длинной позиции

add.signal(strategy.st, 

  name = "sigCrossover",

  arguments = list(columns = c("nFast", "nSlow"),

    relationship = "gte"),

  label = "long")

# Сигнал на открытие короткой позиции

add.signal(strategy.st, 

  name = "sigCrossover",

  arguments = list(columns = c("nFast", "nSlow"),

    relationship = "lt"),

  label = "short")

 

И, наконец, правила, в том числе для стоп-лоссов:

 

# Правило открытия длинной позиции

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "long"

    sigval = TRUE,

    replace = FALSE,

    orderside = "long",

    ordertype = "stoplimit",

    prefer = "High",

    TxnFees = .txnfees,

    orderqty = +.orderqty,

    osFUN = osMaxPos,

    orderset = "ocolong"),

  type = "enter",

  label = "EnterLONG")

# Правило открытия короткой позиции

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "short"

    sigval = TRUE,

    replace = FALSE,

    orderside = "short",

    ordertype = "stoplimit",

    prefer = "Low",

    TxnFees = .txnfees,

    orderqty = -.orderqty,

    osFUN = osMaxPos,

    orderset = "ocoshort"),

  type = "enter",

  label = "EnterSHORT")

# Правило закрытия длинной позиции

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "short"

    sigval = TRUE,

    replace = TRUE,

    orderside = "long" ,

    ordertype = "market",

    TxnFees = .txnfees,

    orderqty = "all",

    orderset = "ocolong"),

  type = "exit",

  label = "Exit2SHORT")

# Правило закрытия короткой позиции

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "long"

    sigval = TRUE,

    replace = TRUE,

    orderside = "short",

    ordertype = "market",

    TxnFees = .txnfees,

    orderqty = "all",

    orderset = "ocoshort"),

  type = "exit",

  label = "Exit2LONG")

# Правила для стоп-лоссов

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "long"

    sigval = TRUE,

    replace = FALSE,

    orderside = "long",

    ordertype = "stoplimit",

    tmult = TRUE,

    threshold = quote(.stoploss),

    TxnFees = .txnfees,

    orderqty = "all",

    orderset = "ocolong"),

  type = "chain"

  parent = "EnterLONG",

  label = "StopLossLONG",

  enabled = FALSE)

 

add.rule(strategy.st, 

  name = "ruleSignal",

  arguments = list(sigcol = "short"

    sigval = TRUE,

    replace = FALSE,

    orderside = "short",

    ordertype = "stoplimit",

    tmult = TRUE,

    threshold = quote(.stoploss),

    TxnFees = .txnfees,

    orderqty = "all",

    orderset = "ocoshort"),

  type = "chain"

  parent = "EnterSHORT",

  label = "StopLossSHORT",

  enabled = FALSE)

 

Добавляем лимит позиции:

 

for(symbol in symbols){

  addPosLimit(portfolio = portfolio.st,

              symbol = symbol,

              timestamp = init_date,

              maxpos = .orderqty)

}

 

Добавление распределения

 

Мы снова используем add.distribution, чтобы назначить наш вектор .StopLoss в качестве значений для цепочек правил StopLossLONG и StopLossSHORT.

 

# Добавление распределения

add.distribution(strategy.st,

  paramset.label = "StopLoss",

  component.type = "chain",

  component.label = "StopLossLONG",

  variable = list(threshold = .StopLoss),

  label = "StopLossLONG")

add.distribution(strategy.st,

  paramset.label = "StopLoss",

  component.type = "chain",

  component.label = "StopLossSHORT",

  variable = list(threshold = .StopLoss),

  label = "StopLossSHORT")

 

Добавление ограничения распределения

 

Мы также можем задать ограничение распределения, чтобы параметры StopLossLONG и StopLossSHORT оставались одинаковыми.

 

# Добавляем ограничение для распределений

add.distribution.constraint(strategy.st,

  paramset.label = "StopLoss",

  distribution.label.1 = "StopLossLONG",

  distribution.label.2 = "StopLossSHORT",

  operator = "==",

  label = "StopLoss")

 

Далее активируем наши правила:

 

# Активация правил

enable.rule(strategy.st, 'chain', 'StopLoss')

 

Для ускорения процесса можно активировать поддержку параллельных вычислений:

 

# Активация параллельных вычислений

library(parallel)

 

if( Sys.info()['sysname'] == "Windows") {

  library(doParallel)

  registerDoParallel(cores=detectCores())

} else {

  library(doMC)

  registerDoMC(cores=detectCores())

}

 

И запускаем оптимизацию:

 

# Запуск оптимизации

results <- apply.paramset(strategy.st, 

  paramset.label = "StopLoss"

  portfolio.st = portfolio.st, 

  account.st = account.st, 

  nsamples = .nsamples, 

  verbose = TRUE)

 

Теперь сохраняем результаты оптимизации в переменной tS:

 

tS <- results$tradeStats

 

Если мы использовали ограничения распределения, то есть величина стоп-лосса для длинный и коротких позиций одинакова, тогда для визуализации результатов можно просто построить график зависимости прибыли от величины стоп-лосса:

 

# График зависимости прибыли от величины стоп-лосса

ggplot(data = tS, aes(x = StopLossLONG, y = End.Equity)) + 

  geom_line(color = "blue", size = 2) + 

  xlab("Стоп-лосс") + 

  ylab("Чистая прибыль") +

  ggtitle("Зависимость прибыли от величины стоп-лосса")

 

 

# чистая прибыль/убыток

z <- tapply(X = tS[,"End.Equity"], INDEX = list(Long=tS[,1], Short = tS[,2]), FUN = sum)

x <- as.numeric(rownames(z))

y <- as.numeric(colnames(z))

filled.contour(x = x, y = y, z = z, color = heat.colors, xlab="StopLoss Long", ylab = "StopLoss Short")

title("Чистая прибыль")

 

# максимальная просадка

z <- tapply(X = tS[,"Max.Drawdown"], INDEX = list(Long=tS[,1], Short=tS[,2]), FUN = sum)

x <- as.numeric(rownames(z))

y <- as.numeric(colnames(z))

filled.contour(x = x, y = y, z = z, color = heat.colors, xlab = "StopLoss Long", ylab = "StopLoss Short")

title("Максимальная просадка")

 

 

Если ограничения не активировались, перебираются все возможные пары значений стоп-лоссов для длинных и коротких значений, и процесс занимает намного больше времени. В данном случае для визуализации результатов оптимизации лучше всего подходят тепловые карты, как и в случае оптимизации величин индикаторов. 

 

Можно также построить гистограмму распределения прибылей и убытков:

 

# Построение гистограммы распределения прибылей

profit_gr <- ggplot(tS, aes(x=End.Equity)) + 

geom_density(fill = "lightblue",color="white"

profit_gr + xlab("Общая прибыль") + 

ggtitle("Распределение прибылей стратегии пересечения двух скользящих средних")

 

Комментариев нет:

Отправить комментарий