stratanalyzer – Strategy analyzers

Strategy analyzers provide an extensible way to attach different calculations to strategy executions.

class quantworks.stratanalyzer.StrategyAnalyzer

Bases: object

Base class for strategy analyzers.

Note

This is a base class and should not be used directly.

Returns

Sharpe Ratio

DrawDown

class quantworks.stratanalyzer.drawdown.DrawDown

Bases: quantworks.stratanalyzer.StrategyAnalyzer

A quantworks.stratanalyzer.StrategyAnalyzer that calculates max. drawdown and longest drawdown duration for the portfolio.

getLongestDrawDownDuration()

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.

getMaxDrawDown()

Returns the max. (deepest) drawdown.

Trades

Example

Save this code as sma_crossover.py:

from quantworks import strategy
from quantworks.technical import ma
from quantworks.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 quantworks.barfeed import yahoofeed
from quantworks.stratanalyzer import returns
from quantworks.stratanalyzer import sharpe
from quantworks.stratanalyzer import drawdown
from quantworks.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 %