Оптимизация параметров с помощью пользовательских индикаторов — Quantstrat

Я ищу примеры кодов оптимизации параметров в Quantstrat при использовании пользовательских индикаторов. Большинство примеров, которые я могу найти в Интернете, используют SMA, MACD и другие классические индикаторы. Мне это не очень помогает, так как я хотел бы в основном оптимизировать торговые системы с помощью пользовательских индикаторов.

Есть ли у кого-нибудь соответствующие ссылки или материалы?

Чтобы быть более конкретным, я впервые пробую вариант оптимизации индикатора. Индикатор, который я использую, — это импульс леса MACD. Я пытаюсь оценить эффективность этого индикатора по сравнению с обычным пересечением сигнальной линии MACD. Но я не хочу слишком зацикливаться на этом конкретном индикаторе, так как моя цель состоит в том, чтобы иметь жизнеспособный код оптимизации для будущих пользовательских индикаторов, которые я буду использовать.

Вот код, данные представляют собой внутридневные котировки EURUSD (5-минутные свечи):

library(quantstrat)


Sys.setenv(TZ = "UTC")

currency(c('EUR', 'USD'))

exchange_rate(c('EURUSD'), tick_size=0.0001)


init_date <- "2018-01-07"
start_date <- "2018-01-08"
end_date <- "2018-01-08"
init_equity <- 1e8 # $100,000,000
adjustment <- FALSE

basic_symbols <- function() {
  symbols <- c(
       "EUR" 
      )
}

  symbols <- basic_symbols()


portfolio.st <- "Port.Luxor.Opt"  
account.st <- "Acct.Luxor.Opt"
strategy.st <- "Strat.Luxor.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,
         initEq = init_equity)

initOrders(portfolio = portfolio.st,
           symbols = symbols,
           initDate = init_date)

strategy(strategy.st, store = TRUE) 

fastMA_custom = 12
fastMA_custom2 = 12
slowMA_custom = 26
slowMA_custom2 = 26
signalMA_custom = 9
signalMA_custom2 = 9
maType="EMA"
MAforest = 3

forest <- function(x){
  step1 <- EMA(x,fastMA_custom)
  step2 <- EMA(x,slowMA_custom)
  step3 <- step1-step2
  step4 <- EMA(step3,signalMA_custom)
  step5 <- step3-step4
  return(step5)
}

smaforest <- function(x){
  step1 <- EMA(x,fastMA_custom2)
  step2 <- EMA(x,slowMA_custom2)
  step3 <- step1-step2
  step4 <- EMA(step3,signalMA_custom2)
  step5 <- step3-step4
  step6 <- EMA(step5,MAforest)
  return(step6)
}

add.indicator(strategy = strategy.st,
              name ="forest",
              arguments = list(x=quote(Cl(mktdata))),
              label="forest")

add.indicator(strategy=strategy.st,
              name ="smaforest",
              arguments = list(x=quote(Cl(mktdata))),
              label="smaforest")

add.distribution(strategy.st,
                 paramset.label = "forestopt",
                 component.type = "indicator",
                 component.label = "forest",
                 variable = list(fastMA_custom= 8:14),
                 label = "fastMA_custom")

add.signal(strategy = strategy.st,
           name="sigCrossover",
           arguments = list(columns = c("forest", "smaforest"),
                            relationship = "gte"),
           label = "long")

add.signal(strategy = strategy.st,
           name="sigCrossover",
           arguments = list(columns = c("forest", "smaforest"),
                            relationship = "lte"),
           label = "short")

add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(sigcol = "long",
                          sigval = TRUE,
                          orderqty = 100000,
                          ordertype = "market",
                          orderside = "long", 
                          TxnFees = -1, 
                          replace = FALSE),
         type = "enter",
         label = "EnterLONG")

add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(sigcol = "short",
                          sigval = TRUE,
                          orderqty = -100000,
                          ordertype = "market",
                          orderside = "short", 
                          replace = FALSE, 
                          TxnFees = -1
                          ),
         type = "enter",
         label = "EnterSHORT")

add.rule(strategy.st, 
         name = "ruleSignal", 
         arguments = list(sigcol = "short", 
                          sigval = TRUE, 
                          orderside = "long", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -1, 
                          replace = TRUE), 
         type = "exit", 
         label = "Exit2SHORT")


add.rule(strategy.st, 
         name = "ruleSignal", 
         arguments = list(sigcol = "long", 
                          sigval = TRUE, 
                          orderside = "short", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -1, 
                          replace = TRUE), 
         type = "exit", 
         label = "Exit2LONG")

addPosLimit(portfolio.st, 'EUR', timestamp=initDate, maxpos=500, minpos=0)


resultsopt <- apply.paramset(strategy.st,
                          paramset.label = "forestopt",
                          portfolio.st = portfolio.st,
                          account.st = account.st, 
                          nsamples = 0)

Тестирование на истории без части оптимизации работает отлично. Это действительно бит оптимизации, который вызывает ошибку.

Вот ошибка, которую я получаю:

ошибка при вызове функции объединения: «simpleError in fun (результат 1, результат 2, результат 3, результат 4, результат 5, результат 6, результат 7): попытка выбрать менее одного элемента»


