library( QuantTools )
# load ticks data set
data( ticks )
ticks
## time price volume id
## 1: 2016-01-19 09:30:00 98.390 100 1
## 2: 2016-01-19 09:30:01 98.350 100 2
## 3: 2016-01-19 09:30:01 98.370 100 3
## 4: 2016-01-19 09:30:01 98.360 100 4
## 5: 2016-01-19 09:30:01 98.370 114 5
## ---
## 2310006: 2016-09-14 10:42:08 111.160 100 2310006
## 2310007: 2016-09-14 10:42:10 111.150 100 2310007
## 2310008: 2016-09-14 10:42:10 111.150 200 2310008
## 2310009: 2016-09-14 10:42:11 111.130 200 2310009
## 2310010: 2016-09-14 10:42:11 111.135 100 2310010
Note: Wide tables or code blocks can be read with ease by holding
Shift
and scrolling a mouse wheel.
Put the following bbands.cpp
file into working directory.
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::depends(QuantTools)]]
#include <Rcpp.h>
#include "BackTest.h"
// [[Rcpp::export]]
Rcpp::List bbands(
Rcpp::DataFrame ticks,
Rcpp::List parameters,
Rcpp::List options,
bool fast = false
) {
int n = parameters["n" ];
double k = parameters["k" ];
int timeFrame = parameters["timeframe" ];
// define strategy states
enum class ProcessingState{ LONG, FLAT, SHORT };
ProcessingState state = ProcessingState::FLAT;
int idTrade = 1;
// initialize indicators
BBands bbands( n, k );
// initialize Processor
Processor bt( timeFrame );
// set options
bt.SetOptions( options );
// if trading hours not set then isTradingHours set true
bool isTradingHours = not bt.IsTradingHoursSet();
// define market open/close events
bt.onMarketOpen = [&]() {
// allow trading
isTradingHours = true;
};
bt.onMarketClose = [&]() {
// forbid trading and close open positions
isTradingHours = false;
if( state == ProcessingState::SHORT ) {
bt.SendOrder(
new Order( OrderSide::BUY, OrderType::MARKET, NA_REAL, "close short (EOD)", idTrade++ )
);
}
if( state == ProcessingState::LONG ) {
bt.SendOrder(
new Order( OrderSide::SELL, OrderType::MARKET, NA_REAL, "close long (EOD)", idTrade++ )
);
}
state = ProcessingState::FLAT;
};
// define what to do when new candle is formed
bt.onCandle = [&]( Candle candle ) {
// add values to indicators
bbands.Add( candle.close );
};
// define what to do when new tick arrived
bt.onTick = [&]( Tick tick ) {
// if bbands not formed yet do nothing
if( not bbands.IsFormed() ) return;
if( not bt.CanTrade() ) return;
if( not isTradingHours ) return;
// if strategy has no position
if( state == ProcessingState::FLAT) {
// if price below lower band then buy
if( tick.price < bbands.GetValue().lower ) {
bt.SendOrder(
new Order( OrderSide::BUY, OrderType::MARKET, NA_REAL, "long", idTrade )
);
state = ProcessingState::LONG;
}
// if price above apper band then sell
if( tick.price > bbands.GetValue().upper ) {
bt.SendOrder(
new Order( OrderSide::SELL, OrderType::MARKET, NA_REAL, "short", idTrade )
);
state = ProcessingState::SHORT;
}
}
// if strategy is long and price goes above sma then close long
if( state == ProcessingState::LONG and tick.price > bbands.GetValue().sma ) {
bt.SendOrder(
new Order( OrderSide::SELL, OrderType::MARKET, NA_REAL, "close long", idTrade++ )
);
state = ProcessingState::FLAT;
}
// if strategy is below and price goes below sma then close long
if( state == ProcessingState::SHORT and tick.price < bbands.GetValue().sma ) {
bt.SendOrder(
new Order( OrderSide::BUY, OrderType::MARKET, NA_REAL, "close short", idTrade++ )
);
state = ProcessingState::FLAT;
}
};
// run back test on tick data
bt.Feed( ticks );
// test summary
Rcpp::List summary = bt.GetSummary();
// if fast return only summary
if( fast ) return summary;
// combine candles and indicators history
Rcpp::List indicators = ListBuilder().AsDataTable()
.Add( bt.GetCandles() )
.Add( bbands.GetHistory() )
.Add( "pnl" , bt.GetOnCandleMarketValueHistory() )
.Add( "drawdown", bt.GetOnCandleDrawDownHistory() );
// return back test summary, trades, orders and candles/indicators
return ListBuilder()
.Add( "summary" , summary )
.Add( "trades" , bt.GetTrades() )
.Add( "orders" , bt.GetOrders() )
.Add( "indicators" , indicators )
.Add( "daily_performance", bt.GetOnDayClosePerformanceHistory() );
}
Note: It is good practice to have
.cpp
file per strategy. It is perfect to have a package of your strategies so you donโt need to recompile them every time you restart an R session.
Compile it
# file above can be found in examples directory
strategy_cpp_file = system.file( package = 'QuantTools', 'examples/bbands.cpp' )
Rcpp::sourceCpp( strategy_cpp_file )
# set strategy parameters
parameters = data.table(
n = 100,
k = 0.5,
timeframe = 60
)
# set options, see ?Processor 'Options' section
options = list(
cost = list( tradeAbs = -0.01 ),
latency = 0.1 # 100 milliseconds
)
# see how fast back testing done on over 2 millin ticks
system.time( { test = bbands( ticks, parameters, options ) } )
## user system elapsed
## 0.089 0.000 0.089
# lets limit testing to one date
interval = '2016-09-08'
test = bbands( ticks[ time %bw% interval ], parameters, options )
test
## $summary
## from to days_tested days_traded n_per_day n n_long n_short n_win n_loss pct_win pct_loss avg_win avg_loss avg_pnl win loss pnl max_dd max_dd_start max_dd_end max_dd_length sharpe sortino r_squared avg_dd
## 1: 2016-09-08 09:30:00 2016-09-08 15:59:59 1 1 17 17 10 7 16 1 94.12 5.88 6.92 -24.09 5.09 1.11 -0.24 0.87 -0.6 2016-09-08 12:24:35 2016-09-08 14:13:25 0 NaN Inf NaN -0.06
##
## $trades
## id_trade id_sent id_enter id_exit time_sent time_enter time_exit side price_enter price_exit pnl mtm mtm_min mtm_max cost pnl_rel mtm_rel mtm_min_rel mtm_max_rel cost_rel state
## 1: 1 9608 9611 9958 2016-09-08 11:10:00 2016-09-08 11:10:00 2016-09-08 11:15:28 long 105.82 106.060 0.220 0.240 0.000 0.240 -0.02 20.7900208 22.680023 0.0000000 22.680023 -1.8900019 closed
## 2: 2 10340 10356 10994 2016-09-08 11:24:01 2016-09-08 11:24:05 2016-09-08 11:41:37 long 105.91 105.940 0.010 0.030 -0.130 0.030 -0.02 0.9441979 2.832594 -12.2745728 2.832594 -1.8883958 closed
## 3: 3 11069 11072 11156 2016-09-08 11:44:53 2016-09-08 11:45:00 2016-09-08 11:47:51 long 105.86 105.940 0.060 0.090 0.000 0.090 -0.02 5.6678632 8.501795 0.0000000 8.501795 -1.8892877 closed
## 4: 4 11172 11174 11834 2016-09-08 11:48:59 2016-09-08 11:49:00 2016-09-08 12:08:54 short 106.01 105.890 0.100 0.120 -0.115 0.120 -0.02 9.4330724 11.319687 -10.8480332 11.319687 -1.8866145 closed
## 5: 5 12021 12024 12128 2016-09-08 12:14:29 2016-09-08 12:14:32 2016-09-08 12:18:34 long 105.86 105.910 0.030 0.050 -0.020 0.050 -0.02 2.8339316 4.723219 -1.8892877 4.723219 -1.8892877 closed
## 6: 6 12165 12172 15898 2016-09-08 12:20:12 2016-09-08 12:20:15 2016-09-08 14:00:36 long 105.85 105.615 -0.255 -0.235 -0.600 0.030 -0.02 -24.0906944 -22.201228 -56.6839868 2.834199 -1.8894662 closed
## 7: 7 15905 15914 16158 2016-09-08 14:00:37 2016-09-08 14:00:37 2016-09-08 14:01:19 short 105.71 105.600 0.090 0.110 -0.200 0.120 -0.02 8.5138587 10.405827 -18.9196859 11.351812 -1.8919686 closed
## 8: 8 16177 16179 16254 2016-09-08 14:01:35 2016-09-08 14:01:35 2016-09-08 14:02:22 long 105.53 105.610 0.060 0.080 -0.080 0.080 -0.02 5.6855870 7.580783 -7.5807827 7.580783 -1.8951957 closed
## 9: 9 16280 16290 16789 2016-09-08 14:02:28 2016-09-08 14:02:29 2016-09-08 14:09:56 short 105.68 105.590 0.070 0.090 -0.210 0.100 -0.02 6.6237699 8.516276 -19.8713096 9.462528 -1.8925057 closed
## 10: 10 16928 16935 16953 2016-09-08 14:12:47 2016-09-08 14:12:50 2016-09-08 14:13:34 short 105.66 105.570 0.070 0.090 0.000 0.090 -0.02 6.6250237 8.517888 0.0000000 8.517888 -1.8928639 closed
## 11: 11 16976 17007 17259 2016-09-08 14:14:29 2016-09-08 14:14:30 2016-09-08 14:20:15 long 105.49 105.590 0.080 0.090 -0.040 0.110 -0.02 7.5836572 8.531614 -3.7918286 10.427529 -1.8959143 closed
## 12: 12 17300 17315 17921 2016-09-08 14:21:47 2016-09-08 14:21:48 2016-09-08 14:35:09 short 105.70 105.590 0.090 0.100 -0.120 0.120 -0.02 8.5146641 9.460738 -11.3528855 11.352886 -1.8921476 closed
## 13: 13 18044 18049 18104 2016-09-08 14:39:22 2016-09-08 14:39:26 2016-09-08 14:40:25 long 105.52 105.570 0.030 0.040 -0.010 0.060 -0.02 2.8430629 3.790751 -0.9476876 5.686126 -1.8953753 closed
## 14: 14 18226 18233 18830 2016-09-08 14:43:00 2016-09-08 14:43:01 2016-09-08 14:58:05 short 105.66 105.570 0.070 0.070 -0.110 0.095 -0.02 6.6250237 6.625024 -10.4107515 8.991104 -1.8928639 closed
## 15: 15 18869 18874 19199 2016-09-08 14:59:01 2016-09-08 14:59:02 2016-09-08 15:06:45 short 105.66 105.560 0.080 0.100 -0.110 0.100 -0.02 7.5714556 9.464320 -10.4107515 9.464320 -1.8928639 closed
## 16: 16 19355 19364 19431 2016-09-08 15:12:54 2016-09-08 15:12:54 2016-09-08 15:14:41 long 105.51 105.560 0.030 0.050 0.000 0.070 -0.02 2.8433324 4.738887 0.0000000 6.634442 -1.8955549 closed
## 17: 17 19617 19630 19992 2016-09-08 15:21:25 2016-09-08 15:21:25 2016-09-08 15:31:13 long 105.50 105.600 0.080 0.100 -0.050 0.100 -0.02 7.5829384 9.478673 -4.7393365 9.478673 -1.8957346 closed
## 18: 18 20125 20128 NA 2016-09-08 15:32:56 2016-09-08 15:32:56 <NA> long 105.54 NA NA -0.030 -0.210 0.030 -0.01 NA -2.842524 -19.8976691 2.842524 -0.9475081 opened
##
## $orders
## id_trade id_sent id_processed time_sent time_processed price_init price_exec side type state comment
## 1: 1 9608 9611 2016-09-08 11:10:00 2016-09-08 11:10:00 NA 105.820 buy market executed long
## 2: 1 9956 9958 2016-09-08 11:15:26 2016-09-08 11:15:28 NA 106.060 sell market executed close long
## 3: 2 10340 10356 2016-09-08 11:24:01 2016-09-08 11:24:05 NA 105.910 buy market executed long
## 4: 2 10983 10994 2016-09-08 11:41:35 2016-09-08 11:41:37 NA 105.940 sell market executed close long
## 5: 3 11069 11072 2016-09-08 11:44:53 2016-09-08 11:45:00 NA 105.860 buy market executed long
## 6: 3 11138 11156 2016-09-08 11:47:28 2016-09-08 11:47:51 NA 105.940 sell market executed close long
## 7: 4 11172 11174 2016-09-08 11:48:59 2016-09-08 11:49:00 NA 106.010 sell market executed short
## 8: 4 11829 11834 2016-09-08 12:08:46 2016-09-08 12:08:54 NA 105.890 buy market executed close short
## 9: 5 12021 12024 2016-09-08 12:14:29 2016-09-08 12:14:32 NA 105.860 buy market executed long
## 10: 5 12125 12128 2016-09-08 12:18:27 2016-09-08 12:18:34 NA 105.910 sell market executed close long
## 11: 6 12165 12172 2016-09-08 12:20:12 2016-09-08 12:20:15 NA 105.850 buy market executed long
## 12: 6 15895 15898 2016-09-08 14:00:36 2016-09-08 14:00:36 NA 105.615 sell market executed close long
## 13: 7 15905 15914 2016-09-08 14:00:37 2016-09-08 14:00:37 NA 105.710 sell market executed short
## 14: 7 16154 16158 2016-09-08 14:01:19 2016-09-08 14:01:19 NA 105.600 buy market executed close short
## 15: 8 16177 16179 2016-09-08 14:01:35 2016-09-08 14:01:35 NA 105.530 buy market executed long
## 16: 8 16250 16254 2016-09-08 14:02:21 2016-09-08 14:02:22 NA 105.610 sell market executed close long
## 17: 9 16280 16290 2016-09-08 14:02:28 2016-09-08 14:02:29 NA 105.680 sell market executed short
## 18: 9 16787 16789 2016-09-08 14:09:54 2016-09-08 14:09:56 NA 105.590 buy market executed close short
## 19: 10 16928 16935 2016-09-08 14:12:47 2016-09-08 14:12:50 NA 105.660 sell market executed short
## 20: 10 16948 16953 2016-09-08 14:13:25 2016-09-08 14:13:34 NA 105.570 buy market executed close short
## 21: 11 16976 17007 2016-09-08 14:14:29 2016-09-08 14:14:30 NA 105.490 buy market executed long
## 22: 11 17254 17259 2016-09-08 14:20:14 2016-09-08 14:20:15 NA 105.590 sell market executed close long
## 23: 12 17300 17315 2016-09-08 14:21:47 2016-09-08 14:21:48 NA 105.700 sell market executed short
## 24: 12 17904 17921 2016-09-08 14:35:04 2016-09-08 14:35:09 NA 105.590 buy market executed close short
## 25: 13 18044 18049 2016-09-08 14:39:22 2016-09-08 14:39:26 NA 105.520 buy market executed long
## 26: 13 18098 18104 2016-09-08 14:40:21 2016-09-08 14:40:25 NA 105.570 sell market executed close long
## 27: 14 18226 18233 2016-09-08 14:43:00 2016-09-08 14:43:01 NA 105.660 sell market executed short
## 28: 14 18809 18830 2016-09-08 14:58:01 2016-09-08 14:58:05 NA 105.570 buy market executed close short
## 29: 15 18869 18874 2016-09-08 14:59:01 2016-09-08 14:59:02 NA 105.660 sell market executed short
## 30: 15 19185 19199 2016-09-08 15:06:42 2016-09-08 15:06:45 NA 105.560 buy market executed close short
## 31: 16 19355 19364 2016-09-08 15:12:54 2016-09-08 15:12:54 NA 105.510 buy market executed long
## 32: 16 19429 19431 2016-09-08 15:14:31 2016-09-08 15:14:41 NA 105.560 sell market executed close long
## 33: 17 19617 19630 2016-09-08 15:21:25 2016-09-08 15:21:25 NA 105.500 buy market executed long
## 34: 17 19981 19992 2016-09-08 15:31:13 2016-09-08 15:31:13 NA 105.600 sell market executed close long
## 35: 18 20125 20128 2016-09-08 15:32:56 2016-09-08 15:32:56 NA 105.540 buy market executed long
## id_trade id_sent id_processed time_sent time_processed price_init price_exec side type state comment
##
## $indicators
## time open high low close volume id lower upper sma pnl drawdown
## 1: 2016-09-08 09:31:00 107.110 107.26 107.030 107.160 48933 212 NA NA NA 0.000000000 0.000000000
## 2: 2016-09-08 09:32:00 107.160 107.18 106.640 106.800 179364 478 NA NA NA 0.000000000 0.000000000
## 3: 2016-09-08 09:33:00 106.800 106.96 106.760 106.910 51495 799 NA NA NA 0.000000000 0.000000000
## 4: 2016-09-08 09:34:00 106.900 106.98 106.830 106.980 37592 942 NA NA NA 0.000000000 0.000000000
## 5: 2016-09-08 09:35:00 106.950 107.06 106.900 106.900 48085 1120 NA NA NA 0.000000000 0.000000000
## ---
## 384: 2016-09-08 15:55:00 105.455 105.47 105.370 105.390 25467 21405 105.5256 105.6266 105.5761 0.007237814 -0.001705514
## 385: 2016-09-08 15:56:00 105.390 105.39 105.340 105.345 35934 21530 105.5226 105.6261 105.5743 0.006811436 -0.002131893
## 386: 2016-09-08 15:57:00 105.345 105.41 105.335 105.410 32188 21633 105.5212 105.6257 105.5734 0.007427316 -0.001516013
## 387: 2016-09-08 15:58:00 105.445 105.47 105.420 105.420 16367 21718 105.5201 105.6254 105.5727 0.007522067 -0.001421262
## 388: 2016-09-08 15:59:00 105.430 105.49 105.430 105.460 42548 21895 105.5199 105.6254 105.5726 0.007901070 -0.001042259
##
## $daily_performance
## date return pnl drawdown avg_pnl n_per_day n_per_day_long close
## 1: 2016-09-08 0.008374824 0.008374824 -0.0005685048 0.0005093574 17 10 105.51
Here is code to reproduce this plot:
# plot result
layout( matrix( 1:2, ncol = 1 ), height = c( 2, 1 ) )
# 1
par( mar = c( 0, 4, 2, 4 ), family = 'sans', xaxt = 'n' )
# indicators and orders
plot_dts(
test$indicators[ time %bw% interval ],
test$orders[ side == 'buy' , .( time_processed, buy = price_exec ) ],
test$orders[ side == 'sell', .( time_processed, sell = price_exec ) ]
)$
candles( label = 'price' )$
lines( c( 'upper', 'sma', 'lower' ), col = c( 'darkgoldenrod', 'chocolate', 'darkgoldenrod' ) )$
lines( c( 'buy', 'sell' ), col = c( 'darkolivegreen', 'darkred' ), type = 'p', pch = c( 24, 25 ), lty = 0 )$
style( time = list( visible = F ) )
# 2
par( xaxt = 's', mar = c( 4, 4, 0, 4 ) )
# performance
plot_dts(
test$indicators[ time %bw% interval, .( time, `P&L, %` = pnl * 100, `Draw Down, %` = drawdown * 100 ) ]
)$lines( col = c( 'darkolivegreen', 'darkred' ) )$
style( legend = list( position = 'bottomleft' ) )
Here is code to reproduce this plot:
library( plotly ) # install.packages( 'plotly' )
p = plot_ly( symbols = c( 'triangle-up', 'triangle-down' ),
colors = c( 'darkseagreen', 'firebrick' ) ) %>% #
add_data( data = test$indicators ) %>%
# candle low-high
add_segments( x = ~time - parameters$timeframe / 2, y = ~low,
xend = ~time - parameters$timeframe / 2, yend = ~high,
showlegend = F, name = 'candle', line = list( color = 'cornflowerblue' )
) %>%
# candle open-close
add_segments( x = ~time - parameters$timeframe, y = ~open,
xend = ~time, yend = ~close,
showlegend = F, name = 'candle', line = list( shape = 'hvh', color = 'cornflowerblue' )
) %>%
# add indicators
add_lines( x = ~time, y = ~lower, name = 'lower', line = list( color = 'goldenrod' ) ) %>%
add_lines( x = ~time, y = ~sma, name = 'sma', line = list( color = 'darkgreen' ) ) %>%
add_lines( x = ~time, y = ~upper, name = 'upper', line = list( color = 'darkmagenta' ) ) %>%
# and orders
add_data( data = test$orders ) %>%
add_markers( x = ~time_processed, y = ~price_exec, color = ~side, text = ~comment,
marker = list( size = 10 ), symbol = ~side
) %>%
layout(
xaxis = list( title = '', rangeselector = list( visible = FALSE ) ),
yaxis = list( title = '' )
) %>%
config( scrollZoom = T, autosizable = T, queueLength = 1 )
p
© 2016 Stanislav Kovalevsky. All rights reserved.