Example #1
0
def backtest(
        strategy,
        data,  # Treated as csv path is str, and dataframe of pd.DataFrame
        commission=COMMISSION_PER_TRANSACTION,
        init_cash=INIT_CASH,
        plot=True,
        verbose=True,
        sort_by="rnorm",
        sentiments=None,
        strats=None,  # Only used when strategy = "multi"
        data_format=None,  # No longer needed but will leave for now to warn removal in a coming release
        return_history=False,
        channel=None,
        symbol=None,
        allow_short=False,
        short_max=1.5,
        **kwargs):
    """Backtest financial data with a specified trading strategy

    Parameters
    ----------------
    strategy : str or an instance of `fastquant.strategies.base.BaseStrategy`
        see list of accepted strategy keys below
    data : pandas.DataFrame
        dataframe with at least close price indexed with time
    commission : float
        commission per transaction [0, 1]
    init_cash : float
        initial cash (currency implied from `data`)
    plot : bool
        show plot backtrader (disabled if `strategy`=="multi")
    sort_by : str
        sort result by given metric (default='rnorm')
    sentiments : pandas.DataFrame
        df of sentiment [0, 1] indexed by time (applicable if `strategy`=='senti')
    strats : dict
        dictionary of strategy parameters (applicable if `strategy`=='multi')
    return_history : bool
        return history of transactions (i.e. buy and sell timestamps) (default=False)
    channel : str
        Channel to be used for last day notification - e.g. "slack" (default=None)
    verbose : int
        Verbose can take values: [-1, 0, 1, 2], with increasing levels of verbosity (default=0).
    symbol : str
        Symbol to be referenced in the channel notification if not None (default=None)
    {0}
    """

    if data_format:
        errmsg = "Warning: data_format argument is no longer needed since formatting is now purely automated based on column names!"
        errmsg += "\nWe will be removing this argument in a coming release!"
        warnings.warn(errmsg, DeprecationWarning)
        print(errmsg)

    # Setting inital support for 1 cpu
    # Return the full strategy object to get all run information
    cerebro = bt.Cerebro(stdstats=False, maxcpus=1, optreturn=False)
    cerebro.addobserver(bt.observers.Broker)
    cerebro.addobserver(bt.observers.Trades)
    cerebro.addobserver(bt.observers.BuySell)

    # Convert all non iterables and strings into lists
    kwargs = {
        k: v if isinstance(v, Iterable) and not isinstance(v, str) else [v]
        for k, v in kwargs.items()
    }

    strat_names = []
    strat_name = None
    if strategy == "multi" and strats is not None:
        for strat, params in strats.items():
            cerebro.optstrategy(STRATEGY_MAPPING[strat],
                                init_cash=[init_cash],
                                transaction_logging=[verbose],
                                commission=commission,
                                channel=None,
                                symbol=None,
                                allow_short=allow_short,
                                short_max=short_max,
                                **params)
            strat_names.append(strat)
    else:

        # Allow instance of BaseStrategy or from the predefined mapping
        if not isinstance(strategy, str) and issubclass(strategy, bt.Strategy):
            strat_name = str(strategy)
        else:
            strat_name = strategy
            strategy = STRATEGY_MAPPING[strategy]

        cerebro.optstrategy(strategy,
                            init_cash=[init_cash],
                            transaction_logging=[verbose],
                            commission=commission,
                            channel=None,
                            symbol=None,
                            allow_short=allow_short,
                            short_max=short_max,
                            **kwargs)
        strat_names.append(strat_name)

    # Apply Total, Average, Compound and Annualized Returns calculated using a logarithmic approach
    cerebro.addanalyzer(btanalyzers.Returns, _name="returns")
    cerebro.addanalyzer(btanalyzers.SharpeRatio, _name="mysharpe")
    cerebro.addanalyzer(btanalyzers.DrawDown, _name="drawdown")
    cerebro.addanalyzer(btanalyzers.TimeDrawDown, _name="timedraw")

    cerebro.broker.setcommission(commission=commission)

    # Treat `data` as a path if it's a string; otherwise, it's treated as a pandas dataframe
    if isinstance(data, str):
        if verbose:
            print("Reading path as pandas dataframe ...")
        # Rename dt to datetime
        data = pd.read_csv(data, header=0, parse_dates=["dt"])

    if strat_name == "sentiment":
        # initialize series for sentiments
        senti_series = pd.Series(sentiments,
                                 name="sentiment_score",
                                 dtype=float)

        # join and reset the index for dt to become the first column
        data = data.merge(senti_series,
                          left_index=True,
                          right_index=True,
                          how="left")
        data = data.reset_index()

    # If a `close` column exists but an `open` column doesn't, create a new `open` column with the same values as the `close` column
    # This is for easier handling of next day trades (w/ the assumption that next day open is equal to current day close)
    if "close" in data.columns and "open" not in data.columns:
        data["open"] = data.close.shift().values

    # If data has `dt` as the index and `dt` or `datetime` are not already columns, set `dt` as the first column
    # This means `backtest` supports the dataframe whether `dt` is the index or a column
    if len(set(["dt", "datetime"]).intersection(data.columns)) == 0:
        if data.index.name == "dt":
            data = data.reset_index()
        # If the index is a datetime index, set this as the datetime column
        elif isinstance(data.index, pd.DatetimeIndex):
            data.index.name = "dt"
            data = data.reset_index()

    # Rename "dt" column to "datetime" to match the formal alias
    data = data.rename(columns={"dt": "datetime"})
    data["datetime"] = pd.to_datetime(data.datetime)

    numeric_cols = [col for col in data.columns if is_numeric_dtype(data[col])]
    params_tuple = tuple([(col, i) for i, col in enumerate(data.columns)
                          if col in numeric_cols + ["datetime"]])
    default_cols = [c for c, _ in DEFAULT_PANDAS]
    non_default_numeric_cols = tuple(
        [col for col, _ in params_tuple if col not in default_cols])

    class CustomData(bt.feeds.PandasData):
        """
        Data feed that includes all the columns in the input dataframe
        """

        # Need to make sure that the new lines don't overlap w/ the default lines already in PandasData
        lines = non_default_numeric_cols

        # automatically handle parameter with -1
        # add the parameter to the parameters inherited from the base class
        params = params_tuple

    # extend the dataframe with sentiment score
    if strat_name == "sentiment":
        data_format_dict = tuple_to_dict(params_tuple)
        # create CustomData which inherits from PandasData
        pd_data = CustomData(dataname=data, **data_format_dict)

    else:
        data_format_dict = tuple_to_dict(params_tuple)
        pd_data = CustomData(dataname=data, **data_format_dict)

    cerebro.adddata(pd_data)
    cerebro.broker.setcash(init_cash)
    # Allows us to set buy price based on next day closing
    # (technically impossible, but reasonable assuming you use all your money to buy market at the end of the next day)
    cerebro.broker.set_coc(True)
    if verbose:
        print("Starting Portfolio Value: %.2f" % cerebro.broker.getvalue())

    # clock the start of the process
    tstart = time.time()
    stratruns = cerebro.run()

    # clock the end of the process
    tend = time.time()

    params = []
    metrics = []
    if verbose:
        print("==================================================")
        print("Number of strat runs:", len(stratruns))
        print("Number of strats per run:", len(stratruns[0]))
        print("Strat names:", strat_names)

    order_history_dfs = []
    periodic_history_dfs = []
    indicator_history_dfs = []
    for strat_idx, stratrun in enumerate(stratruns):
        strats_params = {}

        if verbose:
            print("**************************************************")

        for i, strat in enumerate(stratrun):
            # Get indicator history
            indicators = strat.getindicators()
            indicators_dict = {
                ind.plotlabel() if hasattr(ind, "plotlabel") else
                "indicator{}".format(i): ind.lines[0].array
                for i, ind in enumerate(indicators)
            }
            indicators_df = pd.DataFrame(indicators_dict)
            indicators_df.insert(0, "dt", data["datetime"].values)

            strat_name = strat_names[i]
            p_raw = strat.p._getkwargs()
            p, selected_p = {}, {}
            for k, v in p_raw.items():
                if k not in ["periodic_logging", "transaction_logging"]:
                    # Make sure the parameters are mapped to the corresponding strategy
                    if strategy == "multi":
                        key = ("{}.{}".format(strat_name, k)
                               if k not in GLOBAL_PARAMS else k)
                        # make key with format: e.g. smac.slow_period40_fast_period10
                        if k in strats[strat_name]:
                            selected_p[k] = v
                        pkeys = "_".join(
                            ["{}{}".format(*i) for i in selected_p.items()])
                        history_key = "{}.{}".format(strat_name, pkeys)
                    else:
                        key = k

                        # make key with format: e.g. slow_period40_fast_period10
                        if k in kwargs.keys():
                            selected_p[k] = v
                        history_key = "_".join(
                            ["{}{}".format(*i) for i in selected_p.items()])
                    p[key] = v

            strats_params = {**strats_params, **p}

            if return_history:
                # columns are decided in log method of BaseStrategy class in base.py
                order_history_df = strat.order_history_df
                order_history_df["dt"] = pd.to_datetime(order_history_df.dt)
                # combine rows with identical index
                # history_df = order_history_df.set_index('dt').dropna(how='all')
                # history_dfs[history_key] = order_history_df.stack().unstack().astype(float)
                order_history_df.insert(0, "strat_name", history_key)
                order_history_df.insert(0, "strat_id", strat_idx)
                order_history_dfs.append(order_history_df)

                periodic_history_df = strat.periodic_history_df
                periodic_history_df["dt"] = pd.to_datetime(
                    periodic_history_df.dt)
                periodic_history_df.insert(0, "strat_name", history_key)
                periodic_history_df.insert(0, "strat_id", strat_idx)
                periodic_history_df[
                    "return"] = periodic_history_df.portfolio_value.pct_change(
                    )
                periodic_history_dfs.append(periodic_history_df)

                indicators_df.insert(0, "strat_name", history_key)
                indicators_df.insert(0, "strat_id", strat_idx)
                indicator_history_dfs.append(indicators_df)

        # We run metrics on the last strat since all the metrics will be the same for all strats
        returns = strat.analyzers.returns.get_analysis()
        sharpe = strat.analyzers.mysharpe.get_analysis()
        drawdown = strat.analyzers.drawdown.get_analysis()
        timedraw = strat.analyzers.timedraw.get_analysis()
        # Combine dicts for returns and sharpe
        m = {
            **returns,
            **drawdown,
            **timedraw,
            **sharpe,
            "pnl": strat.pnl,
            "final_value": strat.final_value,
        }

        params.append(strats_params)
        metrics.append(m)
        if verbose:
            print("--------------------------------------------------")
            print(strats_params)
            print(returns)
            print(sharpe)
            print(drawdown)
            print(timedraw)

    params_df = pd.DataFrame(params)
    # Set the index as a separate strat id column, so that we retain the information after sorting
    strat_ids = pd.DataFrame({"strat_id": params_df.index.values})
    metrics_df = pd.DataFrame(metrics)

    # Get indices based on `sort_by` metric
    optim_idxs = np.argsort(metrics_df[sort_by].values)[::-1]
    sorted_params_df = params_df.iloc[optim_idxs].reset_index(drop=True)
    sorted_metrics_df = metrics_df.iloc[optim_idxs].reset_index(drop=True)
    sorted_strat_ids = strat_ids.iloc[optim_idxs].reset_index(drop=True)
    sorted_combined_df = pd.concat(
        [sorted_strat_ids, sorted_params_df, sorted_metrics_df], axis=1)

    # print out the result
    print("Time used (seconds):", str(tend - tstart))

    # Save optimal parameters as dictionary
    optim_params = sorted_params_df.iloc[0].to_dict()
    optim_metrics = sorted_metrics_df.iloc[0].to_dict()
    print("Optimal parameters:", optim_params)
    print("Optimal metrics:", optim_metrics)

    if plot and strategy != "multi":
        has_volume = (data_format_dict["volume"] is not None
                      if "volume" in data_format_dict.keys() else False)
        # Plot only with the optimal parameters when multiple strategy runs are required
        if params_df.shape[0] == 1:
            # This handles the Colab Plotting
            # Simple Check if we are in Colab
            try:
                from google.colab import drive

                iplot = False

            except Exception:
                iplot = True
            cerebro.plot(volume=has_volume, figsize=(30, 15), iplot=iplot)
        else:
            print("=============================================")
            print("Plotting backtest for optimal parameters ...")
            backtest(
                strategy,
                data,  # Treated as csv path is str, and dataframe of pd.DataFrame
                plot=plot,
                verbose=verbose,
                sort_by=sort_by,
                **optim_params)
    # drop extra columns #248
    if (len(
            set(["channel"
                 "symbol"
                 ]).intersection(sorted_combined_df.columns.values)) == 2):
        sorted_combined_df.drop(["channel", "symbol"], axis=1, inplace=True)
    if return_history:
        order_history = pd.concat(order_history_dfs)
        periodic_history = pd.concat(periodic_history_dfs)
        indicator_history = pd.concat(indicator_history_dfs)
        history_dict = dict(
            orders=order_history,
            periodic=periodic_history,
            indicators=indicator_history,
        )

        return sorted_combined_df, history_dict
    else:
        return sorted_combined_df
