Strategy analyzers provide an extensible way to attach different calculations to strategy executions.
Bases: object
Base class for strategy analyzers.
Note
This is a base class and should not be used directly.
Bases: pyalgotrade.stratanalyzer.StrategyAnalyzer
A pyalgotrade.stratanalyzer.StrategyAnalyzer that calculates time-weighted returns for the whole portfolio.
Parameters: | maxLen (int.) – The maximum number of values to hold in net and cumulative returs dataseries. Once a bounded length is full, when new items are added, a corresponding number of items are discarded from the opposite end. If None then dataseries.DEFAULT_MAX_LEN is used. |
---|
Returns a pyalgotrade.dataseries.DataSeries with the cumulative returns for each bar.
Returns a pyalgotrade.dataseries.DataSeries with the returns for each bar.
Bases: pyalgotrade.stratanalyzer.StrategyAnalyzer
A pyalgotrade.stratanalyzer.StrategyAnalyzer that calculates Sharpe ratio for the whole portfolio.
Parameters: | useDailyReturns (boolean.) – True if daily returns should be used instead of the returns for each bar. |
---|
Returns the Sharpe ratio for the strategy execution. If the volatility is 0, 0 is returned.
Parameters: |
|
---|
Bases: pyalgotrade.stratanalyzer.StrategyAnalyzer
A pyalgotrade.stratanalyzer.StrategyAnalyzer that calculates max. drawdown and longest drawdown duration for the portfolio.
Returns the duration of the longest drawdown.
Return type: | datetime.timedelta. |
---|
Note
Note that this is the duration of the longest drawdown, not necessarily the deepest one.
Returns the max. (deepest) drawdown.
Bases: pyalgotrade.stratanalyzer.StrategyAnalyzer
A pyalgotrade.stratanalyzer.StrategyAnalyzer that records the profit/loss and returns of every completed trade.
Note
This analyzer operates on individual completed trades. For example, lets say you start with a $1000 cash, and then you buy 1 share of XYZ for $10 and later sell it for $20:
- The trade’s profit was $10.
- The trade’s return is 100%, even though your whole portfolio went from $1000 to $1020, a 2% return.
Returns the total number of trades.
Returns the number of profitable trades.
Returns the number of unprofitable trades.
Returns the number of trades whose net profit was 0.
Returns a numpy.array with the profits/losses for each trade.
Returns a numpy.array with the profits for each profitable trade.
Returns a numpy.array with the losses for each unprofitable trade.
Returns a numpy.array with the returns for each trade.
Returns a numpy.array with the positive returns for each trade.
Returns a numpy.array with the negative returns for each trade.
Returns a numpy.array with the commissions for each trade.
Returns a numpy.array with the commissions for each profitable trade.
Returns a numpy.array with the commissions for each unprofitable trade.
Returns a numpy.array with the commissions for each trade whose net profit was 0.
Save this code as sma_crossover.py:
from pyalgotrade import strategy
from pyalgotrade.technical import ma
from pyalgotrade.technical import cross
class SMACrossOver(strategy.BacktestingStrategy):
def __init__(self, feed, instrument, smaPeriod):
super(SMACrossOver, self).__init__(feed)
self.__instrument = instrument
self.__position = None
# We'll use adjusted close values instead of regular close values.
self.setUseAdjustedValues(True)
self.__prices = feed[instrument].getPriceDataSeries()
self.__sma = ma.SMA(self.__prices, smaPeriod)
def getSMA(self):
return self.__sma
def onEnterCanceled(self, position):
self.__position = None
def onExitOk(self, position):
self.__position = None
def onExitCanceled(self, position):
# If the exit was canceled, re-submit it.
self.__position.exitMarket()
def onBars(self, bars):
# If a position was not opened, check if we should enter a long position.
if self.__position is None:
if cross.cross_above(self.__prices, self.__sma) > 0:
shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice())
# Enter a buy market order. The order is good till canceled.
self.__position = self.enterLong(self.__instrument, shares, True)
# Check if we have to exit the position.
elif not self.__position.exitActive() and cross.cross_below(self.__prices, self.__sma) > 0:
self.__position.exitMarket()
and save this code in a different file:
from __future__ import print_function
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.stratanalyzer import returns
from pyalgotrade.stratanalyzer import sharpe
from pyalgotrade.stratanalyzer import drawdown
from pyalgotrade.stratanalyzer import trades
from . import sma_crossover
# Load the bars. This file was manually downloaded from Yahoo Finance.
feed = yahoofeed.Feed()
feed.addBarsFromCSV("orcl", "orcl-2000-yahoofinance.csv")
# Evaluate the strategy with the feed's bars.
myStrategy = sma_crossover.SMACrossOver(feed, "orcl", 20)
# Attach different analyzers to a strategy before executing it.
retAnalyzer = returns.Returns()
myStrategy.attachAnalyzer(retAnalyzer)
sharpeRatioAnalyzer = sharpe.SharpeRatio()
myStrategy.attachAnalyzer(sharpeRatioAnalyzer)
drawDownAnalyzer = drawdown.DrawDown()
myStrategy.attachAnalyzer(drawDownAnalyzer)
tradesAnalyzer = trades.Trades()
myStrategy.attachAnalyzer(tradesAnalyzer)
# Run the strategy.
myStrategy.run()
print("Final portfolio value: $%.2f" % myStrategy.getResult())
print("Cumulative returns: %.2f %%" % (retAnalyzer.getCumulativeReturns()[-1] * 100))
print("Sharpe ratio: %.2f" % (sharpeRatioAnalyzer.getSharpeRatio(0.05)))
print("Max. drawdown: %.2f %%" % (drawDownAnalyzer.getMaxDrawDown() * 100))
print("Longest drawdown duration: %s" % (drawDownAnalyzer.getLongestDrawDownDuration()))
print("")
print("Total trades: %d" % (tradesAnalyzer.getCount()))
if tradesAnalyzer.getCount() > 0:
profits = tradesAnalyzer.getAll()
print("Avg. profit: $%2.f" % (profits.mean()))
print("Profits std. dev.: $%2.f" % (profits.std()))
print("Max. profit: $%2.f" % (profits.max()))
print("Min. profit: $%2.f" % (profits.min()))
returns = tradesAnalyzer.getAllReturns()
print("Avg. return: %2.f %%" % (returns.mean() * 100))
print("Returns std. dev.: %2.f %%" % (returns.std() * 100))
print("Max. return: %2.f %%" % (returns.max() * 100))
print("Min. return: %2.f %%" % (returns.min() * 100))
print("")
print("Profitable trades: %d" % (tradesAnalyzer.getProfitableCount()))
if tradesAnalyzer.getProfitableCount() > 0:
profits = tradesAnalyzer.getProfits()
print("Avg. profit: $%2.f" % (profits.mean()))
print("Profits std. dev.: $%2.f" % (profits.std()))
print("Max. profit: $%2.f" % (profits.max()))
print("Min. profit: $%2.f" % (profits.min()))
returns = tradesAnalyzer.getPositiveReturns()
print("Avg. return: %2.f %%" % (returns.mean() * 100))
print("Returns std. dev.: %2.f %%" % (returns.std() * 100))
print("Max. return: %2.f %%" % (returns.max() * 100))
print("Min. return: %2.f %%" % (returns.min() * 100))
print("")
print("Unprofitable trades: %d" % (tradesAnalyzer.getUnprofitableCount()))
if tradesAnalyzer.getUnprofitableCount() > 0:
losses = tradesAnalyzer.getLosses()
print("Avg. loss: $%2.f" % (losses.mean()))
print("Losses std. dev.: $%2.f" % (losses.std()))
print("Max. loss: $%2.f" % (losses.min()))
print("Min. loss: $%2.f" % (losses.max()))
returns = tradesAnalyzer.getNegativeReturns()
print("Avg. return: %2.f %%" % (returns.mean() * 100))
print("Returns std. dev.: %2.f %%" % (returns.std() * 100))
print("Max. return: %2.f %%" % (returns.max() * 100))
print("Min. return: %2.f %%" % (returns.min() * 100))
The output should look like this:
Final portfolio value: $1295462.60
Cumulative returns: 29.55 %
Sharpe ratio: 0.70
Max. drawdown: 24.58 %
Longest drawdown duration: 277 days, 0:00:00
Total trades: 13
Avg. profit: $14391
Profits std. dev.: $127520
Max. profit: $420782
Min. profit: $-89317
Avg. return: 2 %
Returns std. dev.: 13 %
Max. return: 46 %
Min. return: -7 %
Profitable trades: 3
Avg. profit: $196972
Profits std. dev.: $158985
Max. profit: $420782
Min. profit: $66466
Avg. return: 21 %
Returns std. dev.: 18 %
Max. return: 46 %
Min. return: 6 %
Unprofitable trades: 10
Avg. loss: $-40383
Losses std. dev.: $23579
Max. loss: $-89317
Min. loss: $-4702
Avg. return: -3 %
Returns std. dev.: 2 %
Max. return: -0 %
Min. return: -7 %