person Olivier.S    schedule 03.02.2018    source источник
comment
оптимизация работает точно так же с пользовательскими функциями индикатора. Вы не предоставили много усилий/деталей. Почему бы вам не привести пример кода, показывающий, в чем ваша проблема с попыткой заставить работать пользовательский индикатор.   -  person FXQuantTrader    schedule 04.02.2018
comment
Вы абсолютно правы. Я вставил один пример кода оптимизации, который возвращает ошибку.   -  person Olivier.S    schedule 07.02.2018
comment
Есть ли у кого-нибудь соответствующие ссылки или материалы? Другие примеры оптимизации в quantstrat можно найти на SO. Искать с помощью [quantstrat]   -  person FXQuantTrader    schedule 08.02.2018


Ответы (1)


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

Вот пример кода, очень похожий на ваш, который работает над оптимизацией пользовательского индикатора. Надеюсь, станет ясно, как вы должны настроить свой код. Вы делаете несколько вещей неправильно. В частности, вы должны указать формальные аргументы для forest или любой другой пользовательской функции, которую вы хотите оптимизировать в quantstrat. Как правило, это плохая идея — определять все ваши переменные «n» глобально по умолчанию. Укажите их непосредственно в аргументах вызова add.indicator. Точно так же, как это сделано ниже для функции forest.

library(quantstrat)


Sys.setenv(TZ = "UTC")

currency(c('USD'))

symbols <- "AAPL"
getSymbols(symbols)

stock(symbols, currency = "USD")


AAPL <- AAPL["2016/"]

init_date <- "2017-01-07"
start_date <- "2018-01-31"
end_date <- "2018-01-31"
init_equity <- 1e8 # $100,000,000
adjustment <- FALSE


portfolio.st <- "Luxor.Opt"  
account.st <- "Luxor.Opt"
strategy.st <- "Luxor.Opt"

rm.strat(name = portfolio.st)   

initPortf(name = portfolio.st, 
          symbols = symbols)

initAcct(name = account.st,
         portfolios = portfolio.st,
         initEq = init_equity)

initOrders(portfolio = portfolio.st,
           symbols = symbols)

strategy(strategy.st, store = TRUE) 


fastMA_custom2 = 12

slowMA_custom2 = 26

signalMA_custom2 = 9
maType="EMA"
MAforest = 3

forest <- function(x, fastMA_custom, slowMA_custom, signalMA_custom){
  step1 <- EMA(x,fastMA_custom)
  step2 <- EMA(x,slowMA_custom)
  step3 <- step1-step2
  step4 <- EMA(step3,signalMA_custom)
  step5 <- step3-step4
  return(step5)
}

smaforest <- function(x){
  step1 <- EMA(x,fastMA_custom2)
  step2 <- EMA(x,slowMA_custom2)
  step3 <- step1-step2
  step4 <- EMA(step3,signalMA_custom2)
  step5 <- step3-step4
  step6 <- EMA(step5,MAforest)
  return(step6)
}

add.indicator(strategy = strategy.st,
              name ="forest",
              arguments = list(x=quote(Cl(mktdata)),
                               fastMA_custom = 12,
                               slowMA_custom = 26,
                               signalMA_custom = 9
                               ),
              label="forest")

add.indicator(strategy=strategy.st,
              name ="smaforest",
              arguments = list(x=quote(Cl(mktdata))),
              label="smaforest")


add.signal(strategy = strategy.st,
           name="sigCrossover",
           arguments = list(columns = c("forest", "smaforest"),
                            relationship = "gte"),
           label = "long")

add.signal(strategy = strategy.st,
           name="sigCrossover",
           arguments = list(columns = c("forest", "smaforest"),
                            relationship = "lte"),
           label = "short")

add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(sigcol = "long",
                          sigval = TRUE,
                          orderqty = 100000,
                          ordertype = "market",
                          orderside = "long", 
                          TxnFees = -1, 
                          replace = FALSE),
         type = "enter",
         label = "EnterLONG")

add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(sigcol = "short",
                          sigval = TRUE,
                          orderqty = -100000,
                          ordertype = "market",
                          orderside = "short", 
                          replace = FALSE, 
                          TxnFees = -1
         ),
         type = "enter",
         label = "EnterSHORT")

add.rule(strategy.st, 
         name = "ruleSignal", 
         arguments = list(sigcol = "short", 
                          sigval = TRUE, 
                          orderside = "long", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -1, 
                          replace = TRUE), 
         type = "exit", 
         label = "Exit2SHORT")


add.rule(strategy.st, 
         name = "ruleSignal", 
         arguments = list(sigcol = "long", 
                          sigval = TRUE, 
                          orderside = "short", 
                          ordertype = "market", 
                          orderqty = "all", 
                          TxnFees = -1, 
                          replace = TRUE), 
         type = "exit", 
         label = "Exit2LONG")

addPosLimit(portfolio.st, symbols[], timestamp=init_date, maxpos=500, minpos=0)

# applyStrategy(strategy.st, portfolio.st)
# 
# updatePortf(portfolio.st)
# tradeStats(portfolio.st, symbols)


