Module finlab_crypto.strategy

Strategy function plug-in.

You can use Filter and Strategy function as decorator that make strategies easy to construct filters layers and common strategy detection methods, such as back-testing, parameter tuning and analysis charts.

Typical usage example:

  @Filter(timeperiod=20)
  def your_filter(ohlcv):
      your filter logic...
      return filter > filter_value, figures
  f60 = your_filter.create({'timeperiod': 60})

  @Strategy()
  def your_strategy(ohlcv):
     your strategy logic...
      return entries, exits, figures
  portfolio = your_strategy.backtest(ohlcv, freq='4h', plot=True)
Expand source code
"""Strategy function plug-in.

You can use Filter and Strategy function as decorator
that make strategies easy to construct filters layers
and common strategy detection methods, such as back-testing,
parameter tuning and analysis charts.

  Typical usage example:
```
  @Filter(timeperiod=20)
  def your_filter(ohlcv):
      your filter logic...
      return filter > filter_value, figures
  f60 = your_filter.create({'timeperiod': 60})
```
  -------------------------------
```
  @Strategy()
  def your_strategy(ohlcv):
     your strategy logic...
      return entries, exits, figures
  portfolio = your_strategy.backtest(ohlcv, freq='4h', plot=True)
```
"""

from finlab_crypto.utility import (enumerate_variables, enumerate_signal,
                                   stop_early, plot_combination, plot_strategy,
                                   variable_visualization, remove_pd_object
                                   )
from finlab_crypto.overfitting import CSCV
import copy
import vectorbt as vbt
import pandas as pd
import matplotlib.pyplot as plt
from collections import Iterable


class Filter(object):
    """Filter package features plug-in.

    Offer easy way to create filter to use in class Strategy.

    Attributes:
        default_parameters: customized filter attributes.
    """

    def __init__(self, **default_parameters):
        """inits filter."""
        self.func = None
        self.filters = {}
        self._default_parameters = default_parameters
        self.set_parameters(default_parameters)

    def __call__(self, func):
        """decorator function

        Args
          func: A function of the customized filter.
        """
        self.func = func
        return self

    def set_parameters(self, variables):
        """set your customized filter parameters.

        let filter class use variables dict to set method

        Args:
          variables: a dict of your customized filter attributes.

        """
        if self._default_parameters:
            for key, val in self._default_parameters.items():
                setattr(self, key, val)

        if variables:
            for key, val in variables.items():
                setattr(self, key, val)


    def show_parameters(self):
        parameters = {}
        for key, val in self._default_parameters.items():
            parameters[key] = getattr(self, key)
        print(parameters)

    def create(self, variables=None):
        """generate filter signals, fig_data.

        offer easy way to create filter signals, fig_data

        Args:
          variables: a dict of your customized filter attributes.
        Returns:
          signals: a dataframe of filter signals.
          fig_data: a dict of required data for figure display.
        """

        def ret_f(ohlcv):

            variable_enumerate = enumerate_variables(variables)
            if len(variable_enumerate) == 0:
                variable_enumerate.append(self._default_parameters)

            signals = {}
            fig_data = {}
            for v in variable_enumerate:

                self.set_parameters(v)
                results = self.func(ohlcv)

                v = remove_pd_object(v)

                if isinstance(results, Iterable):
                    signals[str(v)], fig_data = results
                else:
                    signals[str(v)] = results

            signals = pd.DataFrame(signals)
            signals.columns.name = 'filter'

            param_names = list(eval(signals.columns[0]).keys())
            arrays = ([signals.columns.map(lambda s: eval(s)[p]) for p in param_names])
            tuples = list(zip(*arrays))
            columns = pd.MultiIndex.from_tuples(tuples, names=param_names)
            signals.columns = columns

            return signals, fig_data

        return ret_f

