def size_order(self, portfolio, initial_order): """ Size the order to reflect the dollar-weighting of the current equity account size based on pre-specified ticker weights. """ ticker = initial_order.ticker if initial_order.action == "EXIT": # Obtain current quantity and liquidate cur_quantity = portfolio.positions[ticker].quantity if cur_quantity > 0: initial_order.action = "SLD" initial_order.quantity = cur_quantity else: initial_order.action = "BOT" initial_order.quantity = cur_quantity else: weight = self.ticker_weights[ticker] # Determine total portfolio value, work out dollar weight # and finally determine integer quantity of shares to purchase price = portfolio.price_handler.tickers[ticker]["adj_close"] price = PriceParser.display(price) equity = PriceParser.display(portfolio.equity) dollar_weight = weight * equity weighted_quantity = int(floor(dollar_weight / price)) initial_order.quantity = weighted_quantity return initial_order
def test_calculate_round_trip(self): """ Purchase/sell multiple lots of AMZN and GOOG at various prices/commissions to check the arithmetic and cost handling. """ # Buy 300 of AMZN over two transactions self.portfolio.transact_position("BOT", "AMZN", 100, PriceParser.parse(566.56), PriceParser.parse(1.00)) self.portfolio.transact_position("BOT", "AMZN", 200, PriceParser.parse(566.395), PriceParser.parse(1.00)) # Buy 200 GOOG over one transaction self.portfolio.transact_position("BOT", "GOOG", 200, PriceParser.parse(707.50), PriceParser.parse(1.00)) # Add to the AMZN position by 100 shares self.portfolio.transact_position("SLD", "AMZN", 100, PriceParser.parse(565.83), PriceParser.parse(1.00)) # Add to the GOOG position by 200 shares self.portfolio.transact_position("BOT", "GOOG", 200, PriceParser.parse(705.545), PriceParser.parse(1.00)) # Sell 200 of the AMZN shares self.portfolio.transact_position("SLD", "AMZN", 200, PriceParser.parse(565.59), PriceParser.parse(1.00)) # Multiple transactions bundled into one (in IB) # Sell 300 GOOG from the portfolio self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(704.92), PriceParser.parse(1.00)) self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(704.90), PriceParser.parse(0.00)) self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(704.92), PriceParser.parse(0.50)) # Finally, sell the remaining GOOG 100 shares self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(704.78), PriceParser.parse(1.00)) # The figures below are derived from Interactive Brokers # demo account using the above trades with prices provided # by their demo feed. self.assertEqual(len(self.portfolio.positions), 0) self.assertEqual(len(self.portfolio.closed_positions), 2) self.assertEqual(PriceParser.display(self.portfolio.cur_cash), 499100.50) self.assertEqual(PriceParser.display(self.portfolio.equity), 499100.50) self.assertEqual(PriceParser.display(self.portfolio.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.portfolio.realised_pnl), -899.50)
def test_get_best_bid_ask(self): """ Tests that the 'get_best_bid_ask' method produces the correct values depending upon validity of ticker. """ bid, ask = self.price_handler.get_best_bid_ask("AMZN") self.assertEqual(PriceParser.display(bid, 5), 502.10001) self.assertEqual(PriceParser.display(ask, 5), 502.11999) bid, ask = self.price_handler.get_best_bid_ask("C")
def test_get_best_bid_ask(self): """ Tests that the 'get_best_bid_ask' method produces the correct values depending upon validity of ticker. """ bid, ask = self.price_handler.get_best_bid_ask("AMZN") self.assertEqual(PriceParser.display(bid, 5), 502.10001) self.assertEqual(PriceParser.display(ask, 5), 502.11999) bid, ask = self.price_handler.get_best_bid_ask("C")
def record_trade(self, fill): """ Append all details about the FillEvent to the CSV trade log. """ fname = os.path.expanduser(os.path.join(self.config.OUTPUT_DIR, self.csv_filename)) with open(fname, 'a') as csvfile: writer = csv.writer(csvfile) writer.writerow([ fill.timestamp, fill.ticker, fill.action, fill.quantity, fill.exchange, PriceParser.display(fill.price, 4), PriceParser.display(fill.commission, 4) ])
def record_trade(self, fill): """ Append all details about the FillEvent to the CSV trade log. """ fname = os.path.expanduser(os.path.join(self.config.OUTPUT_DIR, self.csv_filename)) with open( fname, 'a') as csvfile: writer = csv.writer(csvfile) writer.writerow([ fill.timestamp, fill.ticker, fill.action, fill.quantity, fill.exchange, PriceParser.display(fill.price, 4), PriceParser.display(fill.commission, 4) ])
def test_realised_unrealised_calcs(self): self.assertEqual( PriceParser.display(self.position.unrealised_pnl), -1.00 ) self.assertEqual( PriceParser.display(self.position.realised_pnl), 0.00 ) self.position.update_market_value( PriceParser.parse(75.77), PriceParser.parse(75.79) ) self.assertEqual( PriceParser.display(self.position.unrealised_pnl), 99.00 ) self.position.transact_shares( "SLD", 100, PriceParser.parse(75.78), PriceParser.parse(1.00) ) self.assertEqual( PriceParser.display(self.position.unrealised_pnl), 99.00 ) # still high self.assertEqual( PriceParser.display(self.position.realised_pnl), 98.00 ) self.position.update_market_value( PriceParser.parse(75.77), PriceParser.parse(75.79) ) self.assertEqual( PriceParser.display(self.position.unrealised_pnl), 0.00 )
def test_open_short_position(self): self.assertEqual(PriceParser.display(self.position.cost_basis), -7768.00) self.assertEqual(PriceParser.display(self.position.market_value), -7769.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), -1.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -1.0) self.position.update_market_value( PriceParser.parse(77.72), PriceParser.parse(77.72) ) self.assertEqual(PriceParser.display(self.position.cost_basis), -7768.00) self.assertEqual(PriceParser.display(self.position.market_value), -7772.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), -4.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -4.0)
def test_open_short_position(self): self.assertEqual(PriceParser.display(self.position.cost_basis), -7768.00) self.assertEqual(PriceParser.display(self.position.market_value), -7769.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), -1.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -1.0) self.position.update_market_value(PriceParser.parse(77.72), PriceParser.parse(77.72)) self.assertEqual(PriceParser.display(self.position.cost_basis), -7768.00) self.assertEqual(PriceParser.display(self.position.market_value), -7772.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), -4.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -4.0)
def test_realised_unrealised_calcs(self): self.assertEqual(PriceParser.display(self.position.unrealised_pnl), -1.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), 0.00) self.position.update_market_value(PriceParser.parse(75.77), PriceParser.parse(75.79)) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 99.00) self.position.transact_shares("SLD", 100, PriceParser.parse(75.78), PriceParser.parse(1.00)) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 99.00) # still high self.assertEqual(PriceParser.display(self.position.realised_pnl), 98.00) self.position.update_market_value(PriceParser.parse(75.77), PriceParser.parse(75.79)) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 0.00)
def test_calculate_round_trip(self): """ After the subsequent purchase, carry out two more buys/longs and then close the position out with two additional sells/shorts. The following prices have been tested against those calculated via Interactive Brokers' Trader Workstation (TWS). """ self.position.transact_shares("BOT", 100, PriceParser.parse(74.63), PriceParser.parse(1.00)) self.position.transact_shares("BOT", 250, PriceParser.parse(74.620), PriceParser.parse(1.25)) self.position.transact_shares("SLD", 200, PriceParser.parse(74.58), PriceParser.parse(1.00)) self.position.transact_shares("SLD", 250, PriceParser.parse(75.26), PriceParser.parse(1.25)) self.position.update_market_value(PriceParser.parse(77.75), PriceParser.parse(77.77)) self.assertEqual(self.position.action, "BOT") self.assertEqual(self.position.ticker, "XOM") self.assertEqual(self.position.quantity, 0) self.assertEqual(self.position.buys, 450) self.assertEqual(self.position.sells, 450) self.assertEqual(self.position.net, 0) self.assertEqual(PriceParser.display(self.position.avg_bot, 5), 74.65778) self.assertEqual(PriceParser.display(self.position.avg_sld, 5), 74.95778) self.assertEqual(PriceParser.display(self.position.total_bot), 33596.00) self.assertEqual(PriceParser.display(self.position.total_sld), 33731.00) self.assertEqual(PriceParser.display(self.position.net_total), 135.00) self.assertEqual(PriceParser.display(self.position.total_commission), 5.50) self.assertEqual(PriceParser.display(self.position.net_incl_comm), 129.50) self.assertEqual(PriceParser.display(self.position.avg_price, 3), 74.665) self.assertEqual(PriceParser.display(self.position.cost_basis), 0.00) self.assertEqual(PriceParser.display(self.position.market_value), 0.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), 129.50)
def test_calculating_statistics(self): """ Purchase/sell multiple lots of AMZN, GOOG at various prices/commissions to ensure the arithmetic in calculating equity, drawdowns and sharpe ratio is correct. """ # Create Statistics object price_handler = PriceHandlerMock() self.portfolio = Portfolio(price_handler, PriceParser.parse(500000.00)) portfolio_handler = PortfolioHandlerMock(self.portfolio) statistics = SimpleStatistics(self.config, portfolio_handler) # Check initialization was correct self.assertEqual(PriceParser.display(statistics.equity[0]), 500000.00) self.assertEqual(PriceParser.display(statistics.drawdowns[0]), 00) self.assertEqual(statistics.equity_returns[0], 0.0) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "BOT", "AMZN", 100, PriceParser.parse(566.56), PriceParser.parse(1.00) ) t = "2000-01-01 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[1]), 499807.00) self.assertEqual(PriceParser.display(statistics.drawdowns[1]), 193.00) self.assertEqual(statistics.equity_returns[1], -0.0386) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "BOT", "AMZN", 200, PriceParser.parse(566.395), PriceParser.parse(1.00) ) t = "2000-01-02 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[2]), 499455.00) self.assertEqual(PriceParser.display(statistics.drawdowns[2]), 545.00) self.assertEqual(statistics.equity_returns[2], -0.0705) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "BOT", "GOOG", 200, PriceParser.parse(707.50), PriceParser.parse(1.00) ) t = "2000-01-03 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[3]), 499046.00) self.assertEqual(PriceParser.display(statistics.drawdowns[3]), 954.00) self.assertEqual(statistics.equity_returns[3], -0.0820) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "AMZN", 100, PriceParser.parse(565.83), PriceParser.parse(1.00) ) t = "2000-01-04 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[4]), 499164.00) self.assertEqual(PriceParser.display(statistics.drawdowns[4]), 836.00) self.assertEqual(statistics.equity_returns[4], 0.0236) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "BOT", "GOOG", 200, PriceParser.parse(705.545), PriceParser.parse(1.00) ) t = "2000-01-05 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[5]), 499146.00) self.assertEqual(PriceParser.display(statistics.drawdowns[5]), 854.00) self.assertEqual(statistics.equity_returns[5], -0.0036) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "AMZN", 200, PriceParser.parse(565.59), PriceParser.parse(1.00) ) t = "2000-01-06 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[6]), 499335.00) self.assertEqual(PriceParser.display(statistics.drawdowns[6]), 665.00) self.assertEqual(statistics.equity_returns[6], 0.0379) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(707.92), PriceParser.parse(1.00) ) t = "2000-01-07 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[7]), 499580.00) self.assertEqual(PriceParser.display(statistics.drawdowns[7]), 420.00) self.assertEqual(statistics.equity_returns[7], 0.0490) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(707.90), PriceParser.parse(0.00) ) t = "2000-01-08 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[8]), 499824.00) self.assertEqual(PriceParser.display(statistics.drawdowns[8]), 176.00) self.assertEqual(statistics.equity_returns[8], 0.0488) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(707.92), PriceParser.parse(0.50) ) t = "2000-01-09 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[9]), 500069.50) self.assertEqual(PriceParser.display(statistics.drawdowns[9]), 00.00) self.assertEqual(statistics.equity_returns[9], 0.0491) # Perform transaction and test statistics at this tick self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(707.78), PriceParser.parse(1.00) ) t = "2000-01-10 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[10]), 500300.50) self.assertEqual(PriceParser.display(statistics.drawdowns[10]), 00.00) self.assertEqual(statistics.equity_returns[10], 0.0462) # Test that results are calculated correctly. results = statistics.get_results() self.assertEqual(results["max_drawdown"], 954.00) self.assertEqual(results["max_drawdown_pct"], 0.1908) self.assertAlmostEqual(float(results["sharpe"]), 1.7575)
def test_stream_all_ticks(self): """ The initialisation of the class will open the three test CSV files, then merge and sort them. They will then be stored in a member "tick_stream". This will be used for streaming the ticks. """ # Stream to Tick #1 (GOOG) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["GOOG"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:01.358000") self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["bid"], 5), 683.56000) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["ask"], 5), 683.58000) # Stream to Tick #2 (AMZN) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["AMZN"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:01.562000") self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["bid"], 5), 502.10001) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["ask"], 5), 502.11999) # Stream to Tick #3 (MSFT) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["MSFT"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:01.578000") self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["bid"], 5), 50.14999) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["ask"], 5), 50.17001) # Stream to Tick #10 (GOOG) for i in range(4, 11): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["GOOG"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:05.215000") self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["bid"], 5), 683.56001) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["ask"], 5), 683.57999) # Stream to Tick #20 (GOOG) for i in range(11, 21): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["MSFT"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:09.904000") self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["bid"], 5), 50.15000) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["ask"], 5), 50.17000) # Stream to Tick #30 (final tick, AMZN) for i in range(21, 31): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["AMZN"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f"), "01-02-2016 00:00:14.616000") self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["bid"], 5), 502.10015) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["ask"], 5), 502.11985)
def test_display(self): parsed = PriceParser.parse(self.float) displayed = PriceParser.display(parsed) self.assertEqual(displayed, 10.12)
def test_calculating_statistics(self): """ Purchase/sell multiple lots of AMZN, GOOG at various prices/commissions to ensure the arithmetic in calculating equity, drawdowns and sharpe ratio is correct. """ # Create Statistics object price_handler = PriceHandlerMock() self.portfolio = Portfolio(price_handler, PriceParser.parse(500000.00)) portfolio_handler = PortfolioHandlerMock(self.portfolio) statistics = SimpleStatistics(self.config, portfolio_handler) # Check initialization was correct self.assertEqual(PriceParser.display(statistics.equity[0]), 500000.00) self.assertEqual(PriceParser.display(statistics.drawdowns[0]), 00) self.assertEqual(statistics.equity_returns[0], 0.0) # Perform transaction and test statistics at this tick self.portfolio.transact_position("BOT", "AMZN", 100, PriceParser.parse(566.56), PriceParser.parse(1.00)) t = "2000-01-01 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[1]), 499807.00) self.assertEqual(PriceParser.display(statistics.drawdowns[1]), 193.00) self.assertEqual(statistics.equity_returns[1], -0.0386) # Perform transaction and test statistics at this tick self.portfolio.transact_position("BOT", "AMZN", 200, PriceParser.parse(566.395), PriceParser.parse(1.00)) t = "2000-01-02 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[2]), 499455.00) self.assertEqual(PriceParser.display(statistics.drawdowns[2]), 545.00) self.assertEqual(statistics.equity_returns[2], -0.0705) # Perform transaction and test statistics at this tick self.portfolio.transact_position("BOT", "GOOG", 200, PriceParser.parse(707.50), PriceParser.parse(1.00)) t = "2000-01-03 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[3]), 499046.00) self.assertEqual(PriceParser.display(statistics.drawdowns[3]), 954.00) self.assertEqual(statistics.equity_returns[3], -0.0820) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "AMZN", 100, PriceParser.parse(565.83), PriceParser.parse(1.00)) t = "2000-01-04 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[4]), 499164.00) self.assertEqual(PriceParser.display(statistics.drawdowns[4]), 836.00) self.assertEqual(statistics.equity_returns[4], 0.0236) # Perform transaction and test statistics at this tick self.portfolio.transact_position("BOT", "GOOG", 200, PriceParser.parse(705.545), PriceParser.parse(1.00)) t = "2000-01-05 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[5]), 499146.00) self.assertEqual(PriceParser.display(statistics.drawdowns[5]), 854.00) self.assertEqual(statistics.equity_returns[5], -0.0036) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "AMZN", 200, PriceParser.parse(565.59), PriceParser.parse(1.00)) t = "2000-01-06 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[6]), 499335.00) self.assertEqual(PriceParser.display(statistics.drawdowns[6]), 665.00) self.assertEqual(statistics.equity_returns[6], 0.0379) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(707.92), PriceParser.parse(1.00)) t = "2000-01-07 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[7]), 499580.00) self.assertEqual(PriceParser.display(statistics.drawdowns[7]), 420.00) self.assertEqual(statistics.equity_returns[7], 0.0490) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(707.90), PriceParser.parse(0.00)) t = "2000-01-08 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[8]), 499824.00) self.assertEqual(PriceParser.display(statistics.drawdowns[8]), 176.00) self.assertEqual(statistics.equity_returns[8], 0.0488) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(707.92), PriceParser.parse(0.50)) t = "2000-01-09 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[9]), 500069.50) self.assertEqual(PriceParser.display(statistics.drawdowns[9]), 00.00) self.assertEqual(statistics.equity_returns[9], 0.0491) # Perform transaction and test statistics at this tick self.portfolio.transact_position("SLD", "GOOG", 100, PriceParser.parse(707.78), PriceParser.parse(1.00)) t = "2000-01-10 00:00:00" statistics.update(t, portfolio_handler) self.assertEqual(PriceParser.display(statistics.equity[10]), 500300.50) self.assertEqual(PriceParser.display(statistics.drawdowns[10]), 00.00) self.assertEqual(statistics.equity_returns[10], 0.0462) # Test that results are calculated correctly. results = statistics.get_results() self.assertEqual(results["max_drawdown"], 954.00) self.assertEqual(results["max_drawdown_pct"], 0.1908) self.assertAlmostEqual(float(results["sharpe"]), 1.7575)
def test_unparsed_display(self): displayed = PriceParser.display(self.float) self.assertEqual(displayed, 10.12)
def test_unparsed_display(self): displayed = PriceParser.display(self.float) self.assertEqual(displayed, 10.12)
def test_calculate_round_trip(self): """ After the subsequent sale, carry out two more sells/shorts and then close the position out with two additional buys/longs. The following prices have been tested against those calculated via Interactive Brokers' Trader Workstation (TWS). """ self.position.transact_shares( "SLD", 100, PriceParser.parse(77.68), PriceParser.parse(1.00) ) self.position.transact_shares( "SLD", 50, PriceParser.parse(77.70), PriceParser.parse(1.00) ) self.position.transact_shares( "BOT", 100, PriceParser.parse(77.77), PriceParser.parse(1.00) ) self.position.transact_shares( "BOT", 150, PriceParser.parse(77.73), PriceParser.parse(1.00) ) self.position.update_market_value( PriceParser.parse(77.72), PriceParser.parse(77.72) ) self.assertEqual(self.position.action, "SLD") self.assertEqual(self.position.ticker, "PG") self.assertEqual(self.position.quantity, 0) self.assertEqual(self.position.buys, 250) self.assertEqual(self.position.sells, 250) self.assertEqual(self.position.net, 0) self.assertEqual( PriceParser.display(self.position.avg_bot, 3), 77.746 ) self.assertEqual( PriceParser.display(self.position.avg_sld, 3), 77.688 ) self.assertEqual(PriceParser.display(self.position.total_bot), 19436.50) self.assertEqual(PriceParser.display(self.position.total_sld), 19422.00) self.assertEqual(PriceParser.display(self.position.net_total), -14.50) self.assertEqual(PriceParser.display(self.position.total_commission), 5.00) self.assertEqual(PriceParser.display(self.position.net_incl_comm), -19.50) self.assertEqual( PriceParser.display(self.position.avg_price, 5), 77.67600 ) self.assertEqual(PriceParser.display(self.position.cost_basis), 0.00) self.assertEqual(PriceParser.display(self.position.market_value), 0.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -19.50)
def test_calculate_round_trip(self): """ After the subsequent purchase, carry out two more buys/longs and then close the position out with two additional sells/shorts. The following prices have been tested against those calculated via Interactive Brokers' Trader Workstation (TWS). """ self.position.transact_shares( "BOT", 100, PriceParser.parse(74.63), PriceParser.parse(1.00) ) self.position.transact_shares( "BOT", 250, PriceParser.parse(74.620), PriceParser.parse(1.25) ) self.position.transact_shares( "SLD", 200, PriceParser.parse(74.58), PriceParser.parse(1.00) ) self.position.transact_shares( "SLD", 250, PriceParser.parse(75.26), PriceParser.parse(1.25) ) self.position.update_market_value( PriceParser.parse(77.75), PriceParser.parse(77.77) ) self.assertEqual(self.position.action, "BOT") self.assertEqual(self.position.ticker, "XOM") self.assertEqual(self.position.quantity, 0) self.assertEqual(self.position.buys, 450) self.assertEqual(self.position.sells, 450) self.assertEqual(self.position.net, 0) self.assertEqual( PriceParser.display(self.position.avg_bot, 5), 74.65778 ) self.assertEqual( PriceParser.display(self.position.avg_sld, 5), 74.95778 ) self.assertEqual(PriceParser.display(self.position.total_bot), 33596.00) self.assertEqual(PriceParser.display(self.position.total_sld), 33731.00) self.assertEqual(PriceParser.display(self.position.net_total), 135.00) self.assertEqual(PriceParser.display(self.position.total_commission), 5.50) self.assertEqual(PriceParser.display(self.position.net_incl_comm), 129.50) self.assertEqual( PriceParser.display(self.position.avg_price, 3), 74.665 ) self.assertEqual(PriceParser.display(self.position.cost_basis), 0.00) self.assertEqual(PriceParser.display(self.position.market_value), 0.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), 129.50)
def test_calculate_round_trip(self): """ After the subsequent sale, carry out two more sells/shorts and then close the position out with two additional buys/longs. The following prices have been tested against those calculated via Interactive Brokers' Trader Workstation (TWS). """ self.position.transact_shares("SLD", 100, PriceParser.parse(77.68), PriceParser.parse(1.00)) self.position.transact_shares("SLD", 50, PriceParser.parse(77.70), PriceParser.parse(1.00)) self.position.transact_shares("BOT", 100, PriceParser.parse(77.77), PriceParser.parse(1.00)) self.position.transact_shares("BOT", 150, PriceParser.parse(77.73), PriceParser.parse(1.00)) self.position.update_market_value(PriceParser.parse(77.72), PriceParser.parse(77.72)) self.assertEqual(self.position.action, "SLD") self.assertEqual(self.position.ticker, "PG") self.assertEqual(self.position.quantity, 0) self.assertEqual(self.position.buys, 250) self.assertEqual(self.position.sells, 250) self.assertEqual(self.position.net, 0) self.assertEqual(PriceParser.display(self.position.avg_bot, 3), 77.746) self.assertEqual(PriceParser.display(self.position.avg_sld, 3), 77.688) self.assertEqual(PriceParser.display(self.position.total_bot), 19436.50) self.assertEqual(PriceParser.display(self.position.total_sld), 19422.00) self.assertEqual(PriceParser.display(self.position.net_total), -14.50) self.assertEqual(PriceParser.display(self.position.total_commission), 5.00) self.assertEqual(PriceParser.display(self.position.net_incl_comm), -19.50) self.assertEqual(PriceParser.display(self.position.avg_price, 5), 77.67600) self.assertEqual(PriceParser.display(self.position.cost_basis), 0.00) self.assertEqual(PriceParser.display(self.position.market_value), 0.00) self.assertEqual(PriceParser.display(self.position.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.position.realised_pnl), -19.50)
def test_stream_all_ticks(self): """ The initialisation of the class will open the three test CSV files, then merge and sort them. They will then be stored in a member "tick_stream". This will be used for streaming the ticks. """ # Stream to Tick #1 (GOOG) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["GOOG"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:01.358000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["bid"], 5), 683.56000 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["ask"], 5), 683.58000 ) # Stream to Tick #2 (AMZN) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["AMZN"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:01.562000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["bid"], 5), 502.10001 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["ask"], 5), 502.11999 ) # Stream to Tick #3 (MSFT) self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["MSFT"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:01.578000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["bid"], 5), 50.14999 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["ask"], 5), 50.17001 ) # Stream to Tick #10 (GOOG) for i in range(4, 11): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["GOOG"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:05.215000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["bid"], 5), 683.56001 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["GOOG"]["ask"], 5), 683.57999 ) # Stream to Tick #20 (GOOG) for i in range(11, 21): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["MSFT"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:09.904000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["bid"], 5), 50.15000 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["MSFT"]["ask"], 5), 50.17000 ) # Stream to Tick #30 (final tick, AMZN) for i in range(21, 31): self.price_handler.stream_next() self.assertEqual( self.price_handler.tickers["AMZN"]["timestamp"].strftime( "%d-%m-%Y %H:%M:%S.%f" ), "01-02-2016 00:00:14.616000" ) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["bid"], 5), 502.10015 ) self.assertEqual( PriceParser.display(self.price_handler.tickers["AMZN"]["ask"], 5), 502.11985 )
def test_display(self): parsed = PriceParser.parse(self.float) displayed = PriceParser.display(parsed) self.assertEqual(displayed, 10.12)
def test_calculate_round_trip(self): """ Purchase/sell multiple lots of AMZN and GOOG at various prices/commissions to check the arithmetic and cost handling. """ # Buy 300 of AMZN over two transactions self.portfolio.transact_position( "BOT", "AMZN", 100, PriceParser.parse(566.56), PriceParser.parse(1.00) ) self.portfolio.transact_position( "BOT", "AMZN", 200, PriceParser.parse(566.395), PriceParser.parse(1.00) ) # Buy 200 GOOG over one transaction self.portfolio.transact_position( "BOT", "GOOG", 200, PriceParser.parse(707.50), PriceParser.parse(1.00) ) # Add to the AMZN position by 100 shares self.portfolio.transact_position( "SLD", "AMZN", 100, PriceParser.parse(565.83), PriceParser.parse(1.00) ) # Add to the GOOG position by 200 shares self.portfolio.transact_position( "BOT", "GOOG", 200, PriceParser.parse(705.545), PriceParser.parse(1.00) ) # Sell 200 of the AMZN shares self.portfolio.transact_position( "SLD", "AMZN", 200, PriceParser.parse(565.59), PriceParser.parse(1.00) ) # Multiple transactions bundled into one (in IB) # Sell 300 GOOG from the portfolio self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(704.92), PriceParser.parse(1.00) ) self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(704.90), PriceParser.parse(0.00) ) self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(704.92), PriceParser.parse(0.50) ) # Finally, sell the remaining GOOG 100 shares self.portfolio.transact_position( "SLD", "GOOG", 100, PriceParser.parse(704.78), PriceParser.parse(1.00) ) # The figures below are derived from Interactive Brokers # demo account using the above trades with prices provided # by their demo feed. self.assertEqual(len(self.portfolio.positions), 0) self.assertEqual(len(self.portfolio.closed_positions), 2) self.assertEqual(PriceParser.display(self.portfolio.cur_cash), 499100.50) self.assertEqual(PriceParser.display(self.portfolio.equity), 499100.50) self.assertEqual(PriceParser.display(self.portfolio.unrealised_pnl), 0.00) self.assertEqual(PriceParser.display(self.portfolio.realised_pnl), -899.50)