In the first article of this series, I described my plans for creating a Poloniex Trading Bot and as promised, I'm back with some updates.
I've made some progress and created a Python class that will allow for backtesting of strategies.
My aim here is just to explain what I've done and give a couple of examples of how to use the class.
The code has already been uploaded to GitHub and should be reasonably straightforward to get running on any system with Python 3 installed.
BackTest
class SMACrossoverBackTest(BackTest):
def addindicators(self, **kwargs):
self.fastma = kwargs["fastma"]
self.slowma = kwargs["slowma"]
self.data["fastma"] = self.data["close"].rolling(self.fastma).mean()
self.data["slowma"] = self.data["close"].rolling(self.slowma).mean()
def dostep(self):
if self.step > self.slowma:
prevfastma = self.data.ix[self.step - 1, "fastma"]
prevslowma = self.data.ix[self.step - 1, "slowma"]
price = self.data.ix[self.step, "close"]
fastma = self.data.ix[self.step, "fastma"]
slowma = self.data.ix[self.step, "slowma"]
if fastma > slowma and prevfastma < prevslowma:
self.buy(price)
elif fastma < slowma and prevfastma > prevslowma:
self.sell(price)
The BackTest class should be used by creating your own instance of the class and overriding the addindicators and dostep methods.
The __init__ method of the class requires a pandas dataframe with your chart data and accepts several keyword values. Default values are set so these keywords are optional.
def __init__(self, data, tradepct=10, btcbalance=0.01, coinbalance=0.0,
buyfee=0.25, sellfee=0.15, candlewidth=5, **kwargs):
- tradepct: percentage of btcbalance on each trade
- btcbalance: starting BTC balance for backtest
- coinbalance: starting balance for the cryptocurrency we're testing
- buyfee: Poloniex fee for a buy trade in percent
- sellfee: Poloniex fee for sell trade in percent
- candlewidth: optionally resample chartdata, value in minutes, ie to use 1 hour charts use 60
Any additional keyword arguments are then passed to the addindicators method. In the above example we might pass
fastma=6, slowma=29
In the addindicators method we store our values then create a new column in our dataframe. In this example we're taking a rolling mean of the "close" data with the length of the rolling windows being the values we passed.
(Open, High, Low, Close, Volume, Quote Volume and Weighted Average are already in the dataframe.)
In the dostep method we define what we want to do on each step of the backtest.
The first thing we check for is that enough time steps have passed so that we actually have a values for the both moving averages.
Next, we read our MA values from the previous step and this step, along with the current price.
If the fast MA is above the slow MA and it wasn't on the step prior, then it's just crossed over so we call the buy method.
Similarly, if the fast MA is below the slow MA and it wasn't before, sell.
Testing Testing
Here's how to run a backtest on "XMR" with a fast moving average of 5 and a slow moving average of 25.
polo = Poloniex()
portfolio = Portfolio(["XMR"])
test = SMACrossoverBackTest(portfolio.chartdata["XMR"], fastma=5, slowma=25)
initialvalue, finalvalue, profit = test.runtest()
print("Start Value: {0:.8f}BTC, Final Value: {1:.8f}BTC, Profit {2:.2f}%".\
format(initialvalue, finalvalue, profit))
This runs for a few seconds then prints out
Downloading: XMR
Start Value: 0.01000000BTC, Final Value: 0.00852960BTC, Profit -14.70%
Not great, let's try some higher values for the moving averages, say 25 and 100.
Loading: XMR OK.
Start Value: 0.01000000BTC, Final Value: 0.00956564BTC, Profit -4.34%
A bit better. Since we now have the ability, we can try all sorts of different values for the moving averages or try Exponential MA instead of simple (EMA Crossover code is included in last update). What about adding another moving average and looking for multiple crossovers?
The options are pretty much unlimited as to what can be added to the strategy. A lot of the indicators might be quite complicated to code however, so to make things a bit easier, I'll take @veleje's advice and add TA-Lib to the mix. (soon)
What's Next?
I think it's about time we got some visualisation added to the code. It'll be much easier to see how each strategy is acting on a pretty graph. Likely going down the TKinter + Matplotlib route for the GUI.
Once I've got a GUI running, I'll add TA-Lib and it will be well on it's way to being an actual application rather than just a bunch of code. hehehe.
As always, don't be shy with any suggestions, advice, criticism or questions, and please feel free to download the code and try it out yourself.
Have another day. :-)