by AlexeyA Май 3, 2018

Тестирование алгоритмических торговых стратегий в "R"

Кто не знает, среда R предлагает широкие возможности для работы с большими объемами данных, техническими индикаторами, и хорошо подходит для тестирования автоматических стратегий с тем, чтобы в дальнейшем решить, стоит ли программировать торгового робота на данной стратегии.

Ниже я предлагаю ознакомиться с этими возможностями на примере простой свечной стратегии:

Сигнал на покупку1: Если три предыдущих свечи были красные и текущее закрытие ниже открытия последней зеленой свечи

Сигнал на покупку2: Если текущая свеча красная и ее тело в три раза больше максимального тела из трех предыдущих свечей

Оба сигнала фильтруются по SMA. Покупка идет, только если цена ниже SMA от последних десяти свечей.

Сигналы на продажу - аналогичные с точностью до наоборот, но использоваться не будут, т.к. стратегия тестируется для биржи binance, где шортов нет.

Сигнал на продажу: Если свеча зеленая и тело больше среднего тела последних десяти свечей хотя бы в половину и (не было шортов и цена выше цены открытия позиции)

Исходные данные буду брать из гугл-таблиц, куда предварительно помещу их из сервиса bitcoincharts.com

Для начала нам нужно установить R. Берем его с www.r-project.org

Далее устанавливаем в R две библиотеки: для работы с листами гугл и библиотеку технического анализа

install.packages('googlesheets')
library('googlesheets')
install.packages("TTR")
library("TTR")

К слову, можно получить набор свечей любым другим способом: через файл CSV, через локальную базу SQL. Соответствующие библиотеки в R имеются.

gs_read("https://docs.google.com/spreadsheets/d/1zlNPnPCI_KFTr5wNX6XRjsLicnWgTSgwuoQxOKAM7kw/edit#gid=0")
gs_ls()
be <- gs_title("BTCUSDT 1H")
# list worksheets
gs_ws_ls(be)
# get Westminster voting
west <- gs_read(ss=be, ws = "Sheet1", skip=0)
wdf <- as.data.frame(west)

Теперь у нас есть часовые свечи за три последних месяца (или сколько у вас в файле) в наборе данных.

Используемые названия колонок:

Timestamp - время свечи

Open - цена открытия

Close - цена закрытия

Первым делом добавляем индикаторные и вспомогательные колонки

#знак свечи
wdf$bar <- as.integer(wdf$Close >= wdf$Open) - as.integer(wdf$Close <=wdf$Open)
wdf$body <- abs(wdf$Close - wdf$Open)
#цена открытия последней бычьей и медвежьей свечи
wdf$openbull <- 0
for(i in 2:nrow(wdf)){wdf$openbull[i] = ifelse (wdf$bar[i]==1 && wdf$bar[i-1]!=1, wdf$open[i], wdf$openbull[i-1]) }
wdf$openbear <- 0
for(i in 2:nrow(wdf)){wdf$openbear[i] = ifelse (wdf$bar[i]==-1 && wdf$bar[i-1]!=-1, wdf$Open[i], wdf$openbear[i-1]) }
# для вычисления цвета трех последних свечей
wdf$barsma3 <- 0
for(i in 4:nrow(wdf)){wdf$barsma3[i] = (wdf$bar[i-2] + wdf$bar[i-1] +wdf$bar[i]) / 3 }
#среднее тело свечи для отбраковки откровенно маленьких
wdf$abody <- SMA(wdf$body,10)
#SMA для фильтрации сигналов
wdf$sma10 <- SMA(wdf$Close,10)
#максимум тела трех предыдущих свечей
wdf$body3 <- 0
for(i in 4:nrow(wdf)){wdf$body3[i] <- max(c(wdf$body[i-1], wdf$body[i-2], wdf$body[i-3]))
#Добавляем сигналы и заполняем итерационно-вычисляемые колонки:

# сигналы на покупку и продажу

wdf$up1 <- (wdf$barsma3 == -1) & (wdf$Close < wdf$openbull) & wdf$body > wdf$abody / 5 & (wdf$Close < wdf$sma10)
wdf$up2 <- wdf$bar == -1 & wdf$body > wdf$body3 * 3 & wdf$body > wdf$abody / 5 & (wdf$Close < wdf$sma10)
wdf$dn1 <- wdf$barsma3 ==1 & (wdf$Close > wdf$openbear) & ( wdf$position == 0 || wdf$Close > wdf$pos_price ) & wdf$body > wdf$abody / 5
wdf$dn2 <- wdf$bar == 1 & wdf$body > wdf$body3 * 3 & wdf$body > wdf$abody / 5 & (wdf$Close > wdf$sma10)
# вычисление позиции (0 - не в позиции, 1 - позиция long)
# pos_close - сигнал на закрытие позиции
# balance - баланс. За начало берется число 1000


for(i in 1:nrow(wdf)){

wdf$position[i] <- ifelse( i==1, 0, ifelse( wdf$pos_close[i-1], 0, ifelse((wdf$up1[i-1] || wdf$up2[i-1]) & ( wdf$position[i-1]==0 || wdf$Close[i-1] < wdf$pos_price[i-1] ) , 1, ifelse(wdf$dn1[i-1] || wdf$dn2[i-1], -1, wdf$position[i-1]))))

wdf$position[i] <- ifelse( is.na(wdf$position[i]), 0, wdf$position[i])
wdf$pos_price[i] <- ifelse( i==1, 0, ifelse(wdf$up1[i-1] || wdf$up2[i-1] || wdf$dn1[i-1] || wdf$dn2[i-1], wdf$Close[i-1], wdf$pos_price[i-1]))
wdf$pos_close[i] <- (((wdf$position[i] > 0 & wdf$bar[i] == 1 ) || (wdf$position[i] < 0 & wdf$bar[i] == -1)) & wdf$body[i] > wdf$abody[i] / 2 )
wdf$balance[i] <- ifelse( wdf$position[i] > 0 , ifelse(i==1, 10000 + wdf$body[i] * wdf$bar[i], wdf$balance[i-1] + wdf$body[i] * wdf$bar[i]), wdf$balance[i-1] )
wdf$balance[i] <- ifelse( is.na(wdf$balance[i]), 10000, wdf$balance[i])
}

Итак, относительно небольшим кодом мы вычислили результаты стратегии для часового таймфрейма BTC/USD.

Результат выводим в виде графика

plot(wdf$Timestamp, wdf$Close, type="l", xlab="Date", ylab="Price")
lines(wdf$Timestamp,wdf$balance,col="green")
Изображение удалено.

В итоге мы проверили, что за 3 месяца стратегия принесла более 20 процентов.

Хорошо это или плохо, решать вам.

По всем вопросам обращайтесь: Alexey @aav_1980

Только авторизованные пользователи могут оставлять новые комментарии
Serega163

Отличная статья, спасибо! А объем можно считывать?

AlexeyA

Да, разумеется. Объем - один из пяти основных параметров свечи. Как правило, свеча определяется имеет: Timestamp - время открытия, Open, Close, High, Low, Volume. Везде где видел предоставление данных по свечам - объем там есть.