class Strategy(object):
    """strategy features plug-in.

    offer common strategy detection methods, such as back-testing,
    parameter tuning and analysis charts.

    Attributes:
        default_parameters: customized strategy attributes.

    """

    def __init__(self, **default_parameters):
        """inits strategy."""
        self.filters = {}
        self._default_parameters = default_parameters
        self.set_parameters(default_parameters)

    def __call__(self, func):
        """decorator function

        Args
          func: A function of customized strategy.
        """
        self.func = func
        return self

    def set_parameters(self, variables):
        """set your customized strategy parameters.

        let strategy class use variables dict to set method.

        Args:
          variables: a dict of your customized strategy attributes.

        """

        # remove stop vars
        stop_vars = ['sl_stop', 'tp_stop', 'ts_stop']
        for svar in stop_vars:
            if hasattr(self, svar):
                delattr(self, svar)

        # set defualt variables
        if self._default_parameters:
            for key, val in self._default_parameters.items():
                setattr(self, key, val)

        # set custom variables
        if variables:
            for key, val in variables.items():
                setattr(self, key, val)

    def show_parameters(self):
        parameters = {}
        for key, val in self._default_parameters.items():
            parameters[key] = getattr(self, key)
        print(parameters)

    @staticmethod
    def _enumerate_filters(ohlcv, filters):
        """enumerate filters data.

        process filter dictionary data to prepare for adding filter signals.

        Args:
          ohlcv: a dataframe of your trading target.
          filters: a dict of your customized filter attributes.

        Returns:
          a dict that generate tuple with filter signal dataframe and figures data.
          for example:

        {'mmi': (timeperiod                    20
          timestamp
          2020-11-25 02:00:00+00:00   true
          2020-11-25 03:00:00+00:00   true
          2020-11-25 04:00:00+00:00   true

          [3 rows x 1 columns], {'figures': {'mmi_index': timestamp
            2020-11-25 02:00:00+00:00    0.7
            2020-11-25 03:00:00+00:00    0.7
            2020-11-25 04:00:00+00:00    0.7
            name: close, length: 28597, dtype: float64}})}

        """
        ret = {}
        for fname, f in filters.items():
            # get filter signals and figures
            filter_df, filter_figures = f(ohlcv)
            ret[fname] = (filter_df, filter_figures)
        return ret

    @staticmethod
    def _add_filters(entries, exits, fig_data, filters):
        """add filters in strategy.

        generate entries, exits, fig_data after add filters.

        Args:
          entries: A dataframe of entries point time series.
          exits: A dataframe of exits point time series.
          fig_data: A dict of your customized figure Attributes.
          filters: A dict of _enumerate_filters function return.

        Returns:
          entries: A dataframe of entries point time series after add filter function.
          exits: A dataframe of exits point time series after add filter function.
          fig_data: A dict of tuple with filter signal dataframe and figures data.

        """
        for fname, (filter_df, filter_figures) in filters.items():
            filter_df.columns = filter_df.columns.set_names([fname + '_' + n for n in filter_df.columns.names])
            entries = filter_df.vbt.tile(entries.shape[1]).vbt & entries.vbt.repeat(filter_df.shape[1]).vbt
            exits = exits.vbt.repeat(filter_df.shape[1])
            exits.columns = entries.columns

            # merge figures
            if filter_figures is not None:
                if 'figures' in filter_figures:
                    if 'figures' not in fig_data:
                        fig_data['figures'] = {}
                    for name, fig in filter_figures['figures'].items():
                        fig_data['figures'][fname + '_' + name] = fig
                if 'overlaps' in filter_figures:
                    if 'overlaps' not in fig_data:
                        fig_data['overlaps'] = {}
                    for name, fig in filter_figures['overlaps'].items():
                        fig_data['overlaps'][fname + '_' + name] = fig

        return entries, exits, fig_data

    @staticmethod
    def _add_stops(ohlcv, entries, exits, variables):
        """Add early trading stop condition in strategy.

        Args:
          ohlcv: A dataframe of your trading target.
          entries: A dataframe of entry point time series.
          exits: A dataframe of exits point time series.
          variables: A dict of your customized strategy Attributes.

        Returns:
          entries: A dataframe of entries point time series after add stop_early function.
          exits: A dataframe of exits point time series after add stop_early function.

        """
        entries, exits = stop_early(ohlcv, entries, exits, variables)
        entries = entries.squeeze()
        exits = exits.squeeze()
        return entries, exits

    def backtest(self, ohlcv, variables=None,
                 filters=None, lookback=None, plot=False,
                 signals=False, side='long', cscv_nbins=10,
                 cscv_objective=lambda r: r.mean(), html=None, compounded=True, execution_price='close', **args):

        """Backtest analysis tool set.
        Use vectorbt as base module to create numerical operations features.
        Use seaborn and pyechart as base modules to create analysis charts platform.

        Args:
          ohlcv: A dataframe of your trading target.
          variables: A dict of your customized strategy Attributes.
            Default is None.
          filters: A dict of your customized filter Attributes.
            Default is None.
          lookback: A int of slice that you want to get recent ohlcv.
            Default is None.
          plot: A bool of control plot display.
            Default is False.
          signals: A bool of controlentries, exits, fig_data return.
            Default is False.
          side: A str of transaction direction,short or long.
            Default is long.
          cscv_nbins: A int of CSCV algorithm bin size to control overfitting calculation.
            Default is 10.
          cscv_objective: A function of in sample(is) and out of sample(oos) return benchmark algorithm.
            Default is lambda r:r.mean().
          html: A str of your customized html format file to show plot.
            Default is None.
          compounded: use compounded return as result of backtesting
            Default is True
          execution_price: price for trading operation ('open' or 'close').
            Default is 'open'
          **args:
            Other parameters.

        Returns:
            A dataframe of vectorbt.Portfolio.from_signals results
            Plot results display.

        Raises:
            'Shorting is not support yet':if side is 'short'.
            "side should be 'long' or 'short'":if side is not 'short' or 'long'.

        """
        variables = variables or dict()
        filters = filters or dict()

        variables_without_stop = copy.copy(variables)
        exit_vars = ['sl_stop', 'ts_stop', 'tp_stop']
        stop_vars = {}
        for e in exit_vars:
            if e in variables_without_stop:
                stop_vars[e] = variables[e]
                variables_without_stop.pop(e)

        ohlcv_lookback = ohlcv.iloc[-lookback:] if lookback else ohlcv

        variable_enumerate = enumerate_variables(variables_without_stop)

        if not variable_enumerate:
            variable_enumerate = [self._default_parameters]

        entries, exits, fig_data = enumerate_signal(ohlcv_lookback, self, variable_enumerate)

        if filters:
            filter_signals = self._enumerate_filters(ohlcv_lookback, filters)
            entries, exits, fig_data = self._add_filters(entries, exits, fig_data, filter_signals)

        entries, exits = self._add_stops(ohlcv_lookback, entries, exits, stop_vars)

        if signals:
            return entries, exits, fig_data

        if side == 'long':

            if not compounded:
                args['size'] = vbt.defaults.portfolio['init_cash'] /  ohlcv_lookback.close[0]

            assert execution_price == 'close' or execution_price == 'open'
            price = ohlcv_lookback[execution_price] if execution_price == 'close' else ohlcv_lookback[execution_price].shift(-1).bfill()

            portfolio = vbt.Portfolio.from_signals(
                ohlcv_lookback[execution_price], entries.fillna(False), exits.fillna(False), **args)

        elif side == 'short':
            raise Exception('Shorting is not support yet')

        else:
            raise Exception("side should be 'long' or 'short'")

        if (plot or html is not None) and isinstance(entries, pd.Series):
            plot_strategy(ohlcv_lookback, entries, exits, portfolio, fig_data, html=html)

        elif plot and isinstance(entries, pd.DataFrame):

            # perform CSCV algorithm
            cscv = CSCV(n_bins=cscv_nbins, objective=cscv_objective)
            cscv.add_daily_returns(portfolio.daily_returns())
            cscv_result = cscv.estimate_overfitting(plot=False)

            # plot results
            plot_combination(portfolio, cscv_result)
            plt.show()
            variable_visualization(portfolio)

        return portfolio