Example #2
0
from mean_reversion import MeanReversion

import datetime as dt
import pandas as pd
import backtrader as bt
import backtrader.feeds as feeds
import backtrader.indicators as btind
import backtrader.analyzers as btanalyzers

import matplotlib

if __name__ == '__main__':
    startTime = dt.datetime.now()
    strategy = MeanReversion
    startcash = 10  #BTC
    cerebro = bt.Cerebro(runonce=False, optreturn=False)
    cerebro.addstrategy(strategy)

    # Used when you don't have the csv data and need to pull from the internet
    #    one_year = dt.timedelta(days=365)
    #    days_100 = dt.timedelta(days=100)
    #    days_150 = dt.timedelta(days=150)
    #    days_30  = dt.timedelta(days=30)
    #    month_6  = dt.timedelta(days=180)
    #    start = dt.datetime.now() - days_100
    #    pipeline = CoinbasePipeline('BTC-USD',start=start, granularity=3600)
    #    dataframe = pipeline.get_data()
    #
    dataframe = pd.read_csv("./hist-data/ETH-BTC-100d-1hr-12-16.csv",
                            index_col="datetime",
                            parse_dates=['datetime'])
def backtest_strategy(df: pd.DataFrame,
                      strategy: Type[BaseStrategy],
                      date_from: datetime = None,
                      date_to: datetime = None,
                      perc_size=10,
                      commission=0.00075,
                      start_cash=1000.0,
                      returns_analyzer=False,
                      use_replay=False,
                      df_resample=None,
                      timeframe=bt.TimeFrame.Minutes,
                      compression=60,
                      output=None,
                      *args,
                      **kwargs) -> Tuple[bt.Cerebro, Any]:
    cerebro = bt.Cerebro()

    if date_from is None:
        date_from = df.index.min().to_pydatetime()

    if date_to is None:
        date_to = df.index.max().to_pydatetime()

    # data
    data = backtrader_data(dataname=df, fromdate=date_from, todate=date_to)

    if use_replay:
        cerebro.replaydata(data, timeframe=timeframe, compression=compression)
    else:
        cerebro.adddata(data)

    if df_resample is not None:
        data_resample = BacktestData(dataname=df_resample)
        cerebro.resampledata(data_resample,
                             timeframe=timeframe,
                             compression=compression)

    # strategy
    cerebro.addstrategy(strategy, *args, **kwargs)

    # analysers
    cerebro.addanalyzer(bt.analyzers.SharpeRatio,
                        _name='sharpe_ratio',
                        timeframe=bt.TimeFrame.Days)
    if returns_analyzer:
        cerebro.addanalyzer(bt.analyzers.LogReturnsRolling,
                            _name='log_returns_rolling')
        cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='draw_down')
    cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')
    cerebro.addanalyzer(bt.analyzers.VWR, _name='vwr')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trade')
    cerebro.addanalyzer(bt.analyzers.GrossLeverage, _name='gross_leverage')
    # cerebro.addanalyzer(bt.analyzers.Calmar, _name='calmar')

    # params
    cerebro.broker.setcash(start_cash)
    cerebro.broker.setcommission(commission=commission)
    cerebro.addsizer(bt.sizers.PercentSizer, percents=perc_size)
    cerebro.signal_accumulate(False)
    cerebro.signal_concurrent(False)
    if output is None:
        if 'verbose' in kwargs and kwargs['verbose']:
            import sys
            output = sys.stdout
        else:
            import os
            output = open(os.devnull, "w")
    cerebro.addwriter(bt.WriterFile, csv=True, out=output)
    # run
    thestrats = cerebro.run(runonce=False)

    return cerebro, thestrats
Example #4
0
    def testStrat(self):
        startcash = 100000
        cerebro = bt.Cerebro()
        strat_params = [
            ('AGG', 10),
            ('IEMG', 10),
            ('IAU', 10),
            ('MBB', 10),
            ('IXUS', 10),
            ('IJH', 10),
            ('IJR', 10),
            ('IVW', 10),
            ('IUSV', 10),
            ('IGV', 10),
        ]
        symbol_list = [x[0] for x in strat_params]
        dont_plot = []
        # Add our strategy
        cerebro.addstrategy(RebalanceStrategy, assets=strat_params)

        is_first=True
        for tick in symbol_list:
            data = bt.feeds.YahooFinanceData(dataname=tick,
                                             fromdate=datetime(2017, 1, 1),
                                             todate=datetime(2020, 9, 1))
            if tick not in dont_plot:
                if is_first:
                    data.plotinfo.plotlinelabels = False
                    data.plotinfo.plotylimited = False
                    data.plotinfo.sameaxis = True
                    data_main_plot = data
                    is_first = False
                else:
                    #data.compensate(last_data)
                    data.plotinfo.plotmaster = data_main_plot
                    data.plotinfo.plotlinelabels = False
                    data.plotinfo.plotylimited = False
                    data.plotinfo.sameaxis = True
            else:
                data.plotinfo.plot = False
            cerebro.adddata(data)
            #last_data = copy.deepcopy(data)

        # Set our desired cash start and run settings
        cerebro.broker.setcash(startcash)
        cerebro.addanalyzer(bta.SharpeRatio, _name='mysharpe')
        cerebro.addanalyzer(bta.DrawDown, _name='drawdown')
        cerebro.addanalyzer(bta.TimeDrawDown, _name='drawdowntime')
        cerebro.addanalyzer(bta.PositionsValue, _name='positionsValue', cash=True)  ##
        cerebro.addanalyzer(bta.Returns, _name='returns')
        cerebro.addanalyzer(bta.Transactions, _name='trxns')
        cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="ta")
        cerebro.broker.set_checksubmit(False)

        cerebro.addobserver(AcctValue)
        cerebro.addobservermulti(bto.BuySell, plotlinelabels=False, plotname=False)
        # Run the backtest
        results = cerebro.run(stdstats=False)
        cerebro.plot(iplot=True, volume=False)

        # Get final portfolio Value
        portfolio_value = cerebro.broker.getvalue()
        pnl = portfolio_value - startcash
        #self.assertEquals(pnl, 78239.86999999997)

        # Analyze Results
        analyzers = results[0].analyzers
        trxns = analyzers.trxns.get_analysis()
        trades = analyzers.ta.get_analysis()
        tot_returns = analyzers.returns.get_analysis()
        sharpe = analyzers.mysharpe.get_analysis()
        positionvals = analyzers.positionsValue.get_analysis()
        position_hist = pd.DataFrame(positionvals).T
        position_hist.columns = symbol_list + ["Cash"]
        portfoliovals = position_hist.sum(axis=1)
        returns = np.log(portfoliovals / portfoliovals.shift(1))[1:]
        max_drawdown = analyzers.drawdown.get_analysis()

        # Get SPY returns to compare
        spy_st = pd.Series(returns.cumsum()).index[0]
        spy_end = pd.Series(returns.cumsum()).index[-1]
        SPY_df = web.DataReader('SPY', 'yahoo', spy_st, spy_end)
        SPY_returns = pd.Series(np.log(SPY_df.Close / SPY_df.Close.shift(1)).cumsum())
        # Get SPY $ Portfolio Value
        SPY_portfolio_values = (startcash / SPY_df.Close[0]) * SPY_df.Close

        # Plot Returns Comparison
        plt.clf()
        pd.Series(returns.cumsum()).plot(label="Portfolio Returns")
        pd.Series(SPY_returns).plot(label="SPY Returns")
        plt.title("Portfolio Returns")
        plt.ylabel("Log Returns")
        plt.legend()
        plt.show()

        # Plot Returns Comparison
        plt.clf()
        pd.Series(portfoliovals).plot(label="Portfolio $ Value")
        pd.Series(SPY_portfolio_values).plot(label="SPY Portfolio $ Value")
        plt.title("Portfolio Value")
        plt.ylabel("Dollars")
        plt.legend()
        plt.show()

        # Print Out the Backtest Analysis
        # for x in results[0].analyzers:
        #     x.print()

        print('Final Portfolio Value: ${}'.format(portfolio_value))
        print('Portfololio PnL: ${}'.format(pnl))
        print("Portfolio Total Log Returns: {}".format(tot_returns["rtot"]))
        print("Portfolio Sharpe Ratio: {}".format(sharpe['sharperatio']))
        print(
            "Portfolio Max Drawdown: ${}, Time: {} days".format(max_drawdown.max["moneydown"], max_drawdown.max["len"]))
        print("---Compare to SPY---")
        print("SPY Portfolio Value: ${}".format(SPY_portfolio_values[-1]))
        print("SPY Portfolio Pnl: ${}".format(SPY_portfolio_values[-1] - startcash))
        print("SPY Total Log Returns: {}".format(SPY_returns[-1]))

        # Get Round Trip Trade Info
        printTradeAnalysis(trades)
        print("Portfolio History:")
        print(position_hist)
