Package: pxtrade
pxtradeA multi currency, event driven backtester written in Python.InstallationpipinstallpxtradeExamplesNotebooksare available to cover the main concepts and examples.equities buy and holdfx tradingbitcoinIntraday tradingAssets and PortfoliosBefore we can run a backtest we need to define the assets and portfolios involved.frompxtrade.assetsimportreset,Cash,Stock,FxRate,Portfolioreset()aud=Cash("AUD")usd=Cash("USD")audusd=FxRate("AUDUSD")spy=Stock("SPY")portfolio=Portfolio("USD",code="Portfolio")# a portfolio denominated in USDbenchmark=Portfolio("USD",code="Benchmark")print(portfolio)Portfolio('USD')portfolio.transfer(usd,1e6)# start both with 1M USDbenchmark.transfer(usd,1e6)print(portfolio)Portfolio('USD'):
Cash('USD', 1.0, currency_code='USD'): 1,000,000portfolio.value1000000.0Imposing portfolio constraints through complianceIdeally there will be risk limits in place when running a backtest. Some concrete compliance rules are provided, but you can also define your own byinheriting from ComplianceRule.frompxtrade.complianceimportCompliance,UnitLimitforportin[portfolio,benchmark]:port.compliance=Compliance().add_rule(UnitLimit(spy,1000))Defining a portfolio brokerDifferent portfolios / strategies are likely to vary materially in broker charges. All portfolios have a default broker that executes trades at the last price with no charge (or slippage). Brokers have separate execution and charges strategies. You can use the classes available or define custom strategies by inheriting fromAbstractExecutionorAbstractCharges. Note that backtesting supports multiple currencies. The portfolio could be denominated in USD, for example, but broker charges defined in AUD terms.frompxtrade.brokerimport(Broker,FillAtLastWithSlippage,FixedRatePlusPercentage,)portfolio.broker=Broker(execution_strategy=FillAtLastWithSlippage(0),# no slippage, just fill at lastcharges_strategy=FixedRatePlusPercentage(20,0,currency_code="AUD")# fixed charge of AUD 20 per trade.)Defining a trading strategyAll strategy classes must inherit from pxtrade.Strategy and implement a generate_trades method. Note that the trades returned can either be None, a trade instance or list or trades.frompxtradeimportStrategy,TradeclassExampleStrategy(Strategy):defgenerate_trades(self):trades=list()# get the portfolio trades firstifspy.price<330:trades.append(Trade(portfolio,spy,+100))trades.append(Trade(benchmark,spy,+1000))returntradesThe backtest instance and trade historyA backtest takes a strategy instance as its argument. Any instances of History then record state through time as events are processed.frompxtradeimportBacktest,Historybacktest=Backtest(ExampleStrategy())history=History(portfolios=[portfolio,benchmark],backtest=backtest)Loading event dataEvents can be loaded either from yahoo finance or from an existing data frame.fromdatetimeimportdatefrompxtrade.events.yahooimportload_yahoo_pricesstart_date=date(2020,6,30)end_date=date(2020,9,30)load_yahoo_prices([spy,audusd],backtest,start_date=start_date,end_date=end_date,)Running the backtest and collecting historybacktest.run()df=history.get()df.columnsIndex(['AUD', 'AUDUSD', 'Benchmark', 'Benchmark_AUD', 'Benchmark_SPY',
       'Benchmark_USD', 'Portfolio', 'Portfolio_AUD', 'Portfolio_SPY',
       'Portfolio_USD', 'SPY', 'USD'],
      dtype='object')importcufflinksascfcolumns=["Portfolio_SPY","Benchmark_SPY","SPY"]df[columns].iplot(secondary_y="SPY",title="Portfolio Holdings of SPY",)columns=["Portfolio","Benchmark"]df[columns].iplot(title="Portfolio Value",)