Classes

class Filter (**default_parameters)

Filter package features plug-in.

Offer easy way to create filter to use in class Strategy.

Attributes

default_parameters
customized filter attributes.

inits filter.

Expand source code
class Filter(object):
    """Filter package features plug-in.

    Offer easy way to create filter to use in class Strategy.

    Attributes:
        default_parameters: customized filter attributes.
    """

    def __init__(self, **default_parameters):
        """inits filter."""
        self.func = None
        self.filters = {}
        self._default_parameters = default_parameters
        self.set_parameters(default_parameters)

    def __call__(self, func):
        """decorator function

        Args
          func: A function of the customized filter.
        """
        self.func = func
        return self

    def set_parameters(self, variables):
        """set your customized filter parameters.

        let filter class use variables dict to set method

        Args:
          variables: a dict of your customized filter attributes.

        """
        if self._default_parameters:
            for key, val in self._default_parameters.items():
                setattr(self, key, val)

        if variables:
            for key, val in variables.items():
                setattr(self, key, val)


    def show_parameters(self):
        parameters = {}
        for key, val in self._default_parameters.items():
            parameters[key] = getattr(self, key)
        print(parameters)

    def create(self, variables=None):
        """generate filter signals, fig_data.

        offer easy way to create filter signals, fig_data

        Args:
          variables: a dict of your customized filter attributes.
        Returns:
          signals: a dataframe of filter signals.
          fig_data: a dict of required data for figure display.
        """

        def ret_f(ohlcv):

            variable_enumerate = enumerate_variables(variables)
            if len(variable_enumerate) == 0:
                variable_enumerate.append(self._default_parameters)

            signals = {}
            fig_data = {}
            for v in variable_enumerate:

                self.set_parameters(v)
                results = self.func(ohlcv)

                v = remove_pd_object(v)

                if isinstance(results, Iterable):
                    signals[str(v)], fig_data = results
                else:
                    signals[str(v)] = results

            signals = pd.DataFrame(signals)
            signals.columns.name = 'filter'

            param_names = list(eval(signals.columns[0]).keys())
            arrays = ([signals.columns.map(lambda s: eval(s)[p]) for p in param_names])
            tuples = list(zip(*arrays))
            columns = pd.MultiIndex.from_tuples(tuples, names=param_names)
            signals.columns = columns

            return signals, fig_data

        return ret_f