Example #5
0
def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()
    cerebro.broker.set_cash(args.cash)
    comminfo = bt.commissions.CommInfo_Stocks_Perc(commission=args.commperc,
                                                   percabs=True)

    cerebro.broker.addcommissioninfo(comminfo)
    #cerebro.broker.setcommission(commission=0.0)

    dkwargs = dict()
    if args.fromdate is not None:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        dkwargs['fromdate'] = fromdate

    if args.todate is not None:
        todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        dkwargs['todate'] = todate

    # if dataset is None, args.data has been given
    dataname = DATASETS.get(args.dataset, args.data)
    if args.datafile:
        dataname = args.datafile

        data0 = bt.feeds.GenericCSVData(dataname=dataname,
                                        dtformat=('%Y-%m-%d'),
                                        date=0,
                                        open=1,
                                        high=2,
                                        low=3,
                                        close=4,
                                        volume=5,
                                        openinterest=-1,
                                        **dkwargs)

    cerebro.adddata(data0)

    cerebro.addstrategy(TheRSIStrategy)

    #cerebro.addsizer(FixedPerc, perc=0.96)
    cerebro.addsizer(LongOnly)

    cerebro.broker.setcash(50000.0)

    # Print out the starting conditions
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

    results = cerebro.run()
    st0 = results[0]

    # Print out the final result
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

    if args.plot:
        pkwargs = dict(style='bar')
        pkwargs = dict()
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)

        cerebro.plot(**pkwargs)
Example #6
0
def BackTesting(fromdate=None):

    # Datafeed from file
    class MyCSVData(bt.feeds.GenericCSVData):
        params = (
            ('nullvalue', float('NaN')),
            ('dtformat', '%Y-%m-%d'),
            ('datetime', 0),
            ('time', -1),
            ('open', 2),
            ('high', -1),
            ('low', -1),
            ('close', 1),
            ('volume', -1),
            ('openinterest', 3),
        )

    # Create a Stratey
    class TestStrategy(bt.Strategy):
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
            self.dataopen = self.datas[0].open
            self.openinterest = self.datas[0].openinterest

            # To keep track of pending orders
            self.order = None
            self.target = 0

        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                return

            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():
                    #self.log('BUY EXECUTED, %.2f' % order.executed.price)
                    self.ExecText = 'BUY EXECUT,  '
                elif order.issell():
                    #self.log('SELL EXECUTED, %.2f' % order.executed.price)
                    self.ExecText = 'SEL EXECUT,  '

                self.bar_executed = len(self)

            elif order.status in [
                    order.Canceled, order.Margin, order.Rejected
            ]:
                #self.log('Order Canceled/Margin/Rejected')
                self.ExecText = 'ORD CANCAL,  '

            # Write down: no pending order
            self.order = None

        def next(self):
            # Simply log the closing price of the series from the reference
            PriceText = ', Open: %.2f, Close: %.2f, Signal: %.0f => ' % (
                self.dataopen[0], self.dataclose[0], self.openinterest[0])
            OperText = '             '
            CashValue = 'Cash: %.2f, ' % cerebro.broker.getcash()
            PortoValue = 'Porto: %.2f' % cerebro.broker.getvalue()
            dt = self.datas[0].datetime.date(0)

            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                return

            # Check if we are in the market
            if self.openinterest[0] == 1:

                if self.position.size <= 0:

                    OperText = 'BUY CREATE,  '

                    # Keep track of the created order to avoid a 2nd order
                    self.target = cerebro.broker.getvalue(
                    ) * 0.95 / self.dataopen
                    self.order = self.order_target_size(target=self.target)

            if self.openinterest[0] == -1:

                if self.position.size >= 0:

                    OperText = 'SEL CREATE,  '

                    # Keep track of the created order to avoid a 2nd order
                    self.target = -cerebro.broker.getvalue(
                    ) * 0.95 / self.dataopen
                    self.order = self.order_target_size(target=self.target)

            if self.openinterest[0] == 0:

                if self.position.size != 0:

                    OperText = 'EMPTY CREATE,  '

                    # Keep track of the created order to avoid a 2nd order
                    self.target = 0
                    self.order = self.order_target_size(target=self.target)

            # Print the line
            LineText = str(dt.isoformat()) + str(PriceText) + str(
                OperText) + str(CashValue) + str(PortoValue)
            PrintLog(logfile, 'a', LineText)
            #log('%s, %s' % (self.dt.isoformat(), LineText))

    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    cerebro.addstrategy(TestStrategy)

    # Datas are in a subfolder of the samples. Need to find where the script is
    # because it could have been called from anywhere
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, sigfile)

    # Create a Data Feed
    data = MyCSVData(dataname=datapath, fromdate=fromdate)

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # Set our desired cash start
    cerebro.broker.setcash(10000.0)

    # Set the commission - 0.1% ... divide by 100 to remove the %
    cerebro.broker.setcommission(commission=0.001)

    # Print out the starting conditions
    LineText = ('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    PrintLog(logfile, 'w', LineText)
    print(LineText)

    # Run over everything
    cerebro.run()

    # Print out the final result
    FinalValue = cerebro.broker.getvalue()
    LineText = ('Final Portfolio Value: %.2f' % FinalValue)
    PrintLog(logfile, 'a', LineText)
    print(LineText)

    # Plot the result
    figure = cerebro.plot(volume=False)[0][0]
    figure.savefig(figfile)

    return (FinalValue)
def main():
    ##################################################################################################
    # Setup logger and output dir                                                                    #
    ##################################################################################################
    output_dir = config.output_dir

    if output_dir is None:
        output_dir = './jupyter_py/output/backtest-{}'.format(
            get_current_time())
    if not os.path.exists(output_dir):
        pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)

    # Setup logger
    LogHelper.setup(log_path='{}/backtesting.log'.format(output_dir),
                    log_level=logging.INFO)
    _logger = logging.getLogger(__name__)

    # Log all paremeters
    _logger.info("Backtest parameters: {}".format(vars(config)))

    # load data
    data = GSTools.load_csv_files(config.data_path)
    stk0, stk1 = config.stk0, config.stk1

    # check existence of stocks
    if (stk0 not in data):
        _logger.error("Stock symbol {} does not exist!".format(stk0))
        return
    if (stk1 not in data):
        _logger.error("Stock symbol {} does not exist!".format(stk1))
        return

    # size requirements
    pre_backtest_size = None
    if config.strategy_type == "cointegration" or config.strategy_type == "distance":
        pre_backtest_size = config.lookback
    elif config.strategy_type == "kalman":
        pre_backtest_size = config.kalman_estimation_length

    # select segment of data that we want
    data0, data1 = data[stk0].set_index("date"), data[stk1].set_index("date")
    start_date_dt = datetime.strptime(config.backtest_start, "%Y-%m-%d").date()
    end_date_dt = datetime.strptime(config.backtest_end, "%Y-%m-%d").date()

    data0 = data0[:start_date_dt].tail(pre_backtest_size).append(
        data0[start_date_dt:end_date_dt])
    data1 = data1[:start_date_dt].tail(pre_backtest_size).append(
        data1[start_date_dt:end_date_dt])
    data0 = data0.reset_index()
    data1 = data1.reset_index()

    # initialize cerebro
    cerebro = bt.Cerebro()

    # Create data feeds
    data0 = bt.feeds.PandasData(dataname=data0,
                                timeframe=(bt.TimeFrame.Days),
                                datetime=0,
                                open=1,
                                close=4)
    data1 = bt.feeds.PandasData(dataname=data1,
                                timeframe=(bt.TimeFrame.Days),
                                datetime=0,
                                open=1,
                                close=4)

    # add data feeds to cerebro
    cerebro.adddata(data0)
    cerebro.adddata(data1)

    # Add the strategy
    if config.strategy_type == "distance":
        cerebro.addstrategy(DistStrategy,
                            stk0_symbol=stk0,
                            stk1_symbol=stk1,
                            lookback=config.lookback,
                            max_lookback=pre_backtest_size,
                            enter_threshold_size=config.enter_threshold,
                            exit_threshold_size=config.exit_threshold,
                            loss_limit=config.loss_limit,
                            consider_borrow_cost=True,
                            consider_commission=False,
                            print_msg=True,
                            print_transaction=True)
    elif config.strategy_type == "cointegration":
        cerebro.addstrategy(CointStrategy,
                            stk0_symbol=stk0,
                            stk1_symbol=stk1,
                            lookback=config.lookback,
                            max_lookback=pre_backtest_size,
                            enter_threshold_size=config.enter_threshold,
                            exit_threshold_size=config.exit_threshold,
                            loss_limit=config.loss_limit,
                            consider_borrow_cost=True,
                            consider_commission=True,
                            print_msg=True,
                            print_transaction=True)
    elif config.strategy_type == "kalman":
        cerebro.addstrategy(CointKalmanStrategy,
                            stk0_symbol=stk0,
                            stk1_symbol=stk1,
                            max_lookback=pre_backtest_size,
                            enter_threshold_size=config.enter_threshold,
                            exit_threshold_size=config.exit_threshold,
                            loss_limit=config.loss_limit,
                            consider_borrow_cost=True,
                            consider_commission=True,
                            print_msg=True,
                            print_transaction=True)
    # Add analyzers
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')
    cerebro.addanalyzer(Metrics, lookback=pre_backtest_size, _name='metrics')

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcash(1000000)

    # And run it
    strat = cerebro.run()

    # get MICRO metrics
    results_dict = {}
    results_dict["pair"] = stk0 + "-" + stk1
    results_dict["sharperatio"] = strat[0].analyzers.mysharpe.get_analysis(
    )['sharperatio']
    results_dict["returnstd"] = strat[0].analyzers.metrics.returns_std()
    results_dict["avg_holding_period"] = strat[
        0].analyzers.metrics.avg_holding_period
    results_dict["n_trades"] = strat[0].analyzers.metrics.n_trades
    results_dict["startcash"] = cerebro.getbroker().startingcash
    results_dict["endcash"] = cerebro.getbroker().getvalue()
    results_dict["profit"] = (
        results_dict["endcash"] -
        results_dict["startcash"]) / results_dict["startcash"]
    _logger.info("[pair-performance]: {}".format(results_dict))
                size = int(self.broker.getcash() / self.datas[0].open)
                self.log(
                    f'BUY CREATED --- Size: {size}, Cash:{self.broker.getcash(): .2f},'
                    f' Open: {self.data_open[0]}, Close: {self.data_close[0]}')
                self.buy(size=size)
        else:
            if self.sell_signal < 0:
                self.log(f'SELL CREATED --- Size: {self.position.size}')
                self.sell(size=self.position.size)


data = bt.feeds.YahooFinanceCSVData(dataname='AAPL.csv',
                                    fromdate=datetime(2017, 1, 1),
                                    todate=datetime(2020, 4, 30))

cerebro = bt.Cerebro(stdstats=False, cheat_on_open=True)
cerebro.addstrategy(BBand_Strategy)
cerebro.adddata(data)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.001)
cerebro.addobserver(bt.observers.BuySell)
cerebro.addobserver(bt.observers.Value)
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='time_return')

print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
backtest_result = cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

