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
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
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)
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)
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())
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())
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()
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)
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')
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()
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')
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)
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)
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)
# 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)
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
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)
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')
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