Methods

def create(self, variables=None)

generate filter signals, fig_data.

offer easy way to create filter signals, fig_data

Args

variables
a dict of your customized filter attributes.

Returns

signals
a dataframe of filter signals.
fig_data
a dict of required data for figure display.
Expand source code
def create(self, variables=None):
    """generate filter signals, fig_data.

    offer easy way to create filter signals, fig_data

    Args:
      variables: a dict of your customized filter attributes.
    Returns:
      signals: a dataframe of filter signals.
      fig_data: a dict of required data for figure display.
    """

    def ret_f(ohlcv):

        variable_enumerate = enumerate_variables(variables)
        if len(variable_enumerate) == 0:
            variable_enumerate.append(self._default_parameters)

        signals = {}
        fig_data = {}
        for v in variable_enumerate:

            self.set_parameters(v)
            results = self.func(ohlcv)

            v = remove_pd_object(v)

            if isinstance(results, Iterable):
                signals[str(v)], fig_data = results
            else:
                signals[str(v)] = results

        signals = pd.DataFrame(signals)
        signals.columns.name = 'filter'

        param_names = list(eval(signals.columns[0]).keys())
        arrays = ([signals.columns.map(lambda s: eval(s)[p]) for p in param_names])
        tuples = list(zip(*arrays))
        columns = pd.MultiIndex.from_tuples(tuples, names=param_names)
        signals.columns = columns

        return signals, fig_data

    return ret_f