cerebro.plot(style='candlestick', iplot=True, volume=False)
print(backtest_result[0].analyzers.returns.get_analysis())
Example #9
0
def main():
    # Model Settings
    startcash = 500000
    momentum_period = 126  #days
    num_positions = 20
    reserve = 0.05
    printlog = False

    # Commission and Slippage Settings
    commission = 0.0025

    db_conn = get_db_conn(p_uid='freedbtech_tradesoft',
                          p_pwd='tradesoft',
                          p_host='freedb.tech',
                          p_database='freedbtech_tradesoft',
                          p_port=3306)

    prices = historical_price(tickers, db_conn)
    db_conn.close()

    # remove tickers where we have less than 10 years of data.
    min_obs = 2520
    nobs = prices.groupby(level='ticker').size()
    keep = nobs[nobs > min_obs].index
    prices = prices.loc[idx[keep, :], :]
    #    prices.info()

    print('Number of tickers with min 10 years history= ',
          prices.index.unique(level='ticker'))
    print('latest Date: ', prices.loc['ACC'].index[-1])
    print('latest Date - 126 Trading days: ', prices.loc['ACC'].index[-126])

    from_date = input('start date in format yyyy-mm-dd:')
    to_date = input('end date in format yyyy-mm-dd:')

    fromdate = datetime.datetime.strptime(from_date, '%Y-%m-%d')
    todate = datetime.datetime.strptime(to_date, '%Y-%m-%d')

    cerebro = bt.Cerebro(stdstats=False, cheat_on_open=True)
    #    cerebro.broker.set_coc(True)
    cerebro.broker.setcash(startcash)
    cerebro.broker.setcommission(commission=commission)

    # Add securities as datas1:
    for ticker, data in prices.groupby(level=0):
        if ticker in tickers:
            print(f"Adding ticker: {ticker}")
            data = bt.feeds.PandasData(dataname=data.droplevel(level=0),
                                       name=str(ticker),
                                       fromdate=fromdate,
                                       todate=todate,
                                       plot=False)
            cerebro.adddata(data)

    cerebro.addanalyzer(bt.analyzers.Returns, _name='pfreturn')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='pfdrawdown')
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='pfsharpe')
    cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')

    cerebro.addstrategy(StrategyRiskparity,
                        momentum_period=momentum_period,
                        num_positions=num_positions,
                        printlog=printlog,
                        reserve=reserve)

    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    # Run the strategy. Results will be output from stop.
    results_eq_wts = cerebro.run()
    results_eq_wt = results_eq_wts[0]

    pyfoliozer = results_eq_wt.analyzers.getbyname('pyfolio')
    returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()

    transactions.to_csv('data/transactions.csv')
    positions.to_csv('data/positions.csv')
    returns.to_csv('data/returns.csv')

    # Print out the return
    print('\nPortfolio Return:',
          results_eq_wt.analyzers.pfreturn.get_analysis())

    # Print out the drawdown
    print('\nPortfolio Drawdown:',
          results_eq_wt.analyzers.pfdrawdown.get_analysis())

    # Print out the sharpe
    print('\nPortfolio Sharpe ratio:',
          results_eq_wt.analyzers.pfsharpe.get_analysis())

    # Print out the final result
    print('\nFinal Portfolio Value: %.2f' % cerebro.broker.getvalue())
Example #10
0
import backtrader
import datetime
from strategy import TestStrategy

cerebro = backtrader.Cerebro()

cerebro.addsizer(backtrader.sizers.FixedSize, stake=1000)

cerebro.broker.set_cash(1000000)
data = backtrader.feeds.YahooFinanceCSVData(
    dataname='oracle.csv',
    # Do not pass values before this date
    fromdate=datetime.datetime(2000, 1, 1),
    # Do not pass values after this date
    todate=datetime.datetime(2000, 12, 31),
    reverse=False)

cerebro.adddata(data)

cerebro.addstrategy(TestStrategy)

print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

cerebro.run()

print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

cerebro.plot()
Example #11
0
def backtest(cfg, strategy, plot=False, m_name='Test', **kwargs):
    
    def getReturnAsDataFrame(pfa, bencha):
        d = bencha.get_analysis()
        t = pfa.get_analysis()
        lists = sorted(d.items()) # sorted by key, return a list of tuples
        x, y = zip(*lists) # unpack a list of pairs into two tuples
        listst = sorted(t.items()) # sorted by key, return a list of tuples
        x, y1 = zip(*listst) # unpack a list of pairs into two tuples
        dd = {'data':x, 'Benchmark':np.cumsum(y), 'Portfolio':np.cumsum(y1)}
        df = pd.DataFrame(dd).set_index('data')
        return df

    cerebro = bt.Cerebro()
    
    if 'set_coc' in cfg:
        cerebro.broker.set_coc(cfg['set_coc'])
    if 'set_coo' in cfg:
        cerebro.broker.set_coo(cfg['set_coo'])
        
    cerebro.addstrategy(strategy, **kwargs)

    for a in cfg['assets']:
        if cfg['stocks_df'] is None:
            cerebro.adddata(bt.feeds.YahooFinanceData(dataname=a, fromdate=cfg['startd'], todate=cfg['endd'], plot=False))
        else:
            cerebro.adddata(PandasData(dataname=getStock(cfg['stocks_df'],a)), name=a)

        
        
    #add benchmark 
    if 'benchmark' in cfg:
        if (cfg['benchmark_df'] is None) and (cfg['stocks_df'] is None):
            bm = bt.feeds.YahooFinanceData(dataname=cfg['benchmark'], fromdate=cfg['startd'], todate=cfg['endd'])
        else:
            bm = PandasData(dataname=cfg['benchmark_df'],name=a)

        cerebro.adddata(bm)
        
        cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Months, data=bm, _name='BenchmarkReturns')
        cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Months, _name='PortfolioReturns')

    # Analyzer
    cerebro.addanalyzer(btanalyzers.SharpeRatio, riskfreerate=0.0, timeframe=bt.TimeFrame.Months, _name='mysharpe')
    cerebro.addanalyzer(btanalyzers.Returns, _name='myreturn')
    cerebro.addanalyzer(btanalyzers.DrawDown, _name='mydrawdown')
    cerebro.addanalyzer(AllocationHistory, _name='allocationHistory')
    
    cerebro.broker.setcash(cfg['cash'])
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    thestrats = cerebro.run()
    thestrat = thestrats[0]
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    if plot:
        cerebro.plot()
    
    df = pd.DataFrame()
    if 'benchmark' in cfg:
        df = getReturnAsDataFrame(thestrat.analyzers.getbyname('PortfolioReturns'), 
                                  thestrat.analyzers.getbyname('BenchmarkReturns'))
    
    ret = {
        m_name : { 
        'Max_Drawdown':thestrat.analyzers.getbyname('mydrawdown').get_analysis()['max']['drawdown'],
        'CAGR':thestrat.analyzers.getbyname('myreturn').get_analysis()['rnorm100'],
        'Sharp_Ratio':thestrat.analyzers.getbyname('mysharpe').get_analysis()['sharperatio'],
        'Value': cerebro.broker.getvalue(),
        }
    }

    allocation_history = thestrat.analyzers.getbyname('allocationHistory').get_analysis()['allocation']
    return (pd.DataFrame.from_dict(ret).T,
            cleanWarmUperiod(df), 
            allocation_history)
Example #12
0
def runstrat_main(settings, **kwargs):
    # clock the start of the process
    tstart = process_time()

    args = parse_args(kwargs)
    # signal = list(kwargs["signal"].keys())[0]

    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False,        # default kwarg: stdstats=True
                         # maxcpus=args.maxcpus,
                         # runonce=not args.no_runonce,
                         # exactbars=args.exactbars,
                         # optdatas=not args.no_optdatas,
                         # optreturn=not args.no_optreturn
                         )

    # Add a strategy
    cerebro.addstrategy(strategies.MainStrategy, **settings, **kwargs)

    # Get a data source path
    datapath = args.data

    # Pass it to the backtrader datafeed and add it to the cerebro
    data = bt.feeds.PandasData(dataname=pandasdatafeed(datapath, args=args),
                               fromdate=args.fromdate,  # fromdate=args.fromdate,
                               todate=args.todate)  # todate=args.todate)
    cerebro.adddata(data)

    # Set our desired cash start
    cerebro.broker.setcash(args.cash)   # cerebro.broker.setcash(100000.0)

    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=1)

    # Set the commission
    cerebro.broker.setcommission(commission=0.0)

    # Set Observer
    cerebro.addobserver(bt.observers.Broker)
    cerebro.addobserver(bt.observers.Trades)
    cerebro.addobserver(bt.observers.BuySell, barplot=True, bardist=0.0025)
    cerebro.addobserver(bt.observers.DrawDown)

    # Set Analyzer
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='tradeanalyzer')  # muito completo
    # cerebro.addanalyzer(bt.analyzers.TimeDrawDown, _name='timedrawdown')
    cerebro.addanalyzer(bt.analyzers.VWR, _name='vwr')  # VariabilityWeightedReturn
    cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')  # SystemQualityNumber.

    # Writer
    # cerebro.addwriter(bt.WriterFile, csv=True, out='./logs/log.csv')
    # cerebro.addwriter(bt.WriterFile, csv=args.writercsv, out='./logs/log.csv')

    # Print out the starting conditions
    # print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

    # Run over everything
    results = cerebro.run()

    # # Extract/save analyzers
    # analyzers_log(settings, results)

    # clock the end of the process
    tend = process_time()

    # print out the result
    print('Time used:', str(tend - tstart))

    # # Plot the result
    # if args.plot:
    #     pkwargs = dict(style='bar')
    #     # pkwargs = dict(style='ohlc')
    #     if args.plot is not True:  # evals to True but is not True
    #         npkwargs = eval('dict(' + args.plot + ')')  # args were passed
    #         pkwargs.update(npkwargs)
    #     cerebro.plot(**pkwargs)
    # # cerebro.plot(style='bar', bardist=0)

    # Print out the final result
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
            else:
                pass
        else:
            pass

    def stop(self):
        print('%.2f,%.2f,%.2f,%.2f,,%2d,%2d,%.2f' %
              (self.params.k, self.params.differ, self.params.rangeDays,
               self.tradeCount, self.winCount, self.loseCount,
               self.broker.getvalue()))


if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro(maxcpus=4, optreturn=False, stdstats=False)

    strats = cerebro.optstrategy(dtStrategyV12,
                                 ordersize=1,
                                 k=range(4, 7, 1),
                                 differ=range(0, 1, 1),
                                 rangeDays=range(2, 12, 1))
    # Set the commission
    cerebro.broker.setcommission(leverage=1, mult=5, commission=0.01)
    cerebro.addsizer(maxRiskSizer)
    #cerebro.broker.setcommission(commission=0.0)
    # Add a strategy
    #cerebro.addstrategy(DTStrategy01)
    #cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
    #cerebro.addanalyzer(btanalyzers.AnnualReturn, _name='annual')
