pyalgotrade simulated trading and backtesting
You can see the code of the last lesson. We calculated the most basic index of the average price of simpleMovingAverage, and finally obtained the closing price data of Ping An Bank and the average price data of SMA,
In other words, we learned how to obtain data, define data, calculate a basic technical index, and then print the data.
Today, we'll learn how to use the indicators for trading after we calculate the indicators, for example, how to back test these data after we get the buy and sell signals. We'll go back to the official website,
Let's continue with the example code from last lesson.
1, Detailed explanation of official sample code
An example is given below to calculate an additional index called rsi (relative strength index).
from pyalgotrade import strategy from pyalgotrade.barfeed import quandlfeed from pyalgotrade.technical import ma from pyalgotrade.technical import rsi def safe_round(value, digits): if value is not None: value = round(value, digits) return value class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument): super(MyStrategy, self).__init__(feed) self.__rsi = rsi.RSI(feed[instrument].getCloseDataSeries(), 14) self.__sma = ma.SMA(self.__rsi, 15) self.__instrument = instrument def onBars(self, bars): bar = bars[self.__instrument] self.info("%s %s %s" % ( bar.getClose(), safe_round(self.__rsi[-1], 2), safe_round(self.__sma[-1], 2) )) # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed's bars. myStrategy = MyStrategy(feed, "orcl") myStrategy.run()
You can understand the most basic calculation method of this index. Its calculation rule is to divide the average price of the rising days by the average price of the falling days. The larger the result, the better the rise in compliance.
If you are interested, you can verify it yourself. The use method here and the calculation method of ma in the last class have always been. The following is the result of its calculation:
Next, let's look at the trading and backtesting part, that is, the trading part. Let's take a brief look at its Translation:
Let's continue with a simple strategy, this time simulating the actual transaction. The idea is simple:
- If the adjusted closing price is higher than SMA(15), we will go long (we buy the market order).
- If the long position is in place and the adjusted closing price is lower than SMA(15), we will exit the long position (we sell the market order).
from __future__ import print_function from pyalgotrade import strategy from pyalgotrade.barfeed import quandlfeed from pyalgotrade.technical import ma class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument, smaPeriod): super(MyStrategy, self).__init__(feed, 1000) self.__position = None self.__instrument = instrument # We'll use adjusted close values instead of regular close values. self.setUseAdjustedValues(True) self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod) def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f" % (execInfo.getPrice())) def onEnterCanceled(self, position): self.__position = None def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f" % (execInfo.getPrice())) self.__position = None def onExitCanceled(self, position): # If the exit was canceled, re-submit it. self.__position.exitMarket() def onBars(self, bars): # Wait for enough bars to be available to calculate a SMA. if self.__sma[-1] is None: return bar = bars[self.__instrument] # If a position was not opened, check if we should enter a long position. if self.__position is None: if bar.getPrice() > self.__sma[-1]: # Enter a buy market order for 10 shares. The order is good till canceled. self.__position = self.enterLong(self.__instrument, 10, True) # Check if we have to exit the position. elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): self.__position.exitMarket() def run_strategy(smaPeriod): # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed. myStrategy = MyStrategy(feed, "orcl", smaPeriod) myStrategy.run() print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity()) run_strategy(15)
Here, you can also define transaction rules through MyStrategy:
def __init__(self, feed, instrument, smaPeriod): super(MyStrategy, self).__init__(feed, 1000)
His constructor has one more parameter: smaPeriod, which represents the period value of the average price in days.
Then call the constructor of the parent class and pass the feed dataset and 1000, where 1000 represents cash_or_brk, you can understand it as money invested. Its default value is one million, and 1000 is passed here.
self.__position = None self.__instrument = instrument
Current position__ Position is set to null,
# We'll use adjusted close values instead of regular close values. self.setUseAdjustedValues(True)
Then, the adjusted closing price is used to replace the original closing price.
self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod)
The value of sma is still calculated here, and Ma is called sma method, feed[instrument] represents the data set corresponding to the stock code, getPriceDataSeries represents the acquisition of price data, and the second parameter is smaPeriod, which represents the cycle value of the average price in several days.
Looking down, we find several more functions: these functions are used to control buying and selling.
- onEnterOk buy,
- Onentercancelled purchase cancelled,
- onExitOk sold,
- Onexitcancelled sales cancelled,
def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f" % (execInfo.getPrice()))
Get an execution information, execInfo. The purchase order is obtained through the position of the position, and then the execution information is obtained. Then print out the price of the purchase entrustment.
In other words, this function is used to check whether there is a buying entrustment after I generate a buying signal. If the buying is executed, the information will be printed. Similarly, for onExitOk function:
def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f" % (execInfo.getPrice())) self.__position = None
This function will find out whether there is a sale Commission, and if so, print it out. If the execution is successful__ position=None is short selling.
def onEnterCanceled(self, position): self.__position = None
When the purchase is cancelled, it will also be cancelled__ position=None.
def onExitCanceled(self, position): # If the exit was canceled, re-submit it. self.__position.exitMarket()
When the sale is cancelled, self__ position. Exitmarket() will execute the logic of the sell transaction again.
def onBars(self, bars): # Wait for enough bars to be available to calculate a SMA. if self.__sma[-1] is None: return bar = bars[self.__instrument] # If a position was not opened, check if we should enter a long position. if self.__position is None: if bar.getPrice() > self.__sma[-1]: # Enter a buy market order for 10 shares. The order is good till canceled. self.__position = self.enterLong(self.__instrument, 10, True) # Check if we have to exit the position. elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): self.__position.exitMarket()
onBars is a way to provide market data The part of conditional judgment in the trading process (calculating buy and sell signals) also appears in this function
if self.__sma[-1] is None: return
First, it will judge whether the data is sufficient. If the data is insufficient, return directly. Because if it is set to 15 days, the average price cannot be calculated in less than 15 days.
bar = bars[self.__instrument]
Initial dataset settings
if self.__position is None: if bar.getPrice() > self.__sma[-1]: # Enter a buy market order for 10 shares. The order is good till canceled. self.__position = self.enterLong(self.__instrument, 10, True) # Check if we have to exit the position. elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): self.__position.exitMarket()
1. If the current stock is not bought:
If the current price is greater than the average price, buy. self.enterLong carries out the buy operation. Its second parameter 10 represents 10 shares. Indicates the number of shares purchased. If it is A shares, buy at least 100 shares at A time. The third parameter is True, which means that the buy operation can be executed until it is cancelled.
2. If the current stock has been purchased:
If the current price is less than the average price, and__ position. If exitact is not canceling the order, the logic of selling transaction will be executed.
Therefore, if you want to define your own policy, you need to instantiate the five methods of the parent class. Then comes the call implementation part of the topic:
def run_strategy(smaPeriod): # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed. myStrategy = MyStrategy(feed, "orcl", smaPeriod) myStrategy.run() print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity())
It will get data from the previous:
# Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv")
Define policies and set parameters:
# Evaluate the strategy with the feed. myStrategy = MyStrategy(feed, "orcl", smaPeriod) myStrategy.run() # Print total position: (cash + shares * price) (cash + shares * price) #The information related to the order in the getBroker and transaction, including shareholding and cash, comes from the broker print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity())
These operations are encapsulated, and finally the main script is executed directly by calling this method and passing the cycle value of the average price of smaPeriod for several days.
2, Actual combat
We rewrite our own script based on this example.
#Data back test using pyalgotrade import pyalgotrade from pyalgotrade import strategy from pyalgotrade.barfeed import quandlfeed from pyalgotrade_tushare import tools, barfeed from pyalgotrade.technical import ma def safe_round(value, digits): if value is not None: value = round(value, digits) return value class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument, smaPeriod): super(MyStrategy, self).__init__(feed, 10000) self.__position = None self.__instrument = instrument # We'll use adjusted close values instead of regular close values. self.setUseAdjustedValues(True) self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod) def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f" % (execInfo.getPrice())) def onEnterCanceled(self, position): self.__position = None def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f" % (execInfo.getPrice())) self.__position = None def onExitCanceled(self, position): # If the exit was canceled, re-submit it. self.__position.exitMarket() def onBars(self, bars): # Wait for enough bars to be available to calculate a SMA. if self.__sma[-1] is None: return bar = bars[self.__instrument] # If a position was not opened, check if we should enter a long position. if self.__position is None: if bar.getPrice() > self.__sma[-1]: # Enter a buy market order for 10 shares. The order is good till canceled. self.__position = self.enterLong(self.__instrument, 100, True) # Check if we have to exit the position. elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): self.__position.exitMarket() def run_strategy(smaPeriod): # Load the bar feed from the CSV file instruments = ["000001"] feeds = tools.build_feed(instruments, 2016, 2018, "histdata") print(feeds) # Evaluate the strategy with the feed's bars. myStrategy = MyStrategy(feeds, instruments[0], smaPeriod) myStrategy.run() # Print total position: (cash + shares * price) (cash + shares * price) print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity()) run_strategy(15)
Here I will super (mystrategy, self)__ init__ (feed, 10000) the initial cash is set to 10000. If it is greater than this value, it means you have made money!
self.__ position = self. Enterlong (self. _instrument, 100, true) the second parameter passes 100, because A shares are sold from one hand.
Run the above code:
At this time, there is no AdjClose in our market data:
def setUseAdjustedValues(self, useAdjusted): if useAdjusted and not self.barsHaveAdjClose(): raise Exception("The barfeed doesn't support adjusted close values")
Just remove this code
Use the original default closing price.
def __init__(self, feed, instrument, smaPeriod): super(MyStrategy, self).__init__(feed, 10000) self.__position = None self.__instrument = instrument # # We'll use adjusted close values instead of regular close values. # self.setUseAdjustedValues(True) self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod)
function:
Here we have completed a simple back test to know whether to make money. Next, you can try multi cycle back testing, for example, setting different smaPeriod. The general logic here is the same, but the indicators are different.
The final result given here is the total position amount. We mainly gave the trading signal before. That is, the signal of buying and selling. So many places will be ignored here. For example, the transaction rate, the details of each transaction price will be ignored.
Of course, more details will raise the threshold for you to understand this open source project. Therefore, at the beginning of quantification, quantitative analysis can be carried out first, and then manually. This is the fastest way.
3, Multi parameter back test
Then go to the example page on the official website:
Call this loop to annotate the redundant output code: you can see that the longer the time, the lower the benefit. The average price gain in 11 days is the highest
Please indicate: Illusory private school ยป Python quantitative trading practice-36 simulated sma trading and backtesting