def set_parameters(self, variables)

set your customized filter parameters.

let filter class use variables dict to set method

Args

variables
a dict of your customized filter attributes.
Expand source code
def set_parameters(self, variables):
    """set your customized filter parameters.

    let filter class use variables dict to set method

    Args:
      variables: a dict of your customized filter attributes.

    """
    if self._default_parameters:
        for key, val in self._default_parameters.items():
            setattr(self, key, val)

    if variables:
        for key, val in variables.items():
            setattr(self, key, val)
def show_parameters(self)
Expand source code
def show_parameters(self):
    parameters = {}
    for key, val in self._default_parameters.items():
        parameters[key] = getattr(self, key)
    print(parameters)
class Strategy (**default_parameters)

strategy features plug-in.

offer common strategy detection methods, such as back-testing, parameter tuning and analysis charts.

Attributes

default_parameters
customized strategy attributes.

inits strategy.

Expand source code
class Strategy(object):
    """strategy features plug-in.

    offer common strategy detection methods, such as back-testing,
    parameter tuning and analysis charts.

    Attributes:
        default_parameters: customized strategy attributes.

    """

    def __init__(self, **default_parameters):
        """inits strategy."""
        self.filters = {}
        self._default_parameters = default_parameters
        self.set_parameters(default_parameters)

    def __call__(self, func):
        """decorator function

        Args
          func: A function of customized strategy.
        """
        self.func = func
        return self

    def set_parameters(self, variables):
        """set your customized strategy parameters.

        let strategy class use variables dict to set method.

        Args:
          variables: a dict of your customized strategy attributes.

        """

        # remove stop vars
        stop_vars = ['sl_stop', 'tp_stop', 'ts_stop']
        for svar in stop_vars:
            if hasattr(self, svar):
                delattr(self, svar)

        # set defualt variables
        if self._default_parameters:
            for key, val in self._default_parameters.items():
                setattr(self, key, val)

        # set custom variables
        if variables:
            for key, val in variables.items():
                setattr(self, key, val)

    def show_parameters(self):
        parameters = {}
        for key, val in self._default_parameters.items():
            parameters[key] = getattr(self, key)
        print(parameters)

    @staticmethod
    def _enumerate_filters(ohlcv, filters):
        """enumerate filters data.

        process filter dictionary data to prepare for adding filter signals.

        Args:
          ohlcv: a dataframe of your trading target.
          filters: a dict of your customized filter attributes.

        Returns:
          a dict that generate tuple with filter signal dataframe and figures data.
          for example:

        {'mmi': (timeperiod                    20
          timestamp
          2020-11-25 02:00:00+00:00   true
          2020-11-25 03:00:00+00:00   true
          2020-11-25 04:00:00+00:00   true

          [3 rows x 1 columns], {'figures': {'mmi_index': timestamp
            2020-11-25 02:00:00+00:00    0.7
            2020-11-25 03:00:00+00:00    0.7
            2020-11-25 04:00:00+00:00    0.7
            name: close, length: 28597, dtype: float64}})}

        """
        ret = {}
        for fname, f in filters.items():
            # get filter signals and figures
            filter_df, filter_figures = f(ohlcv)
            ret[fname] = (filter_df, filter_figures)
        return ret

    @staticmethod
    def _add_filters(entries, exits, fig_data, filters):
        """add filters in strategy.

        generate entries, exits, fig_data after add filters.

        Args:
          entries: A dataframe of entries point time series.
          exits: A dataframe of exits point time series.
          fig_data: A dict of your customized figure Attributes.
          filters: A dict of _enumerate_filters function return.

        Returns:
          entries: A dataframe of entries point time series after add filter function.
          exits: A dataframe of exits point time series after add filter function.
          fig_data: A dict of tuple with filter signal dataframe and figures data.

        """
        for fname, (filter_df, filter_figures) in filters.items():
            filter_df.columns = filter_df.columns.set_names([fname + '_' + n for n in filter_df.columns.names])
            entries = filter_df.vbt.tile(entries.shape[1]).vbt & entries.vbt.repeat(filter_df.shape[1]).vbt
            exits = exits.vbt.repeat(filter_df.shape[1])
            exits.columns = entries.columns

            # merge figures
            if filter_figures is not None:
                if 'figures' in filter_figures:
                    if 'figures' not in fig_data:
                        fig_data['figures'] = {}
                    for name, fig in filter_figures['figures'].items():
                        fig_data['figures'][fname + '_' + name] = fig
                if 'overlaps' in filter_figures:
                    if 'overlaps' not in fig_data:
                        fig_data['overlaps'] = {}
                    for name, fig in filter_figures['overlaps'].items():
                        fig_data['overlaps'][fname + '_' + name] = fig

        return entries, exits, fig_data

    @staticmethod
    def _add_stops(ohlcv, entries, exits, variables):
        """Add early trading stop condition in strategy.

        Args:
          ohlcv: A dataframe of your trading target.
          entries: A dataframe of entry point time series.
          exits: A dataframe of exits point time series.
          variables: A dict of your customized strategy Attributes.

        Returns:
          entries: A dataframe of entries point time series after add stop_early function.
          exits: A dataframe of exits point time series after add stop_early function.

        """
        entries, exits = stop_early(ohlcv, entries, exits, variables)
        entries = entries.squeeze()
        exits = exits.squeeze()
        return entries, exits

    def backtest(self, ohlcv, variables=None,
                 filters=None, lookback=None, plot=False,
                 signals=False, side='long', cscv_nbins=10,
                 cscv_objective=lambda r: r.mean(), html=None, compounded=True, execution_price='close', **args):

        """Backtest analysis tool set.
        Use vectorbt as base module to create numerical operations features.
        Use seaborn and pyechart as base modules to create analysis charts platform.

        Args:
          ohlcv: A dataframe of your trading target.
          variables: A dict of your customized strategy Attributes.
            Default is None.
          filters: A dict of your customized filter Attributes.
            Default is None.
          lookback: A int of slice that you want to get recent ohlcv.
            Default is None.
          plot: A bool of control plot display.
            Default is False.
          signals: A bool of controlentries, exits, fig_data return.
            Default is False.
          side: A str of transaction direction,short or long.
            Default is long.
          cscv_nbins: A int of CSCV algorithm bin size to control overfitting calculation.
            Default is 10.
          cscv_objective: A function of in sample(is) and out of sample(oos) return benchmark algorithm.
            Default is lambda r:r.mean().
          html: A str of your customized html format file to show plot.
            Default is None.
          compounded: use compounded return as result of backtesting
            Default is True
          execution_price: price for trading operation ('open' or 'close').
            Default is 'open'
          **args:
            Other parameters.

        Returns:
            A dataframe of vectorbt.Portfolio.from_signals results
            Plot results display.

        Raises:
            'Shorting is not support yet':if side is 'short'.
            "side should be 'long' or 'short'":if side is not 'short' or 'long'.

        """
        variables = variables or dict()
        filters = filters or dict()

        variables_without_stop = copy.copy(variables)
        exit_vars = ['sl_stop', 'ts_stop', 'tp_stop']
        stop_vars = {}
        for e in exit_vars:
            if e in variables_without_stop:
                stop_vars[e] = variables[e]
                variables_without_stop.pop(e)

        ohlcv_lookback = ohlcv.iloc[-lookback:] if lookback else ohlcv

        variable_enumerate = enumerate_variables(variables_without_stop)

        if not variable_enumerate:
            variable_enumerate = [self._default_parameters]

        entries, exits, fig_data = enumerate_signal(ohlcv_lookback, self, variable_enumerate)

        if filters:
            filter_signals = self._enumerate_filters(ohlcv_lookback, filters)
            entries, exits, fig_data = self._add_filters(entries, exits, fig_data, filter_signals)

        entries, exits = self._add_stops(ohlcv_lookback, entries, exits, stop_vars)

        if signals:
            return entries, exits, fig_data

        if side == 'long':

            if not compounded:
                args['size'] = vbt.defaults.portfolio['init_cash'] /  ohlcv_lookback.close[0]

            assert execution_price == 'close' or execution_price == 'open'
            price = ohlcv_lookback[execution_price] if execution_price == 'close' else ohlcv_lookback[execution_price].shift(-1).bfill()

            portfolio = vbt.Portfolio.from_signals(
                ohlcv_lookback[execution_price], entries.fillna(False), exits.fillna(False), **args)

        elif side == 'short':
            raise Exception('Shorting is not support yet')

        else:
            raise Exception("side should be 'long' or 'short'")

        if (plot or html is not None) and isinstance(entries, pd.Series):
            plot_strategy(ohlcv_lookback, entries, exits, portfolio, fig_data, html=html)

        elif plot and isinstance(entries, pd.DataFrame):

            # perform CSCV algorithm
            cscv = CSCV(n_bins=cscv_nbins, objective=cscv_objective)
            cscv.add_daily_returns(portfolio.daily_returns())
            cscv_result = cscv.estimate_overfitting(plot=False)

            # plot results
            plot_combination(portfolio, cscv_result)
            plt.show()
            variable_visualization(portfolio)

        return portfolio