def run_strat(**kwargs):

    # Instatiates backtester
    cerebro = bt.Cerebro()

    # Set Cash & Commission
    cerebro.broker.setcash(10000)
    cerebro.broker.setcommission(commission=0.005)

    # Allow fractional shares
    cerebro.broker.addcommissioninfo(CommInfoFractional())

    # Adds DataFeeds for universe to the backtester
    if kwargs["data_set"] == "testing":
        from_dt = datetime.datetime(2020, 11, 1) - datetime.timedelta(40)
        to_dt = datetime.datetime(2021, 2, 8)
    else:
        from_dt = datetime.datetime(2019, 8, 19)
        to_dt = datetime.datetime(2020, 10, 31)

    dataPath = Path(__file__).parent / "data/*.csv"
    for file in glob.glob(str(dataPath)):
        data = btfeeds.GenericCSVData(
            dataname=file,
            fromdate=from_dt,
            todate=to_dt,
            datetime=0,
            open=1,
            high=2,
            low=3,
            close=4,
            volume=5,
            openinterest=-1,
            time=-1,
            dtformat=("%Y-%m-%d"),
        )
        cerebro.adddata(data)

    # cerebro.optstrategy(
    #     PairTrading,
    #     trigger=map(lambda x: x / 10, range(10, 40, 5)),
    #     period=range(10, 55, 5),
    #     sell_mul=map(lambda x: x / 10, range(1, 11, 1)),
    #     printout=kwargs["show_log"],
    # )
    cerebro.addstrategy(PairTrading,
                        sell_mul=0.2,
                        trigger=3,
                        period=30,
                        printout=kwargs["show_log"])

    if kwargs["show_pyfolio"]:
        cerebro.addanalyzer(bt.analyzers.PyFolio, _name="pyfolio")

    results = cerebro.run()

    if kwargs["show_plot"]:
        cerebro.plot()

    if kwargs["show_pyfolio"]:
        pyfoliozer = results[0].analyzers.getbyname("pyfolio")
        returns, positions, transactions, _ = pyfoliozer.get_pf_items()
        returns.to_pickle(
            Path(__file__).parent / "results" / kwargs["data_set"] /
            "returns.pickle")
        positions.to_pickle(
            Path(__file__).parent / "results" / kwargs["data_set"] /
            "positions.pickle")
        transactions.to_pickle(
            Path(__file__).parent / "results" / kwargs["data_set"] /
            "transactions.pickle")
        target = self.broker.getvalue(
        ) * self.params.prop  # Ideal total value of the position
        price = data.close[0]
        shares = int(target / price)
        if shares * price > cash:
            return 0  # Not enough money for this trade
        else:
            return shares


if __name__ == '__main__':
    start = datetime.datetime(2017, 8, 1)
    end = datetime.datetime(2020, 4, 30)
    is_first = True
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False, optreturn=False)
    symbols = [
        'adaniports_15min.csv', 'asianpaint_15min.csv', 'axisbank_15min.csv',
        'bajajfinsv_15min.csv', 'bajaj_auto_15min.csv', 'bajfinance_15min.csv',
        'bhartiartl_15min.csv', 'bpcl_15min.csv', 'britannia_15min.csv',
        'cipla_15min.csv', 'coalindia_15min.csv', 'drreddy_15min.csv',
        'eichermot_15min.csv', 'gail_15min.csv', 'grasim_15min.csv',
        'hcltech_15min.csv', 'hdfcbank_15min.csv', 'hdfc_15min.csv',
        'heromotoco_15min.csv', 'hindalco_15min.csv', 'hindunilvr_15min.csv',
        'icicibank_15min.csv', 'indusindbk_15min.csv', 'infratel_15min.csv',
        'infy_15min.csv', 'ioc_15min.csv', 'itc_15min.csv',
        'jswsteel_15min.csv', 'kotakbank_15min.csv', 'lt_15min.csv',
        'maruti_15min.csv', 'mm_15min.csv', 'nestleind_15min.csv',
        'ntpc_15min.csv', 'ongc_15min.csv', 'powergrid_15min.csv',
        'reliance_15min.csv', 'sbin_15min.csv', 'sunpharma_15min.csv',
        'tatamotors_15min.csv', 'tatasteel_15min.csv', 'tcs_15min.csv',
def runstrat(pargs=None):
    args = parse_args(pargs)

    # Create a cerebro
    cerebro = bt.Cerebro()

    if args.cash is not None:
        cerebro.broker.set_cash(args.cash)

    # Get the dates from the args
    fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
    todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

    # Create the 1st data
    data = bt.feeds.BacktraderCSVData(
        dataname=args.data,
        fromdate=fromdate,
        todate=todate)

    cerebro.adddata(data)  # Add the data to cerebro

    # Add the strategy
    cerebro.addstrategy(bt.strategies.SMA_CrossOver)

    tframes = dict(
        days=bt.TimeFrame.Days,
        weeks=bt.TimeFrame.Weeks,
        months=bt.TimeFrame.Months,
        years=bt.TimeFrame.Years)

    # Add the Analyzers
    cerebro.addanalyzer(bt.analyzers.TimeReturn,
                        timeframe=tframes[args.tframe])

    shkwargs = dict()
    if args.annualize:
        shkwargs['annualize'] = True

    if args.riskfreerate is not None:
        shkwargs['riskfreerate'] = args.riskfreerate

    if args.factor is not None:
        shkwargs['factor'] = args.factor

    if args.stddev_sample:
        shkwargs['stddev_sample'] = True

    if args.no_convertrate:
        shkwargs['convertrate'] = False

    cerebro.addanalyzer(bt.analyzers.SharpeRatio,
                        timeframe=tframes[args.tframe],
                        **shkwargs)

    # Add a writer to get output
    cerebro.addwriter(bt.WriterFile, csv=args.writercsv, rounding=4)

    cerebro.run()  # And run it

    # Plot if requested
    if args.plot:
        pkwargs = dict(style='bar')
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)

        cerebro.plot(**pkwargs)
def runstrat():
	
	#Create an instance of cerebro
	cerebro = bt.Cerebro(exactbars=-1)  #exactbars True reduces memory usage significantly, but change to '-1' for partial memory savings (keeping indicators in memory) or 'false' to turn off completely if having trouble accessing bars beyond max indicator paramaters.  
	cerebro.broker.set_coc(False)    #cheat on close allows you to buy the close price of the current bar in which order was made.  cheat on open allows you to simulate a market order on the open price of the next bar
	cerebro.broker.set_coo(False)    #cheat on open allows you to buy the close price of the current bar in which order was made.  cheat on open allows you to simulate a market order on the open price of the next bar
	cerebro.broker.set_shortcash(True) #False means decrease cash available when you short, True means increase it
	
	#Add our strategy
	cerebro.addstrategy(Strategy)
	
	#Create/Instantiate objects to access parameters from UserInput Class
	#Can NOT create object referencing Strategy class as per backtrader
	modelp = UserInputs.model_params()
	datalist = UserInputs.datalist('hist')
	ibdatalist = UserInputs.datalist('ib')
	
	#Ensure stock lists have no duplicates - duplicates will BREAK program
	if len(datalist) != len(set(datalist)) or len(ibdatalist) != len(set(ibdatalist)):
		print("*****You have duplicates in stock list - FIX LIST*****")
	
	#GET THE DATA
	session_start = modelp.get('sessionstart')
	session_end = modelp.get('sessionend')	
	
	if modelp.get('live_status'):
		#*****  LIVE TRADING PARAMETERS*******
		store = bt.stores.IBStore(host='127.0.0.1',
								port=7497,
								clientId = 100)
		
		#get number of tickers
		indicator_dict = UserInputs.ind_params()
		max_ind = max(indicator_dict.values()) 
							
		#Data set for live trading IB
		for i,j in enumerate(ibdatalist):
			
			#Data for live IB trading
			data = store.getdata(dataname=j,
								timeframe=bt.TimeFrame.Minutes,
								tz = pytz.timezone('US/Central'),
								#historical = True, 
								backfill_start = True,  #true enables maximum allowable backfill in single request
								useRTH = True, 
								rtbar=True,
								fromdate = UserInputs.ib_backfill_start(UserInputs.max_ind()),#from date determined by today - max period paramater 
								sessionstart = session_start,
								sessionend = session_end,
								notifyall=True,
								qcheck=2.0,
								debug=True)
			
			cerebro.resampledata(data, name="{}0".format(j),timeframe=bt.TimeFrame.Minutes, compression=modelp.get('timeframe0'))
			
			#Apply resamplings
			if modelp.get('timeframe1on'):
				data_Timeframe1 = cerebro.resampledata(data,name="{}1".format(j),
														timeframe=bt.TimeFrame.Minutes,
														compression = modelp.get('timeframe1'))
			
			if modelp.get('timeframe2on'):
				data_Timeframe2 = cerebro.resampledata(data,name="{}2".format(j),
														timeframe=bt.TimeFrame.Minutes,
														compression = modelp.get('timeframe2'))
										
			cerebro.broker = store.getbroker()
			
		
	elif not modelp.get('live_status'):
		#******BACTESTING ONLY - MYSQL DATABASE*********
		
		#mysql configuration items for connection
		host = '127.0.0.1'
		user = '******'
		password = '******'
		database = 'Stock_Prices'
		table = '5_min_prices'
		
		#Determine data range to run
		start_date = modelp.get('start_date')
		end_date = modelp.get('end_date')
	
		for i,j in enumerate(datalist):
			#Get data from mysql and add data to Cerebro
			data = mysql.MySQLData(dbHost = host,
									dbUser = user,
									dbPWD = password,
									dbName = database,
									table = table,
									symbol = j,
									fromdate = start_date,
									todate= end_date,
									sessionstart = session_start,
									sessionend = session_end,
									timeframe=bt.TimeFrame.Minutes,
									compression = modelp.get('timeframe0'),
									)
									
			data_BaseTimeframe = cerebro.adddata(data=data, 
												name="{}0".format(j),
												)
			data_BaseTimeframe.csv=True #Report this data to csv file (true) or not (false)	
			#data_BaseTimeframe.plotinfo.plot = False
			
			if modelp.get('timeframe1on'):
				#Apply resamplings			
				data_Timeframe1 = cerebro.resampledata(data,
										name="{}1".format(j),
										timeframe=bt.TimeFrame.Minutes,
										compression = modelp.get('timeframe1'),
										)
				data_Timeframe1.csv=False #Report this data to csv file (true) or not (false)				
				#data_Timeframe1.plotinfo.plot = False
				#data_Timeframe1.plotinfo.plotmaster = data_BaseTimeframe
	
			if modelp.get('timeframe2on'):
				data_Timeframe2 = cerebro.resampledata(data,
										name="{}2".format(j),
										timeframe=bt.TimeFrame.Minutes,
										compression = modelp.get('timeframe2'),
										)
				data_Timeframe2.csv=False #Report this data to csv file (true) or not (false)	
				#data_Timeframe2.plotinfo.plot = False
				#data_Timeframe2.plotinfo.plotmaster = data_BaseTimeframe
			
		# Set our desired cash start
		cerebro.broker.setcash(modelp.get('start_cash'))
		
		# Set the commission
		cerebro.broker.setcommission(commission=0.00003,
									margin= None,
									mult=1.0,
									commtype=None,
									percabs=True,
									stocklike=True,
									leverage=1)
		
		"""
		#Set the slippage
		cerebro.broker.set_slippage_perc(0.001,
										slip_open=True, 
										slip_limit=True,
										slip_match=True,
										slip_out=False)
		"""
		
	# Add SQN to qualify the trades (rating to analyze quality of trading system: 2.5-3 good, above 3 excellent.  SquareRoot(NumberTrades) * Average(TradesProfit) / StdDev(TradesProfit).  Need at least 30 trades to be reliable
	cerebro.addanalyzer(bt.analyzers.SQN)
	
	#Add Sharpe Ratio - configured for daily returns
	cerebro.addanalyzer(bt.analyzers.SharpeRatio)
	
	#Adding my own custom analyzer - when creating analyzer, make sure to include file in _init_.py within backtrader.analyzer folder so it runs
	cerebro.addanalyzer(bt.analyzers.AcctStats)
	
	#Adding analyzer for drawdown
	cerebro.addanalyzer(bt.analyzers.DrawDown)
	
	# Add TradeAnalyzer to output trade statistics - THESE ARE THE TRADE NOTIFICATIONS THAT ARE PRINTED WHEN PROGRAM IS RUN
	cerebro.addanalyzer(bt.analyzers.Transactions)
	
	#Adds just buy/sell observer to chart (assuming stdstats is set to false)
	cerebro.addobservermulti(bt.observers.BuySell,)
	
	#Adds custom observers
	cerebro.addobserver(bt.observers.AcctValue) #reports trade statistics in command prompt at end of program run
	cerebro.addobserver(bt.observers.OrderObserver) #reports trades in command prompt when program is run
	
	#Generate output report in csv format
	if UserInputs.model_params().get('writer')=='on':
		current_time = datetime.now().strftime("%Y-%m-%d_%H.%M.%S.csv") 
		csv_file = 'C:/Program Files (x86)/Python36-32/Lib/site-packages/backtrader/out/'
		csv_file += 'Strategy'
		csv_file += current_time
		cerebro.addwriter(bt.WriterFile, csv = True, out=csv_file)
		print("Writer CSV Report On")

	
	#RUN EVERYTHING
	results = cerebro.run(stdstats=False, #enables some additional chart information like profit/loss, buy/sell, etc, but tends to clutter chart
						runonce=True,
						)
	
	if not modelp.get('live_status'):
	
		#Print analyzers
		for alyzer in results[0].analyzers:
			alyzer.print()
		
		#Calculate Program end time
		end_time = datetime.now().time()
		print('Program end at {}'.format(end_time))
		
		#PLOT TRADING RESULTS - **Ensure trading session ends at 2:55 (if it ends at 3, some tickers only go up to 2:55, creating data length mismatches between tickers that doesn't allow plotting)
		plot_end = modelp.get('end_date')-timedelta(days=1) #Hack to prevent errors with plotting, specifically x and y shape different error
		
		#Chart 5 minute timeframes only, 1 by 1
		
		for i in range (0,len(results[0].datas),3):
			for j, d in enumerate(results[0].datas):
				d.plotinfo.plot = i ==j
			
			cerebro.plot(barup='olive', bardown='lightpink',volup = 'lightgreen',voldown='crimson')
