BitCoin Trading Strategies BackTest With PyAlgoTrade
Written by Khang Nguyen Vo, khangvo88@gmail.com, for the RobustTechHouse blog. Khang is a graduate from the Masters of Quantitative and Computational Finance Program, John Von Neumann Institute 2014. He is passionate about research in machine learning, predictive modeling and backtesting of trading strategies.
INTRODUCTION
Bitcoin (or BTC) was invented by Japanese Satoshi Nakamoto and considered the first decentralized digital currency or crypto-currency. In this article, we experiment with a simple momentum based trading strategy for Bitcoin using PyAlgoTrade which is a Python Backtesting library. The Moving Average Crossover trading strategy we start with is defined as:
- Enter position:
- Long when MA10 > MA20
- Short when MA10 < MA20
- Exit position:
- reverse trend
- Take profit when we gain $20
- Cut loss when we lose $10
MA10 refers to 10 day moving average price and MA20 refers 20 day moving average price.
DATA
The bitcoin data can be obtained from Bitcoin charts. The raw data of this source is at minute based sampling frequency and we group the data to 15-minutes prices as follows:
BITCOIN TRADING STRATEGY BACKTEST WITH PYALGOTRADE
PyAlgoTrade, as mentioned in previous blog, is an event-driven library. So we must override the basic events onEnterOk and onExitOk, which are raised when orders submitted before are successfully filled.
import Momentum.MyBaseStrategy as bstr #extend from pyalgotrade.BacktestStrategy from pyalgotrade.technical import ma from pyalgotrade.barfeed import csvfeed from pyalgotrade.bar import Frequency from pyalgotrade import plotter import numpy as np import datetime class MyStrategy(bstr.MyBTStrategy): def __init__(self, feed, cash,sma): self.__instrument = 'Bitcoin' bstr.MyBTStrategy.__init__(self,feed,cash, instrument=self.__instrument) self.MAXPROFIT = 20; self.STOPLOSS = 10 self.getBroker().setAllowNegativeCash(True) # using for trading signal self.__position = None self.__price = feed[self.__instrument].getCloseDataSeries() self.__sma10 = ma.SMA(self.__price,sma[0],maxLen=100000) self.__sma20 = ma.SMA(self.__price,sma[1],maxLen=100000) self.__lastPrice = 0 #last price. Use for take profit and cutloss self.__signal = 0 #1: buying, -1: selling, 0: no change self.__last_exit_time = None def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("%s %d at VND %s" %(self.alert_message,execInfo.getQuantity(), ut.accountingFormat(execInfo.getPrice()))) self.__lastPrice = execInfo.getPrice() self.record_detail_transaction(position) def onEnterCanceled(self, position): self.__position = None def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("%s %d at %s\n=================================" %(self.alert_message, execInfo.getQuantity(),'{:11,.2f}'.format(execInfo.getPrice()))) self.__position = None self.record_detail_transaction(position, False) # log detail for later analysis # run before onEnterOk and onExitOk def onOrderUpdated(self,order): pass
The main process of trading algorithm is in onBars, which is raised every time there is new record of time series. PyAlgoTrade feed the data series and put it in bars, on each time given. This mandatory method is implemented as follows:
. . . # main event to update trading strategy def onBars(self, bars): self.portfolio_values.append(self.getBroker().getEquity()) if self.__sma20[-1] is None: return bar = bars[self.__instrument] if self.__sma10[-1] > self.__sma20[-1]: self.__signal = 1 # buying signal elif self.__sma10[-1] < self.__sma20[-1]: self.__signal =-1 # selling signal shares = 1 if(self.__position) is None and self.__sma20[-2] is not None: # go into long position if self.__sma10[-1] > self.__sma20[-1] and self.__sma10[-2] <= self.__sma20[-2]: self.info("short SMA > long SMA. RAISE BUY SIGNAL") #shares = int(self.getBroker().getCash() * 0.9 / bar.getClose()) self.__position = self.enterLong(self.__instrument,shares,False) self.alert_message='Long position' self.buy_signals.append(self.getCurrentDateTime()) #short position elif self.__sma10[-1] < self.__sma20[-1] and self.__sma10[-2] >= self.__sma20[-2]: self.info("short SMA < long SMA. RAISE SELL SIGNAL") self.__position = self.enterShort(self.__instrument,shares,False) self.alert_message='Short position' self.sell_signals.append(self.getCurrentDateTime()) elif self.__lastPrice is not None and self.getBroker().getPositions() != {}: pos = self.getBroker().getPositions()[self.__instrument] # take profit when we obtain >= $20 if( np.sign(pos)*(bar.getClose() - self.__lastPrice) >= self.MAXPROFIT): self.alert_message = 'TAKE PROFIT' self.__position.exitMarket() self.__lastPrice = None # cut loss when we lose more than $10 elif (np.sign(pos)*(self.__lastPrice - bar.getClose())) >= self.STOPLOSS: self.alert_message = 'STOP LOSS' self.__position.exitMarket() self.__lastPrice = None elif pos*self.__signal < 0: self.alert_message = "Reverse signal. TAKE PROFIT" self.__position.exitMarket() self.__lastPrice = None if self.__signal < 0: self.sell_signals.append(self.getCurrentDateTime()) else: self.buy_signals.append(self.getCurrentDateTime()) self.__last_exit_time = self.getCurrentDateTime()
Then the main script as follows:
filename = '../btcUSD15m_2.csv' # TODO: change the date range firstDate = datetime.datetime(2014,1,1,0,0,0,0,pytz.utc) endDate = datetime.datetime(2014,3,31,0,0,0,0,pytz.utc) feed = csvfeed.GenericBarFeed(15*Frequency.MINUTE,pytz.utc,maxLen=100000) feed.setBarFilter(csvfeed.DateRangeFilter(firstDate,endDate)) feed.addBarsFromCSV('Bitcoin', filename) cash = 10 ** 3 # 1,000 USD myStrategy = MyStrategy(feed,cash,[12,30]) #short and long moving average plt = plotter.StrategyPlotter(myStrategy, True, False, True) myStrategy.run() myStrategy.printTradingPerformance()
The trading transaction detail of this strategy from Jan 2014 to Mar 2014 are as follows:
In this short time window, the Sharpe Ratio is indeed poor and only -1.9. Moreover, there are a total of 200 trades executed in 3 months, and most are unprofitable trades (132/200 trades = 66%). Therefore, we need to reduce the number of unprofitable trades.
TWEAKING BITCOIN TRADING STRATEGY BACKTEST
The problem might be that we are using a very short-length moving average window to calculate the change of trends, so the strategy is very sensitive to changes. Now we try a longer moving average window with MA(80,200) crossover
myStrategy = MyStrategy(feed,cash,[80,200]) #short and long moving average plt = plotter.StrategyPlotter(myStrategy, True, False, True) myStrategy.run()
The result of this trading strategy as follows for the same period.
The summary result when running this strategy between 2013-2015
CHARTS IN 2013
CHARTS IN 2014
CHARTS IN 2015
We see that the trading performance is better now. The Sharpe ratio is larger than 0.5, and in 2014, the cumulative returns is as big as 33%. The length of Moving Average could be further optimized (data-mined!).
NOTE ON TRANSACTION COSTS
In real trading, it is mandatory to add commission rates or transaction costs. Usually, the transaction cost can be computed as the difference between ASK price and BID price (BID-ASK SPREAD) if market orders are used to buy or sell. In our data set, the average “bid ask spread” is about 0.11, so we set the cost of each transaction to BTC 0.11.
from pyalgotrade.broker import backtesting feed = createFeed(firstDate, endDate) strat3 = MyStrategy(feed,cash,[80,200]) #short and long moving average strat3.getBroker().setCommission(backtesting.FixedPerTrade(0.11)) #t-cost per trade = $0.11 strat3.run() strat3.printTradingPerformance()
Overall, the strategy is still profitable, though we have to be mindful that because BitCoin history is very short, so the statistical significance of the strategy is inconclusive. Note that we assume there are no broker transaction fees. In reality, usually this fee cost 0.7$ per trade.
BitCoin Trading Strategies BackTest With PyAlgoTrade的更多相关文章
- Basics of Algorithmic Trading: Concepts and Examples
https://www.investopedia.com/articles/active-trading/101014/basics-algorithmic-trading-concepts-and- ...
- Algorithmic Trading[z]
Algorithmic Trading has been a hot topic for equity/derivative trading over a decade. Many ibanks an ...
- Python金融行业必备工具
有些国外的平台.社区.博客如果连接无法打开,那说明可能需要"科学"上网 量化交易平台 国内在线量化平台: BigQuant - 你的人工智能量化平台 - 可以无门槛地使用机器学习. ...
- [转]Introduction to Learning to Trade with Reinforcement Learning
Introduction to Learning to Trade with Reinforcement Learning http://www.wildml.com/2018/02/introduc ...
- Introduction to Learning to Trade with Reinforcement Learning
http://www.wildml.com/2015/12/implementing-a-cnn-for-text-classification-in-tensorflow/ The academic ...
- OnePy--构建属于自己的量化回测框架
本文主要记录我构建量化回测系统的学习历程. 被遗弃的项目:Chandlercjy/OnePy_Old 新更新中的项目:Chandlercjy/OnePy 目录 1. 那究竟应该学习哪种编程语言比较好呢 ...
- Should You Build Your Own Backtester?
By Michael Halls-Moore on August 2nd, 2016 This post relates to a talk I gave in April at QuantCon 2 ...
- An Introduction to Stock Market Data Analysis with R (Part 1)
Around September of 2016 I wrote two articles on using Python for accessing, visualizing, and evalua ...
- 数据集划分——train set, validate set and test set
先扯点闲篇儿,直取干货者,可以点击这里. 我曾误打误撞的搞过一年多的量化交易,期间尝试过做价格和涨跌的预测,当时全凭一腔热血,拿到行情数据就迫不及待地开始测试各种算法. 最基本的算法是技术指标类型的, ...
随机推荐
- 2018.7.13vue知识小结
//配置是否允许vue-devtools检查代码,方便调试,生产环境中需要设置为false Vue.config.devtools=false; Vue.config.productionTip=fa ...
- FreeRtos——单任务
原创(当然借鉴了官网资料^_^): 在之前的移植工作准备好之后,我们需要调用freertos提供给我们的API函数实现操作系统地运行.首先,第一个函数: 任务函数任务是由 C 语言函数实现的.唯一特别 ...
- 对java中arraylist深入理解
1.ArrayList插入删除一定慢么? 取决于你删除的元素离数组末端有多远,ArrayList拿来作为堆栈来用还是挺合适的,push和pop操作完全不涉及数据移动操作. 2.ArrayList的遍历 ...
- PHP uxf framework 在模版中加入url标签
1. 确保不修改discuz代码: 2. 继承discuz template类,重载parse_template 方法:由于discuz在模版引擎这一块没有考虑扩展性,对标签的解析全部写在一个方法中, ...
- div允许用户输入
主要是用到contenteditable属性,就可以用div让用户输入了 <div id="guo" style="width:500px; height:200p ...
- jrtplib编译指南
The library offers support for the Real-time Transport Protocol (RTP), The library uses the JThread ...
- web.xml文件中<mime-mapping>
http://blog.csdn.net/sccemstanford/article/details/9064155 ————————————————————————————————————————— ...
- 你有自己的Web缓存知识体系吗?
赵舜东 江湖人称赵班长,曾在武警某部负责指挥自动化的架构和运维工作,2008年退役后一直从事互联网运维工作.曾带团队负责国内某电商的运维工作,<saltstack入门与实践>作者,某学院高 ...
- easyui_extension.js
$.extend($.fn.datagrid.methods,{ /** * 开打提示功能 * * @param {} * jq * @param {} * params 提示消息框的样式 * @re ...
- TensorFlow基础笔记(7) 图像风格化效果与性能优化进展
参考 http://hacker.duanshishi.com/?p=1693http://blog.csdn.net/hungryof/article/details/53981959http:// ...