# Portfolio Symbol Num.Txns Num.Trades Net.Trading.PL Avg.Trade.PL Med.Trade.PL Largest.Winner Largest.Loser Gross.Profits Gross.Losses Std.Dev.Trade.PL Std.Err.Trade.PL Percent.Positive Percent.Negative Profit.Factor
# AAPL Luxor.Opt   AAPL      127         63       -45123.5    -28493.01     -46000.1        1328999      -1047001      10371975    -12167035           474783         59817.04         42.85714         57.14286     0.8524653
# Avg.Win.Trade Med.Win.Trade Avg.Losing.Trade Med.Losing.Trade Avg.Daily.PL Med.Daily.PL Std.Dev.Daily.PL Std.Err.Daily.PL Ann.Sharpe Max.Drawdown Profit.To.Max.Draw Avg.WinLoss.Ratio Med.WinLoss.Ratio Max.Equity Min.Equity
# AAPL      384147.2      305999.5        -337973.2        -298000.6    -28493.01     -46000.1           474783         59817.04  -0.952672     -3182097        -0.01418043           1.13662          1.026842   985976.4   -2196121
# End.Equity
# AAPL   -45123.5

add.distribution(strategy.st,
                 paramset.label = "forestopt",  #The label we will use when we want to run this optimisation in paramset
                 component.type = "indicator", # The custom function is of indicator type (not other alternatives including signal or rule)
                 component.label = "forest", #this is the name of your custom function
                 variable = list(fastMA_custom = seq(8, 12, by = 2)),
                 label = "myForestOptLabel") #choose whatever you want

resultsopt <- apply.paramset(strategy.st,
                             paramset.label = "forestopt",
                             portfolio.st = portfolio.st,
                             account.st = account.st, 
                             nsamples = 0)

# Check, results are equal in the case where fastMA_custom = 12 compared to when running `applyStrategy`:

resultsopt$tradeStats
# myForestOptLabel   Portfolio Symbol Num.Txns Num.Trades Net.Trading.PL Avg.Trade.PL Med.Trade.PL Largest.Winner Largest.Loser Gross.Profits Gross.Losses Std.Dev.Trade.PL Std.Err.Trade.PL Percent.Positive Percent.Negative
# 1                8 Luxor.Opt.1   AAPL       97         48      2609902.7     23790.64    -34500.75        1405999      -1055001       9615975     -8474024         492362.4         71066.39         43.75000         56.25000
# 2               10 Luxor.Opt.2   AAPL      105         52      -340106.5    -34770.27    -52500.75        1405999      -1047001       8538977    -10347031         480790.5         66673.64         40.38462         59.61538
# 3               12 Luxor.Opt.3   AAPL      127         63       -45123.5    -28493.01    -46000.10        1328999      -1047001      10371975    -12167035         474783.0         59817.04         42.85714         57.14286
# Profit.Factor Avg.Win.Trade Med.Win.Trade Avg.Losing.Trade Med.Losing.Trade Avg.Daily.PL Med.Daily.PL Std.Dev.Daily.PL Std.Err.Daily.PL Ann.Sharpe Max.Drawdown Profit.To.Max.Draw Avg.WinLoss.Ratio Med.WinLoss.Ratio Max.Equity
# 1     1.1347590      457903.6      386998.5        -313852.8        -237000.5     23790.64    -34500.75         492362.4         71066.39  0.7670464     -2444032         1.06786773          1.458976          1.632902  3585933.7
# 2     0.8252586      406617.9      334998.1        -333775.2        -245002.2    -34770.27    -52500.75         480790.5         66673.64 -1.1480282     -3152090        -0.10789872          1.218239          1.367327  1343982.9
# 3     0.8524653      384147.2      305999.5        -337973.2        -298000.6    -28493.01    -46000.10         474783.0         59817.04 -0.9526720     -3182097        -0.01418043          1.136620          1.026842   985976.4
# Min.Equity End.Equity
# 1   -1122012  2609902.7
# 2   -1808107  -340106.5
# 3   -2196121   -45123.5

# Results for  fastMA_custom = 12 are identical.

Выше я также показываю, как вы можете быстро проверить свой вывод apply.paramsets, сравнив его с отдельным случаем через apply.Strategy для n = 12. Проводить такие проверки — хорошая идея.

person FXQuantTrader    schedule 08.02.2018
comment
Спасибо, это помогло мне переписать код более традиционным способом. Хотя ошибка все равно есть. Даже если я скопирую приведенный выше код и запущу его, я получаю: :error, вызывая функцию объединения: simpleError in fun(result.1, result.2, result.3): попытка выбрать меньше одного элемента. Может быть, это связано с моей версией пакетов? - person Olivier.S; 11.02.2018
comment
Код, который я предоставил, отлично работает после вызова devtools::install_github("braverock/quantstrat"). Я не понимаю ошибки, которую вы описываете. - person FXQuantTrader; 12.02.2018
comment
Я обновил свои пакеты до более новых версий. Он работал нормально, как в вашем примере. Спасибо - person Olivier.S; 15.02.2018