Methods

def backtest(self, ohlcv, variables=None, filters=None, lookback=None, plot=False, signals=False, side='long', cscv_nbins=10, cscv_objective=<function Strategy.<lambda>>, html=None, compounded=True, execution_price='close', **args)

Backtest analysis tool set. Use vectorbt as base module to create numerical operations features. Use seaborn and pyechart as base modules to create analysis charts platform.

Args

ohlcv
A dataframe of your trading target.
variables
A dict of your customized strategy Attributes. Default is None.
filters
A dict of your customized filter Attributes. Default is None.
lookback
A int of slice that you want to get recent ohlcv. Default is None.
plot
A bool of control plot display. Default is False.
signals
A bool of controlentries, exits, fig_data return. Default is False.
side
A str of transaction direction,short or long. Default is long.
cscv_nbins
A int of CSCV algorithm bin size to control overfitting calculation. Default is 10.
cscv_objective
A function of in sample(is) and out of sample(oos) return benchmark algorithm. Default is lambda r:r.mean().
html
A str of your customized html format file to show plot. Default is None.
compounded
use compounded return as result of backtesting Default is True
execution_price
price for trading operation ('open' or 'close'). Default is 'open'

**args: Other parameters.

Returns

A dataframe of vectorbt.Portfolio.from_signals results Plot results display.