Example #18
0
def runstrategy(ticker_list, bench_ticker):
    args = parse_args()
    print(args)

    # Create a cerebro
    cerebro = bt.Cerebro()

    # Get the dates from the args
    fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
    todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

    # bench = bt.feeds.YahooFinanceData(
    #     dataname=bench_ticker,
    #     fromdate=fromdate,
    #     todate=todate,
    #     buffered=True,plot = False
    # )

    bench = bt.feeds.GenericCSVData(
        dataname='/Users/joan/PycharmProjects/CSV_DB/IB/' + bench_ticker +
        '.csv',
        fromdate=fromdate,
        todate=todate,
        nullvalue=0.0,
        dtformat=('%Y%m%d'),
        datetime=1,
        open=2,
        high=3,
        low=4,
        close=5,
        volume=6,
        reverse=False,
        plot=False)

    cerebro.adddata(bench, name=bench_ticker)

    for i in ticker_list:
        print('Loading data: ' + i)
        # data = bt.feeds.YahooFinanceData(
        #     dataname=i,
        #     fromdate=fromdate,
        #     todate=todate,
        #     adjclose=True,
        #     buffered=True, plot = False
        #     )

        data = bt.feeds.GenericCSVData(
            dataname='/Users/joan/PycharmProjects/CSV_DB/IB/' + i + '.csv',
            fromdate=fromdate,
            todate=todate,
            nullvalue=0.0,
            dtformat=('%Y%m%d'),
            datetime=1,
            open=2,
            high=3,
            low=4,
            close=5,
            volume=6,
            reverse=False,
            plot=False)

        cerebro.adddata(data, name=i)

    # Add the strategy
    cerebro.addstrategy(PairTradingStrategy,
                        period=args.period,
                        stake=args.stake)

    # cerebro.optstrategy(
    #         PairTradingStrategy,
    #         period=range(45, 75),
    #         )

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcash(args.cash)

    # Add the commission - only stocks like a for each operation
    # cerebro.broker.setcommission(commission=args.commperc)

    comminfo = FixedCommisionScheme()
    cerebro.broker.addcommissioninfo(comminfo)

    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe_ratio')

    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="ta")
    cerebro.addanalyzer(bt.analyzers.SQN, _name="sqn")
    cerebro.addanalyzer(bt.analyzers.SharpeRatio_A,
                        _name='myysharpe',
                        riskfreerate=args.rf_rate)
    cerebro.addanalyzer(bt.analyzers.PyFolio, _name='mypyf')
    cerebro.addanalyzer(bt.analyzers.TimeReturn,
                        timeframe=bt.TimeFrame.Days,
                        data=bench,
                        _name='benchreturns')

    cerebro.addobserver(bt.observers.Value)
    cerebro.addobserver(bt.observers.Benchmark, plot=False)
    cerebro.addobserver(bt.observers.DrawDown)

    # And run it
    strat = cerebro.run(runonce=not args.runnext,
                        preload=not args.nopreload,
                        oldsync=args.oldsync)
    #
    # strat = cerebro.optstrategy(
    #     PairTradingStrategy,
    #     period=range(45, 75),
    #     printlog=True)

    # Plot if requested
    if args.plot:
        cerebro.plot(style='candlestick',
                     barup='green',
                     bardown='red',
                     figsize=(100, 100))

    bench_returns = strat[0].analyzers.benchreturns.get_analysis()
    bench_df = pd.DataFrame.from_dict(bench_returns,
                                      orient='index',
                                      columns=['return'])
    return_df = pd.DataFrame.from_dict(
        strat[0].analyzers.mypyf.get_analysis()['returns'],
        orient='index',
        columns=['return'])

    # print('Sharpe Ratio(bt):', firstStrat.analyzers.myysharpe.get_analysis()['sharperatio'])
    # print('Sharpe Ratio:', empyrical.sharpe_ratio(return_df, risk_free=args.rf_rate / 252, period='daily')[0])
    # print('Sharpe Ratio Benchmark:', empyrical.sharpe_ratio(bench_df, risk_free=args.rf_rate / 252, period='daily')[0])
    # print('')
    #
    # print('Sortino Ratio:', empyrical.sortino_ratio(return_df, period='daily')[0])
    # print('Sortino Ratio Benchmark:', empyrical.sortino_ratio(bench_df, period='daily')[0])
    # print('')
    # print('VaR:', empyrical.value_at_risk(return_df) * 100, '%')
    # print('VaR Benchmark:', empyrical.value_at_risk(bench_df) * 100, '%')
    #
    # print('')
    #
    # print('Capture:', round(empyrical.capture(return_df, bench_df, period='daily')[0] * 100), '%')
    # print('')
    #
    # print('Max drawdown: ', round(empyrical.max_drawdown(return_df)[0] * 100), '%')
    # print('Max drawdown Benchmark: ', round(empyrical.max_drawdown(bench_df)[0] * 100), '%')
    #
    # print('')
    alpha, beta = empyrical.alpha_beta(return_df,
                                       bench_df,
                                       risk_free=args.rf_rate)
    # print('Beta: ', beta)
    # print('')
    # print('Annual return:', round(empyrical.annual_return(return_df)[0] * 100), '%')
    # print('Annual Vol:', round(empyrical.annual_volatility(return_df)[0] * 100), '%')
    # print('')
    # print('Annual return Benchmark:', round(empyrical.annual_return(bench_df)[0] * 100), '%')
    # print('Annual Vol Benchmark:', round(empyrical.annual_volatility(bench_df)[0] * 100), '%')
    # print('')

    printTradeAnalysis(strat[0].analyzers.ta.get_analysis())

    dic = {
        'SQN':
        printSQN(strat[0].analyzers.sqn.get_analysis()),
        'sharpe':
        empyrical.sharpe_ratio(return_df,
                               risk_free=args.rf_rate / 252,
                               period='daily')[0],
        'sharpe_bm':
        empyrical.sharpe_ratio(bench_df,
                               risk_free=args.rf_rate / 252,
                               period='daily')[0],
        'sortino':
        empyrical.sortino_ratio(bench_df, period='daily')[0],
        'sortino_bm':
        empyrical.sortino_ratio(bench_df, period='daily')[0],
        'VaR':
        empyrical.value_at_risk(return_df) * 100,
        'VaR_bm':
        empyrical.value_at_risk(bench_df) * 100,
        'capture':
        round(empyrical.capture(return_df, bench_df, period='daily')[0] * 100),
        'max_dd':
        round(empyrical.max_drawdown(return_df)[0] * 100, 2),
        'max_dd_bm':
        round(empyrical.max_drawdown(bench_df)[0] * 100, 2),
        'beta':
        beta,
        'return_annual':
        round(empyrical.annual_return(return_df)[0] * 100, 2),
        'return_annual_bm':
        round(empyrical.annual_volatility(return_df)[0] * 100, 2),
        'vol_annual':
        round(empyrical.annual_return(bench_df)[0] * 100, 2),
        'vol_annual_bm':
        round(empyrical.annual_volatility(bench_df)[0] * 100, 2)
    }

    df = pd.DataFrame(dic, index=[0])
    print(df)

    def calc_stats(df):
        df['perc_ret'] = (1 + df['return']).cumprod() - 1
        # print(df.tail())
        return df

    s = return_df.rolling(30).std()
    b = bench_df.rolling(30).std()

    # Get final portfolio Value
    portvalue = cerebro.broker.getvalue()

    # Print out the final result
    print('Final Portfolio Value: ${}'.format(round(portvalue, 2)),
          'PnL: ${}'.format(round(portvalue - args.cash, 2)),
          'PnL: {}%'.format(round(((portvalue / args.cash) - 1) * 100, 2)))

    # Finally plot the end results

    if args.plot:

        fig, axs = plt.subplots(2, sharex=True)
        fig.autofmt_xdate()

        axs[1].plot(s)
        axs[1].plot(b)

        axs[1].set_title('Drawdown')
        axs[1].legend(['Fund', 'Benchmark'])

        axs[0].set_title('Returns')
        axs[0].plot(calc_stats(return_df)['perc_ret'])
        axs[0].plot(calc_stats(bench_df)['perc_ret'])
        axs[0].legend(['Fund', 'Benchmark'])
        plt.show()