Raises

'Shorting is not support yet':if side is 'short'. "side should be 'long' or 'short'":if side is not 'short' or 'long'.

Expand source code
def backtest(self, ohlcv, variables=None,
             filters=None, lookback=None, plot=False,
             signals=False, side='long', cscv_nbins=10,
             cscv_objective=lambda r: r.mean(), html=None, compounded=True, execution_price='close', **args):

    """Backtest analysis tool set.
    Use vectorbt as base module to create numerical operations features.
    Use seaborn and pyechart as base modules to create analysis charts platform.

    Args:
      ohlcv: A dataframe of your trading target.
      variables: A dict of your customized strategy Attributes.
        Default is None.
      filters: A dict of your customized filter Attributes.
        Default is None.
      lookback: A int of slice that you want to get recent ohlcv.
        Default is None.
      plot: A bool of control plot display.
        Default is False.
      signals: A bool of controlentries, exits, fig_data return.
        Default is False.
      side: A str of transaction direction,short or long.
        Default is long.
      cscv_nbins: A int of CSCV algorithm bin size to control overfitting calculation.
        Default is 10.
      cscv_objective: A function of in sample(is) and out of sample(oos) return benchmark algorithm.
        Default is lambda r:r.mean().
      html: A str of your customized html format file to show plot.
        Default is None.
      compounded: use compounded return as result of backtesting
        Default is True
      execution_price: price for trading operation ('open' or 'close').
        Default is 'open'
      **args:
        Other parameters.

    Returns:
        A dataframe of vectorbt.Portfolio.from_signals results
        Plot results display.

    Raises:
        'Shorting is not support yet':if side is 'short'.
        "side should be 'long' or 'short'":if side is not 'short' or 'long'.

    """
    variables = variables or dict()
    filters = filters or dict()

    variables_without_stop = copy.copy(variables)
    exit_vars = ['sl_stop', 'ts_stop', 'tp_stop']
    stop_vars = {}
    for e in exit_vars:
        if e in variables_without_stop:
            stop_vars[e] = variables[e]
            variables_without_stop.pop(e)

    ohlcv_lookback = ohlcv.iloc[-lookback:] if lookback else ohlcv

    variable_enumerate = enumerate_variables(variables_without_stop)

    if not variable_enumerate:
        variable_enumerate = [self._default_parameters]

    entries, exits, fig_data = enumerate_signal(ohlcv_lookback, self, variable_enumerate)

    if filters:
        filter_signals = self._enumerate_filters(ohlcv_lookback, filters)
        entries, exits, fig_data = self._add_filters(entries, exits, fig_data, filter_signals)

    entries, exits = self._add_stops(ohlcv_lookback, entries, exits, stop_vars)

    if signals:
        return entries, exits, fig_data

    if side == 'long':

        if not compounded:
            args['size'] = vbt.defaults.portfolio['init_cash'] /  ohlcv_lookback.close[0]

        assert execution_price == 'close' or execution_price == 'open'
        price = ohlcv_lookback[execution_price] if execution_price == 'close' else ohlcv_lookback[execution_price].shift(-1).bfill()

        portfolio = vbt.Portfolio.from_signals(
            ohlcv_lookback[execution_price], entries.fillna(False), exits.fillna(False), **args)

    elif side == 'short':
        raise Exception('Shorting is not support yet')

    else:
        raise Exception("side should be 'long' or 'short'")

    if (plot or html is not None) and isinstance(entries, pd.Series):
        plot_strategy(ohlcv_lookback, entries, exits, portfolio, fig_data, html=html)

    elif plot and isinstance(entries, pd.DataFrame):

        # perform CSCV algorithm
        cscv = CSCV(n_bins=cscv_nbins, objective=cscv_objective)
        cscv.add_daily_returns(portfolio.daily_returns())
        cscv_result = cscv.estimate_overfitting(plot=False)

        # plot results
        plot_combination(portfolio, cscv_result)
        plt.show()
        variable_visualization(portfolio)

    return portfolio
def set_parameters(self, variables)

set your customized strategy parameters.

let strategy class use variables dict to set method.

Args

variables
a dict of your customized strategy attributes.
Expand source code
def set_parameters(self, variables):
    """set your customized strategy parameters.

    let strategy class use variables dict to set method.

    Args:
      variables: a dict of your customized strategy attributes.

    """

    # remove stop vars
    stop_vars = ['sl_stop', 'tp_stop', 'ts_stop']
    for svar in stop_vars:
        if hasattr(self, svar):
            delattr(self, svar)

    # set defualt variables
    if self._default_parameters:
        for key, val in self._default_parameters.items():
            setattr(self, key, val)

    # set custom variables
    if variables:
        for key, val in variables.items():
            setattr(self, key, val)
def show_parameters(self)
Expand source code
def show_parameters(self):
    parameters = {}
    for key, val in self._default_parameters.items():
        parameters[key] = getattr(self, key)
    print(parameters)