Example #19
0
def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    # data0 = bt.feeds.YahooFinanceCSVData(dataname=args.data0, **kwargs)
    # cerebro.adddata(data0, name='d0')

    # data1 = bt.feeds.YahooFinanceCSVData(dataname=args.data1, **kwargs)
    # data1.plotinfo.plotmaster = data0
    # cerebro.adddata(data1, name='d1')

    # data2 = bt.feeds.YahooFinanceCSVData(dataname=args.data2, **kwargs)
    # data2.plotinfo.plotmaster = data0
    # cerebro.adddata(data2, name='d2')

    # Futu data feed
    symble_list = ['US.ATHM', 'US.CMCM']  # 'US.RYB'
    quote_ctx = ft.OpenQuoteContext(host='127.0.0.1', port=11111)
    # _, prices = quote_ctx.get_history_kline(symble_list, start=args.fromdate, end=args.todate,
    #                                         fields=[ft.KL_FIELD.DATE_TIME, ft.KL_FIELD.OPEN, ft.KL_FIELD.HIGH,
    #                                                 ft.KL_FIELD.LOW, ft.KL_FIELD.CLOSE, ft.KL_FIELD.TRADE_VOL])
    _, prices = quote_ctx.get_multiple_history_kline(symble_list,
                                                     start=args.fromdate,
                                                     end=args.todate)

    # print(prices)
    for price in prices:
        name = price['code'][0]
        price = price.assign(Data=price.time_key.apply(
            lambda x: str(x)[0:10])).set_index('Data')
        price = price.drop(columns=['code', 'time_key'])
        price = price.rename(index=str,
                             columns={
                                 "open": "Open",
                                 "high": "High",
                                 "low": "Low",
                                 "close": "Close",
                                 "volume": "Volume"
                             })

        price['Volume'] = price['Volume'].astype('int64')
        price.index = price.index.astype('datetime64[ns]')

        # Pass it to the backtrader datafeed and add it to the cerebro
        data = bt.feeds.PandasData(
            dataname=price,
            #    datetime='Date',
            nocase=True,
        )

        cerebro.adddata(data, name=name)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))
    cerebro.broker.setcommission(commission=0.001)

    # Sizer
    # cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))
    cerebro.addsizer(TestSizer, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    args.plot = 'style="candle"'
    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))
                        self.lastEntryPrice = self.dataclose[0]
                    elif self.dataclose[0] > self.exitPrice:
                        self.close()
                    else:
                        pass

    def stop(self):
        print('%2d,%2d,%2d,%2d,%2d,%2d,%2d,%2d,%.2f' %
              (self.params.longIN, self.params.differIN, self.params.longExit,
               self.params.differExit, self.params.atrDays, self.tradeCount,
               self.winCount, self.loseCount, self.broker.getvalue()))


if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro(maxcpus=6)

    # Add a strategy
    #cerebro.addstrategy(TurtleStrategy01)

    cerebro.addstrategy(TurtleStrategy01)

    # Set the commission
    cerebro.broker.setcommission(leverage=1, mult=5, commission=0.01)
    cerebro.addsizer(maxRiskSizer)
    # Datas are in a subfolder of the samples. Need to find where the script is
    # because it could have been called from anywhere
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    #datapath = os.path.join(modpath, '../../datas/RBindex_10m.csv')
    datapath = os.path.join(modpath, '../../datas/PPindex.csv')
Example #21
0
        self.end_val = self.strategy.broker.get_value()

    def get_analysis(self):
        return {"start": self.start_val, "end": self.end_val,
                "growth": self.end_val - self.start_val, "return": self.end_val / self.start_val}

class AcctValue(bt.Observer):
    alias = ('Value',)
    lines = ('value',)

    plotinfo = {"plot": True, "subplot": True}

    def next(self):
        self.lines.value[0] = self._owner.broker.getvalue()    # Get today's account value (cash + stocks)

cerebro = bt.Cerebro(stdstats=False)    # I don't want the default plot objects

cerebro.broker.set_cash(1000000)    # Set our starting cash to $1,000,000
cerebro.broker.setcommission(0.02)

#-------------------------------------------------
start = datetime.datetime(2018, 1, 1)
end = datetime.datetime(2018, 3, 31)
#--------------------------------------------
import sys
sys.path.append('../lib/')
from makeData import *

start_test = '2018-01-01'
start_validation = '2018-01-01'
data_path   = '..\\dataBackTesting\\'
        self.end_val = self.strategy.broker.get_value()

    def get_analysis(self):
        return {"start": self.start_val, "end": self.end_val,
                "growth": self.end_val - self.start_val, "return": self.end_val / self.start_val}

start = dt.datetime(2010, 1, 1)
end = dt.datetime(2016, 10, 31)
# Different stocks from past posts because of different data source (no plot for NTDOY)
symbols = ["AAPL", "GOOG", "MSFT", "AMZN", "SNY", "VZ", "IBM", "HPQ", "QCOM", "NVDA"]
datafeeds = {s: web.DataReader(s, "yahoo", start, end) for s in symbols}
for df in datafeeds.values():
    df["OpenInterest"] = 0    # PandasData reader expects an OpenInterest column;
                              # not provided by Google and we don't use it so set to 0

cerebro = bt.Cerebro(stdstats=False)

plot_symbols = ["AAPL", "GOOG", "NVDA"]
is_first = True
#plot_symbols = []
for s, df in datafeeds.items():
    data = bt.feeds.PandasData(dataname=df, name=s)
    if s in plot_symbols:
        if is_first:
            data_main_plot = data
            is_first = False
        else:
            data.plotinfo.plotmaster = data_main_plot
    else:
        data.plotinfo.plot = False
    cerebro.adddata(data)    # Give the data to cerebro
def backtest(tickers, intra_freq, year, params_dict):
    log_file = open('trade_log.txt', "w")
    sys.stdout = log_file  #log output

    class ValueObserver(bt.Observer):
        lines = ('value', )

        plotinfo = dict(plot=True, subplot=True, plotlinelabels=True)

        plotlines = dict(
            #value=dict(marker='*', markersize=8.0, color='lime', fillstyle='full')
            value=dict(linewidth=1.5))

        def next(self):
            self.lines.value[
                0] = self._owner.dataclose_x - self._owner.params.m * self._owner.dataclose_y

    # Create a Stratey
    class PairStrategy(bt.Strategy):
        params = (
            ('m', round(float(params_dict['m']), 5)),  #slope
            ('b', round(float(params_dict['b']), 5)),  #intercept
            ('std', round(float(params_dict['std']), 5)),
            ('avg', params_dict['avg']),
            ('muti', params_dict['muti']),  # muti*std+avg
            ('size', params_dict['size']))

        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            print('%s, %s' % (dt.strftime('%Y-%m-%d %H:%M:%S'), txt))

        def __init__(self):
            # Keep a reference to the "close" line in the data
            #x is BP y is RDSA
            self.dataclose_x = self.datas[0].close
            self.dataclose_y = self.datas[1].close

            # To keep track of pending orders and buy price/commission
            self.order = None
            self.buyprice = None
            self.buycomm = None

        def notify_cashvalue(self, cash, value):
            self.log('Cash %s Value %s' % (cash, value))

        def notify_order(self, order):
            print(type(order), 'Is Buy ', order.isbuy())
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                return

            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.log(
                        'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price, order.executed.value,
                         order.executed.comm))

                    self.buyprice = order.executed.price
                    self.buycomm = order.executed.comm
                else:  # Sell
                    self.log(
                        'SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price, order.executed.value,
                         order.executed.comm))

                self.bar_executed = len(self)

            elif order.status in [
                    order.Canceled, order.Margin, order.Rejected
            ]:
                self.log('Order Canceled/Margin/Rejected')

            self.order = None

        def notify_trade(self, trade):
            if not trade.isclosed:
                return

            self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))

        def next(self):
            # Simply log the closing price of the series from the reference
            self.log('Close(%s), %.2f' % (tickers[0], self.dataclose_x[0]))
            self.log('Close(%s), %.2f' % (tickers[1], self.dataclose_y[0]))

            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                return

            # Check if we are in the market
            if not self.position:

                if ((
                    (self.dataclose_x[0] -
                     self.params.m * self.dataclose_y[0] - self.params.b) <=
                    (2 * self.params.std * self.params.muti + self.params.avg))
                        and
                    ((self.dataclose_x[0] -
                      self.params.m * self.dataclose_y[0] - self.params.b) >=
                     -(2 * self.params.std * self.params.muti +
                       self.params.avg))):
                    #limit loss, we will not trade unless  -(2*std + 0.5) <= (BP-m*RSDA-b) <= (2*std + 0.5)

                    # BUY
                    # BP-m*RSDA-b >1*std + 0.5
                    if ((self.dataclose_x[0] -
                         self.params.m * self.dataclose_y[0] - self.params.b) >
                        (self.params.std * self.params.muti +
                         self.params.avg)):
                        self.log('SELL PORTFOLIO,SELL{},BUY{}'.format(
                            self.dataclose_x[0], self.dataclose_y[0]))
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.sell(self.datas[0],
                                               size=self.params.size)
                        self.order = self.buy(self.datas[1],
                                              size=int(self.params.m *
                                                       self.params.size))

                    # BP-m*RSDA-b <-(1*std + 0.5)
                    elif ((self.dataclose_x[0] -
                           self.params.m * self.dataclose_y[0] - self.params.b)
                          < -(self.params.std * self.params.muti +
                              self.params.avg)):
                        self.log('BUY PORTFOLIO,BUY{},SELL{}'.format(
                            self.dataclose_x[0], self.dataclose_y[0]))
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.buy(self.datas[0],
                                              size=self.params.size)
                        self.order = self.sell(self.datas[1],
                                               size=int(self.params.m *
                                                        self.params.size))

            else:
                # this part is not exactly what I want but till now it is fine

                #if the price is too high we need to buy our portfolio back incase of risk(buy BP sell RDSA)
                if ((self.dataclose_x[0] -
                     self.params.m * self.dataclose_y[0] - self.params.b) >=
                    (2 * self.params.std * self.params.muti +
                     self.params.avg)):
                    self.log('CLOSE THE SELL LIMIT LOSS,BUY{},SELL{}'.format(
                        self.dataclose_x[0], self.dataclose_y[0]))
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.buy(self.datas[0], size=self.params.size)
                    self.order = self.sell(self.datas[1],
                                           size=int(self.params.m *
                                                    self.params.size))

                #if the price is too low we need to sell our portfolio back incase of risk(sell BP buy RDSA)
                elif ((self.dataclose_x[0] -
                       self.params.m * self.dataclose_y[0] - self.params.b) <=
                      -(2 * self.params.std * self.params.muti +
                        self.params.avg)):
                    self.log('CLOSE THE BUY LIMIT LOSS,SELL{},BUY{}'.format(
                        self.dataclose_x[0], self.dataclose_y[0]))
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.sell(self.datas[0],
                                           size=self.params.size)
                    self.order = self.buy(self.datas[1],
                                          size=int(self.params.m *
                                                   self.params.size))

                else:
                    # SELL
                    # -0.5 < BP-m*RSDA-b < 0.5
                    if (((self.dataclose_x[0] -
                          self.params.m * self.dataclose_y[0] - self.params.b)
                         < self.params.avg) and
                        ((self.dataclose_x[0] -
                          self.params.m * self.dataclose_y[0] - self.params.b)
                         > -self.params.avg)):

                        # comes from higher portfolio value
                        if ((self.dataclose_x[0] -
                             self.params.m * self.dataclose_y[0] -
                             self.params.b) > 0):
                            self.log('CLOSE THE SELL,BUY{},SELL{}'.format(
                                self.dataclose_x[0], self.dataclose_y[0]))
                            # Keep track of the created order to avoid a 2nd order
                            self.order = self.buy(self.datas[0],
                                                  size=self.params.size)
                            self.order = self.sell(self.datas[1],
                                                   size=int(self.params.m *
                                                            self.params.size))

                        # comes from lower portfolio value
                        else:
                            self.log('CLOSE THE BUY,SELL{},BUY{}'.format(
                                self.dataclose_x[0], self.dataclose_y[0]))
                            # Keep track of the created order to avoid a 2nd order
                            self.order = self.sell(self.datas[0],
                                                   size=self.params.size)
                            self.order = self.buy(self.datas[1],
                                                  size=int(self.params.m *
                                                           self.params.size))

    # Run the model
    variable_name = ['stock1', 'stock2']
    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    cerebro.addstrategy(PairStrategy)

    # load the data
    datalist = [
        ('data\\intra_' + tickers[0] + '_' + year + '_' + intra_freq + '.csv',
         variable_name[0]),  #[0] = Data file, [1] = Data name
        ('data\\intra_' + tickers[1] + '_' + year + '_' + intra_freq + '.csv',
         variable_name[1]),
    ]

    # Loop through the list adding to cerebro.
    for i in range(len(datalist)):
        data = bt.feeds.GenericCSVData(dataname=datalist[i][0],
                                       datetime=0,
                                       open=1,
                                       high=2,
                                       low=3,
                                       close=4,
                                       openinterest=-1,
                                       time=-1,
                                       volume=-1,
                                       dtformat="%Y-%m-%d %H:%M:%S",
                                       timeframe=bt.TimeFrame.Minutes,
                                       compression=1)
        cerebro.adddata(data, name=datalist[i][1])

    # Set our desired cash start
    cerebro.broker.setcash(100000.0)

    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=10)

    # Set the commission - 0.1% ... divide by 100 to remove the %
    cerebro.broker.setcommission(commission=0.001)

    cerebro.addobserver(ValueObserver)

    # Print out the starting conditions
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

    # Run over everything
    cerebro.run()

    # Print out the final result
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

    # Print
    plot = cerebro.plot(volume=False)[0][0]
    plot.set_size_inches(18.5, 10.5)
    plot.savefig(tickers[0] + '_' + tickers[1] + '.png', dpi=1000)
Example #24
0
    def next(self):
        if not self.position:
            if self.rsi < self.params.rsi_low:
                self.buy(size=10)
        else:
            if self.rsi > self.params.rsi_high:
                self.sell(size=10)


# INPUT CONDITIONS TO FEED INTO CEREBRO IS ADDED HERE
if __name__ == '__main__':
    # Variable for our starting cash
    startcash = 10000
    # Create an instance of cerebro
    cerebro = bt.Cerebro(optreturn=False)

    # ADD STRATEGY
    cerebro.optstrategy(firstStrategy,
                        period=range(10, 11),
                        rsi_low=range(33, 34),
                        rsi_high=range(60, 61))

    cerebro.addobserver(OrderObserver)

    # DATA FEED FROM EXCHANGE

    symbol = str('ETH/USDT')
    timeframe = str('15m')
    exchange = str('poloniex')
    exchange_out = str(exchange)
Example #25
0
        rets = np.zeros(len(available))
        for i, d in enumerate(available):
            # calculate individual daily returns
            rets[i] = (d.close[0] - d.close[-1]) / d.close[-1]

        # calculate weights using formula
        market_ret = np.mean(rets)
        weights = -(rets - market_ret)
        weights = weights / np.sum(np.abs(weights))

        for i, d in enumerate(available):
            self.order_target_percent(d, target=weights[i])


cerebro = bt.Cerebro(stdstats=False)
cerebro.broker.set_coc(True)

for ticker in tickers:
    data = bt.feeds.GenericCSVData(
        fromdate=start,
        todate=end,
        dataname=fr"{output_dir}/{ticker}.csv",
        dtformat=("%Y-%m-%d"),
        openinterest=-1,
        nullvalue=0.0,
        plot=False,
    )
    cerebro.adddata(data)

cerebro.broker.setcash(1_000_000)
Example #26
0
                # Keep track of the created order to avoid a 2nd order
                self.order = self.buy()

        else:

            if self.dataclose[0] < self.sma[0]:
                # SELL, SELL, SELL!!! (with all possible default parameters)
                self.log('SELL CREATE, %.2f' % self.dataclose[0])

                # Keep track of the created order to avoid a 2nd order
                self.order = self.sell()


if __name__ == '__main__':

    cerebro = bt.Cerebro()  # create a "Cerebro" engine instance

    cerebro.addstrategy(SmaCross)  # Add the trading strategy

    data = MyTDXCSVData(dataname='C2105.csv')
    cerebro.adddata(data)  # Add the data feed

    # Set our desired cash start
    cerebro.broker.setcash(100000.0)

    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=10)

    # Set the commission - 0.1% ... divide by 100 to remove the %
    cerebro.broker.setcommission(commission=0.001)
Example #27
0
            if self.dataclose[0] < self.sma[0]:
                # SELL, SELL, SELL!!! (with all possible default parameters)
                self.log('SELL CREATE, %.2f' % self.dataclose[0])

                # Keep track of the created order to avoid a 2nd order
                self.order = self.sell()

    def stop(self):
        self.log('(MA Period %2d) Ending Value %.2f' %
                 (self.params.maperiod, self.broker.getvalue()), doprint=True)


if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    strats = cerebro.optstrategy(
        TestStrategy,
        maperiod=xrange(10, 31))

    # Datas are in a subfolder of the samples. Need to find where the script is
    # because it could have been called from anywhere
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, './datas/yahoo/orcl-1995-2014.txt')

    # Create a Data Feed
    data = bt.feeds.YahooFinanceCSVData(
        dataname=datapath,
        # Do not pass values before this date
Example #28
0
def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()

    storekwargs = dict(token=args.token,
                       account=args.account,
                       practice=not args.live)

    if not args.no_store:
        store = StoreCls(**storekwargs)

    if args.broker:
        if args.no_store:
            broker = BrokerCls(**storekwargs)
        else:
            broker = store.getbroker()

        cerebro.setbroker(broker)

    timeframe = bt.TimeFrame.TFrame(args.timeframe)
    # Manage data1 parameters
    tf1 = args.timeframe1
    tf1 = bt.TimeFrame.TFrame(tf1) if tf1 is not None else timeframe
    cp1 = args.compression1
    cp1 = cp1 if cp1 is not None else args.compression

    if args.resample or args.replay:
        datatf = datatf1 = bt.TimeFrame.Ticks
        datacomp = datacomp1 = 1
    else:
        datatf = timeframe
        datacomp = args.compression
        datatf1 = tf1
        datacomp1 = cp1

    fromdate = None
    if args.fromdate:
        dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
        fromdate = datetime.datetime.strptime(args.fromdate, dtformat)

    DataFactory = DataCls if args.no_store else store.getdata

    datakwargs = dict(timeframe=datatf,
                      compression=datacomp,
                      qcheck=args.qcheck,
                      historical=args.historical,
                      fromdate=fromdate,
                      bidask=args.bidask,
                      useask=args.useask,
                      backfill_start=not args.no_backfill_start,
                      backfill=not args.no_backfill,
                      tz=args.timezone)

    if args.no_store and not args.broker:  # neither store nor broker
        datakwargs.update(storekwargs)  # pass the store args over the data

    data0 = DataFactory(dataname=args.data0, **datakwargs)

    data1 = None
    if args.data1 is not None:
        if args.data1 != args.data0:
            datakwargs['timeframe'] = datatf1
            datakwargs['compression'] = datacomp1
            data1 = DataFactory(dataname=args.data1, **datakwargs)
        else:
            data1 = data0

    rekwargs = dict(
        timeframe=timeframe,
        compression=args.compression,
        bar2edge=not args.no_bar2edge,
        adjbartime=not args.no_adjbartime,
        rightedge=not args.no_rightedge,
        takelate=not args.no_takelate,
    )

    if args.replay:
        cerebro.replaydata(dataname=data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.replaydata(dataname=data1, **rekwargs)

    elif args.resample:
        cerebro.resampledata(dataname=data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.resampledata(dataname=data1, **rekwargs)

    else:
        cerebro.adddata(data0)
        if data1 is not None:
            cerebro.adddata(data1)

    if args.valid is None:
        valid = None
    else:
        valid = datetime.timedelta(seconds=args.valid)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        smaperiod=args.smaperiod,
                        trade=args.trade,
                        exectype=bt.Order.ExecType(args.exectype),
                        stake=args.stake,
                        stopafter=args.stopafter,
                        valid=valid,
                        cancel=args.cancel,
                        donotcounter=args.donotcounter,
                        sell=args.sell)

    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)
    if args.exactbars < 1:  # plotting is possible
        if args.plot:
            pkwargs = dict(style='line')
            if args.plot is not True:  # evals to True but is not True
                npkwargs = eval('dict(' + args.plot + ')')  # args were passed
                pkwargs.update(npkwargs)

            cerebro.plot(**pkwargs)
Example #29
0
        self.log('Price, %.4f' % close)

        if self.obv_cul1 > 0 and self.long_headl2:
            self.buy()

        elif self.long_headl2 and self.obv5 > self.obv10:
            self.buy()

        if self.sma_cdl1:
            self.close()


if __name__ == '__main__':

    # backtest from the best result
    cerebro = bt.Cerebro(stdstats=True)
    data = Candle4H(
        dataname=IGNIS_4H,
        dtformat="%Y-%m-%d",
        tmformat="%H:%M:%S",
        fromdate=datetime.datetime(2019, 6, 1),
        todate=datetime.datetime(2019, 7, 14),
    )
    cerebro.adddata(data)
    cerebro.addstrategy(Strat)
    cerebro.addsizer(bt.sizers.PercentSizer, percents=95)
    cerebro.broker.set_cash(1000000)
    cerebro.broker.setcommission(commission=0.001)
    cerebro.run()

    cerebro.plot(style='candlestick', barup='green', bardown='red')
Example #30
0
    def train_strategy(cls, training_data, stock_id):
        """
        Find the optimized parameter of the stategy by using training data.
        :param training_data(DataFrame): data used to train the strategy.
        :param stock_id(integer): stock on which the strategy works.
        :return: params(dict like {ma_periods: dict{ma_period_s: 1, ma_period_l: 2, stock_id: '0'}}
        """
        # get the params list
        params_list = cls.get_params_list(training_data, stock_id)

        al_results = []

        cerebro = bt.Cerebro()
        
        # Change Data Type [https://www.backtrader.com/docu/dataautoref/#pandasdata]
        #convert_dict = {'date': complex, 'high': float, 'low': float, 'close': float, 'volume': int}
        #data = data.astype(convert_dict)
        training_data = training_data.apply(pd.to_numeric)
        training_data.head()

        data = bt.feeds.PandasData(dataname=training_data)

        cerebro.adddata(data)
        cerebro.optstrategy(cls, ma_periods=params_list)
        cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='al_return',
                            timeframe=bt.analyzers.TimeFrame.NoTimeFrame)
        cerebro.addanalyzer(bt.analyzers.TimeDrawDown, _name='al_max_drawdown')

        cerebro.broker.setcash(conf.DEFAULT_CASH)

        logger.debug(f'Starting train the strategy for stock {stock_id}...')

        results = cerebro.run()

        for result in results:
            params = result[0].params
            analyzers = result[0].analyzers
            al_return_rate = analyzers.al_return.get_analysis()
            total_return_rate = 0.0
            for k, v in al_return_rate.items():
                total_return_rate = v
            al_result = dict(
                params=params,
                total_return_rate=total_return_rate,
                max_drawdown=analyzers.al_max_drawdown.get_analysis().get('maxdrawdown'),
                max_drawdown_period=analyzers.al_max_drawdown.get_analysis().get('maxdrawdownperiod')
            )
            al_results.append(al_result)

        # Get the best params
        best_al_result = bsu.Utils.get_best_params(al_results)

        params = best_al_result.get('params')
        ma_periods = params.ma_periods

        logger.debug(
            'Stock %s best parma is ma_period_s: %d, ma_period_l: %d' %
            (
                ma_periods.get('stock_id'),
                ma_periods.get('ma_period_s'),
                ma_periods.get('ma_period_l')
            ))

        return params