def test_transact_transaction_3(self): position = BacktestPositionFactory.create_position(self.contract) quantity1 = 50 price1 = 100 commission1 = 5 position.transact_transaction( Transaction(self.random_time, self.contract, quantity1, price1, commission1)) quantity2 = 30 price2 = 120 commission2 = 7 position.transact_transaction( Transaction(self.random_time, self.contract, quantity2, price2, commission2)) quantity3 = -40 price3 = 150 commission3 = 11 transaction3 = Transaction(self.random_time, self.contract, quantity3, price3, commission3) position.transact_transaction(transaction3) self.assertEqual(position.contract(), self.contract) self.assertEqual(position._is_closed, False) self.assertEqual(position.quantity(), quantity1 + quantity2 + quantity3) self.assertEqual(position.direction(), 1) self.assertEqual(position.start_time, self.random_time)
def test_transact_transaction_close_position_2_transactions(self): for quantity in (-50, 50): portfolio, dh, _ = self.get_portfolio_and_data_handler() contract = self.fut_contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) all_commissions = 0.0 transaction_1 = Transaction(self.random_time, contract, quantity=quantity, price=200, commission=7) portfolio.transact_transaction(transaction_1) all_commissions += transaction_1.commission self.data_handler_prices = self.prices_series portfolio.update() new_price = dh.get_last_available_price(ticker) transaction_2 = Transaction(self.end_time, contract, quantity=-transaction_1.quantity, price=new_price, commission=transaction_1.commission) portfolio.transact_transaction(transaction_2) all_commissions += transaction_2.commission portfolio.update() pnl = (new_price - transaction_1.price) * transaction_1.quantity * contract.contract_size \ - all_commissions self.assertEqual(portfolio.initial_cash, self.initial_cash) self.assertEqual(portfolio.net_liquidation, self.initial_cash + pnl) self.assertEqual(portfolio.gross_exposure_of_positions, 0) self.assertEqual(portfolio.current_cash, self.initial_cash + pnl) self.assertEqual(len(portfolio.open_positions_dict), 0)
def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday( self): self.buy_stop_loss_order = Order(self.msft_contract, quantity=1, execution_style=StopOrder(120.0), time_in_force=TimeInForce.GTC) self.exec_handler.assign_order_ids([self.buy_stop_loss_order]) self._set_bar_for_today(open_price=120.0, high_price=130.0, low_price=68.0, close_price=90.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_1.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_2.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, 1, 120, 0), ] self.monitor.record_transaction.assert_has_calls( call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls( call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 3) self.assertEqual(self.portfolio.transact_transaction.call_count, 3)
def test_transact_transaction_5(self): position = BacktestFuturePosition(self.ticker) quantity1 = 50 price1 = 100 commission1 = 5 cash_move1 = position.transact_transaction( Transaction(self.random_time, self.ticker, quantity1, price1, commission1)) self.assertEqual(cash_move1, -commission1) quantity1 = 50 price1 = 110 commission1 = 5 cash_move1 = position.transact_transaction( Transaction(self.random_time, self.ticker, quantity1, price1, commission1)) self.assertEqual(cash_move1, -commission1) quantity2 = -30 price2 = 120 commission2 = 7 cash_move2 = position.transact_transaction( Transaction(self.random_time, self.ticker, quantity2, price2, commission2)) expected_move = (price2 - 105) * (-quantity2) * self.point_value - commission2 self.assertEqual(cash_move2, expected_move)
def test_execute_orders(self): mocked_fill_volume = 30 mocked_fill_price = 100.0 simulated_executor = self._set_up_simulated_executor( mocked_fill_price=mocked_fill_price, mocked_fill_volume=mocked_fill_volume) simulated_executor.execute_orders() # No open orders were available, thus the list of transactions should be empty self.assertCountEqual(self.recorded_transactions, []) simulated_executor.assign_order_ids(self.orders) simulated_executor.accept_orders(self.orders) simulated_executor.execute_orders() # Both open orders were available expected_transactions = [ Transaction( self.backtest_date, self.contract_ticker_mapper.ticker_to_contract( self.example_ticker), mocked_fill_volume, mocked_fill_price, 0.0), Transaction( self.backtest_date, self.contract_ticker_mapper.ticker_to_contract( self.example_ticker_2), mocked_fill_volume, mocked_fill_price, 0.0), ] self.assertCountEqual(self.recorded_transactions, expected_transactions)
def test_portfolio_eod_series(self): expected_portfolio_eod_series = PricesSeries() # Empty portfolio portfolio, dh, timer = self.get_portfolio_and_data_handler() portfolio.update(record=True) expected_portfolio_eod_series[timer.time] = self.initial_cash contract = self.fut_contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) # Buy contract self._shift_timer_to_next_day(timer) transaction_1 = Transaction(timer.time, contract, quantity=50, price=250, commission=7) portfolio.transact_transaction(transaction_1) self.data_handler_prices = self.prices_series portfolio.update(record=True) position = portfolio.open_positions_dict[contract] price_1 = dh.get_last_available_price(ticker) pnl = contract.contract_size * transaction_1.quantity * (price_1 - transaction_1.price) nav = self.initial_cash + pnl - transaction_1.commission expected_portfolio_eod_series[timer.time] = nav # Contract goes up in value self._shift_timer_to_next_day(timer) self.data_handler_prices = self.prices_up portfolio.update(record=True) price_2 = dh.get_last_available_price(ticker) # == 270 pnl = contract.contract_size * transaction_1.quantity * (price_2 - price_1) nav += pnl expected_portfolio_eod_series[timer.time] = nav # Sell part of the contract self._shift_timer_to_next_day(timer) transaction_2 = Transaction(timer.time, contract, quantity=-25, price=price_2, commission=19) portfolio.transact_transaction(transaction_2) self.data_handler_prices = self.prices_up portfolio.update(record=True) pnl = (transaction_2.price - price_2) * transaction_2.quantity * contract.contract_size - transaction_2.commission nav += pnl expected_portfolio_eod_series[timer.time] = nav # Price goes down self._shift_timer_to_next_day(timer) self.data_handler_prices = self.prices_down portfolio.update(record=True) price_3 = dh.get_last_available_price(ticker) # == 210 pnl2 = contract.contract_size * position.quantity() * (price_3 - price_2) nav += pnl2 expected_portfolio_eod_series[timer.time] = nav tms = portfolio.portfolio_eod_series() assert_series_equal(expected_portfolio_eod_series, tms)
def test_position_close(self): position = BacktestPosition(self.contract) self.assertEqual(position.is_closed, False) position.transact_transaction(Transaction(self.time, self.contract, 20, 100, 0)) self.assertEqual(position.is_closed, False) position.transact_transaction(Transaction(self.time, self.contract, -10, 120, 20)) self.assertEqual(position.is_closed, False) position.transact_transaction(Transaction(self.time, self.contract, -10, 110, 20)) self.assertEqual(position.is_closed, True)
def test_transact_transaction_2(self): portfolio, dh, _ = self.get_portfolio_and_data_handler() contract = self.contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) # First transaction transaction_1 = Transaction(self.random_time, contract, quantity=50, price=100, commission=5) portfolio.transact_transaction(transaction_1) # Set new prices self.data_handler_prices = self.prices_series portfolio.update() # Get the new price of the contract new_price = dh.get_last_available_price(ticker) pnl_1 = (new_price - transaction_1.price) * transaction_1.quantity * transaction_1.contract.contract_size \ - transaction_1.commission cash_move1 = self._cash_move(transaction_1) self.assertEqual(portfolio.initial_cash, self.initial_cash) self.assertEqual(portfolio.net_liquidation, self.initial_cash + pnl_1) self.assertEqual(portfolio.gross_exposure_of_positions, new_price * transaction_1.quantity) self.assertEqual(portfolio.current_cash, self.initial_cash + cash_move1) self.assertEqual(len(portfolio.open_positions_dict), 1) # Second transaction transaction_2 = Transaction(self.random_time, contract, quantity=-20, price=110, commission=5) portfolio.transact_transaction(transaction_2) portfolio.update() pnl_2 = (new_price - transaction_2.price) * transaction_2.quantity * transaction_2.contract.contract_size \ - transaction_2.commission cash_move_2 = self._cash_move(transaction_2) self.assertEqual(portfolio.initial_cash, self.initial_cash) self.assertEqual(portfolio.net_liquidation, self.initial_cash + pnl_1 + pnl_2) position = portfolio.open_positions_dict[contract] self.assertEqual( portfolio.gross_exposure_of_positions, new_price * position.quantity() * contract.contract_size) self.assertEqual(portfolio.current_cash, self.initial_cash + cash_move1 + cash_move_2) self.assertEqual(len(portfolio.open_positions_dict), 1)
def test_transact_transaction_split_and_close(self): portfolio, dh, _ = self.get_portfolio_and_data_handler() contract = self.fut_contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) # Transact the initial transaction transactions = [] quantity = 50 # Set initial price for the given ticker self.data_handler_prices = self.prices_down price_1 = dh.get_last_available_price(ticker) # == 210 initial_transaction = Transaction(self.random_time, contract, quantity=quantity, price=price_1, commission=10) portfolio.transact_transaction(initial_transaction) transactions.append(initial_transaction) portfolio.update() # Change of price for the given ticker self.data_handler_prices = self.prices_series price_2 = dh.get_last_available_price(ticker) # == 250 transaction_to_split = Transaction(self.random_time, contract, quantity=(-2) * quantity, price=price_2, commission=18) portfolio.transact_transaction(transaction_to_split) transactions.append(transaction_to_split) portfolio.update() trade_pnl = (price_2 - price_1) * contract.contract_size * quantity trade_pnl -= initial_transaction.commission trade_pnl -= transaction_to_split.commission * abs( initial_transaction.quantity / transaction_to_split.quantity) # Change of price for the given ticker self.data_handler_prices = self.prices_series price_3 = dh.get_last_available_price(ticker) # == 270 closing_transaction = Transaction(self.random_time, contract, quantity=quantity, price=price_3, commission=5) portfolio.transact_transaction(closing_transaction) transactions.append(closing_transaction) portfolio.update() # All positions should be closed at this moment self.assertEqual(len(portfolio.open_positions_dict), 0)
def test_position_direction1(self): position = BacktestPositionFactory.create_position(self.contract) self.assertEqual(position.direction(), 0) position.transact_transaction( Transaction(self.start_time, self.contract, 20, 100, 0)) self.assertEqual(position.direction(), 1) position.transact_transaction( Transaction(self.start_time, self.contract, -10, 100, 0)) self.assertEqual(position.direction(), 1) position.transact_transaction( Transaction(self.start_time, self.contract, -10, 100, 0)) self.assertEqual(position.direction(), 1)
def test_position_close(self): position = BacktestPositionFactory.create_position(self.ticker) self.assertEqual(position._is_closed, False) position.transact_transaction( Transaction(self.start_time, self.ticker, 20, 100, 0)) self.assertEqual(position._is_closed, False) position.transact_transaction( Transaction(self.start_time, self.ticker, -10, 120, 20)) self.assertEqual(position._is_closed, False) position.transact_transaction( Transaction(self.start_time, self.ticker, -10, 110, 20)) self.assertEqual(position._is_closed, True)
def test_closed_position(self): position = BacktestPosition(self.contract) position.transact_transaction(Transaction(self.time, self.contract, 20, 100, 0)) position.transact_transaction(Transaction(self.time, self.contract, -20, 100, 0)) with self.assertRaises(AssertionError): position.transact_transaction(Transaction(self.time, self.contract, 1, 100, 0)) with self.assertRaises(AssertionError): position.transact_transaction(Transaction(self.time, self.contract, -1, 100, 0)) with self.assertRaises(AssertionError): position.update_price(110, 120)
def test_position_stats_on_close(self): position = BacktestPosition(self.contract) position.transact_transaction(Transaction(self.time, self.contract, 20, 100, 0)) position.update_price(110, 120) position.transact_transaction(Transaction(self.time, self.contract, -20, 120, 0)) self.assertEqual(position.contract(), self.contract) self.assertEqual(position.quantity(), 0) self.assertEqual(position.current_price, 110) self.assertEqual(position.market_value, 0) self.assertAlmostEqual(position.cost_basis(), 0, places=6) self.assertEqual(position.avg_cost_per_share(), 0) self.assertAlmostEqual(position.unrealised_pnl(), 0, places=6) self.assertEqual(position.realized_pnl(), 20*20)
def test_transact_transaction_split_position(self): portfolio, dh, _ = self.get_portfolio_and_data_handler() contract = self.fut_contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) # Transact two transaction, which will result in transactions splitting quantity_after_first_transaction = 50 quantity_after_second_transaction = -10 initial_price = 200 commission = 7 # Transact the initial transaction transaction_1 = Transaction(self.random_time, contract, quantity_after_first_transaction, initial_price, commission) portfolio.transact_transaction(transaction_1) # Set new prices self.data_handler_prices = self.prices_series new_price = dh.get_last_available_price(ticker) # == 250 portfolio.update() # Transact the second transaction transaction_quantity = -quantity_after_first_transaction + quantity_after_second_transaction transaction_2 = Transaction(self.end_time, contract, transaction_quantity, new_price, commission) portfolio.transact_transaction(transaction_2) portfolio.update() # Compute the pnl of the position, which was closed quantity = max(abs(quantity_after_first_transaction), abs(quantity_after_second_transaction)) quantity *= sign(quantity_after_first_transaction) all_commissions = transaction_1.commission + transaction_2.commission pnl = (new_price - initial_price ) * quantity * contract.contract_size - all_commissions position = list(portfolio.open_positions_dict.values())[0] self.assertEqual(position.quantity(), -10) self.assertEqual(portfolio.current_cash, self.initial_cash + pnl) self.assertEqual(portfolio.net_liquidation, self.initial_cash + pnl) self.assertEqual( portfolio.gross_exposure_of_positions, abs(quantity_after_second_transaction * self.contract_size * new_price)) self.assertEqual(len(portfolio.open_positions_dict), 1)
def test_market_opens_at_much_lower_price_than_it_closed_at_yesterday(self): self._set_bar_for_today(open_price=70.0, high_price=100.0, low_price=68.0, close_price=90.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_ticker, -1, 70.0, 0), Transaction(self.timer.now(), self.msft_ticker, -1, 70.0, 0), ] self.monitor.record_transaction.assert_has_calls(call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls(call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 2) self.assertEqual(self.portfolio.transact_transaction.call_count, 2)
def test_transact_transaction_3(self): portfolio, dh, _ = self.get_portfolio_and_data_handler() contract = self.fut_contract ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract) # First transaction transaction_1 = Transaction(self.random_time, contract, quantity=50, price=200, commission=7) portfolio.transact_transaction(transaction_1) # Set new prices self.data_handler_prices = self.prices_series portfolio.update() # Get the new price of the contract new_price = dh.get_last_available_price(ticker) pnl_1 = (new_price - transaction_1.price) * transaction_1.quantity * transaction_1.contract.contract_size \ - transaction_1.commission cash_move_1 = -transaction_1.commission self.assertEqual(portfolio.net_liquidation, self.initial_cash + pnl_1) self.assertEqual(portfolio.gross_exposure_of_positions, transaction_1.quantity * contract.contract_size * new_price) self.assertEqual(portfolio.current_cash, self.initial_cash + cash_move_1) self.assertEqual(len(portfolio.open_positions_dict), 1) # Second transaction transaction_2 = Transaction(self.random_time, contract, quantity=-20, price=new_price, commission=15) portfolio.transact_transaction(transaction_2) portfolio.update() # Computed the realized pnl, which is already included in the portfolio current cash - it may not be the same as # the pnl of the trade, as the commission trades trade_size = min(abs(transaction_1.quantity), abs(transaction_2.quantity)) realised_pnl = (-1) * transaction_1.price * trade_size * sign(transaction_1.quantity) * contract.contract_size realised_pnl += (-1) * transaction_2.price * trade_size * sign(transaction_2.quantity) * contract.contract_size realised_pnl -= transaction_2.commission # Only transaction2 commission is yet realised self.assertEqual(portfolio.current_cash, self.initial_cash + cash_move_1 + realised_pnl) position = portfolio.open_positions_dict[contract] position_unrealised_pnl = position.unrealised_pnl self.assertEqual(portfolio.net_liquidation, portfolio.current_cash + position_unrealised_pnl) exposure = position.quantity() * contract.contract_size * new_price self.assertEqual(portfolio.gross_exposure_of_positions, position.total_exposure()) self.assertEqual(exposure, position.total_exposure())
def test_portfolio_history(self): # empty portfolio portfolio, dh, timer = self.get_portfolio_and_data_handler() portfolio.update(record=True) # buy contract quantity = 50 price = 250 commission1 = 7 new_time = timer.time + RelativeDelta(days=1) portfolio.transact_transaction( Transaction(new_time, self.fut_contract, quantity, price, commission1)) timer.set_current_time(new_time) self.data_handler_prices = self.prices_series portfolio.update(record=True) # buy another instrument - price goes up portfolio.transact_transaction( Transaction(new_time, self.contract, 20, 120, commission1)) new_time = timer.time + RelativeDelta(days=1) timer.set_current_time(new_time) self.data_handler_prices = self.prices_up portfolio.update(record=True) # sell part of the contract quantity = -30 price = 270 commission2 = 9 new_time = timer.time + RelativeDelta(days=1) portfolio.transact_transaction( Transaction(new_time, self.fut_contract, quantity, price, commission2)) timer.set_current_time(new_time) self.data_handler_prices = self.prices_up portfolio.update(record=True) # price goes down new_time = timer.time + RelativeDelta(days=1) timer.set_current_time(new_time) self.data_handler_prices = self.prices_down portfolio.update(record=True) asset_history = portfolio.positions_history() self.assertEqual(asset_history.shape, (5, 2)) self.assertEqual(asset_history.iloc[4, 0].total_exposure, 315000) self.assertEqual(asset_history.iloc[4, 1].total_exposure, 2000)
def _remove_acquired_or_not_active_positions(self): """ Generate an artificial transaction to address closing a position from portfolio, which was inactive for at least a week (during this time not a single price for the asset was available). The price of this transaction is the last price of the asset recorded by the portfolio and the commission is set to 0, as no real order is created. """ all_tickers_in_portfolio = list( self.portfolio.open_positions_dict.keys()) current_prices_series = self.data_handler.get_last_available_price( tickers=all_tickers_in_portfolio) positions_to_be_removed = [ p for p in self.portfolio.open_positions_dict.values() if np.isnan(current_prices_series[p.ticker()]) ] for position in positions_to_be_removed: closing_transaction = Transaction(self.timer.now(), position.ticker(), -position.quantity(), position.current_price, 0) self.monitor.record_transaction(closing_transaction) self.portfolio.transact_transaction(closing_transaction) self.logger.warning( f"{self.timer.now()}: position assigned to Ticker {position.ticker()} removed due to " f"incomplete price data.")
def test_position_stats_on_close(self): position = BacktestPositionFactory.create_position(self.contract) position.transact_transaction( Transaction(self.start_time, self.contract, 20, 100, 50)) position.update_price(110, 120) closing_transaction = Transaction(self.start_time, self.contract, -20, 120, 50) position.transact_transaction(closing_transaction) self.assertEqual(position.contract(), self.contract) self.assertEqual(position._is_closed, True) self.assertEqual(position.quantity(), 0) self.assertEqual(position.current_price, 110) # set by update_price self.assertEqual(position.direction(), 1) self.assertEqual(position.start_time, self.start_time)
def _parse_transactions_file( self, path_to_transactions_file: str) -> List[Transaction]: """ Parse the Transactions csv file created by the Monitor and generate a list of transactions objects. """ ticker_params_to_ticker = {(ticker.name, ticker.security_type, ticker.point_value): ticker for ticker in self.tickers} def get_matching_ticker(row: QFSeries) -> Ticker: """ Returns the matching specific ticker. In case if the ticker does not belong to the list of tickers passed as the parameter, the transaction is excluded. """ ticker_str = row.loc["Contract symbol"] name = row.loc["Asset Name"] sec_type = SecurityType(row.loc["Security type"]) point_value = row.loc["Contract size"] ticker = ticker_params_to_ticker.get((name, sec_type, point_value), None) if isinstance(ticker, FutureTicker): ticker_type = ticker.supported_ticker_type() ticker = ticker_type(ticker_str, sec_type, point_value) return ticker transactions_df = pd.read_csv(path_to_transactions_file) transactions = [ Transaction(time=pd.to_datetime(row.loc["Timestamp"]), ticker=get_matching_ticker(row), quantity=row.loc["Quantity"], price=row.loc["Price"], commission=row.loc["Commission"]) for _, row in transactions_df.iterrows() ] transactions = [t for t in transactions if t.ticker is not None] return transactions
def _record_trade_and_transaction( self, prev_position_quantity: int, prev_position_avg_price: float, transaction: Transaction): """ Trade is defined as a transaction that goes in the direction of making your position smaller. For example: selling part or entire long position is a trade buying back part or entire short position is a trade buying additional shares of existing long position is NOT a trade """ self.transactions.append(transaction) is_a_trade = sign(transaction.quantity) * sign(prev_position_quantity) == -1 if is_a_trade: time = transaction.time contract = transaction.contract # only the part that goes in the opposite direction is considered as a trade quantity = min([abs(transaction.quantity), abs(prev_position_quantity)]) quantity *= sign(prev_position_quantity) # sign of the position should be preserved entry_price = prev_position_avg_price exit_price = transaction.average_price_including_commission() trade = Trade(time=time, contract=contract, quantity=quantity, entry_price=entry_price, exit_price=exit_price) self.trades.append(trade)
def test_market_value(self): position = BacktestFuturePosition(self.ticker) quantity = 50 price = 100 commission = 5 position.transact_transaction( Transaction(self.random_time, self.ticker, quantity, price, commission)) self.assertEqual(position.market_value(), 0) # before update_price position.update_price(bid_price=100, ask_price=100 + 1) self.assertEqual(position.market_value(), 0) # price did not change yet bid_price = 110 position.update_price(bid_price=bid_price, ask_price=bid_price + 1) market_value = (bid_price - price) * quantity * self.point_value self.assertEqual(position.market_value(), market_value) bid_price = 120 position.update_price(bid_price=bid_price, ask_price=bid_price + 1) market_value = (bid_price - price) * quantity * self.point_value self.assertEqual(position.market_value(), market_value)
def test_transact_transaction_2(self): position = BacktestFuturePosition(self.contract) quantity = -50 price = 100 commission = 5 cash_move = position.transact_transaction(Transaction(self.random_time, self.contract, quantity, price, commission)) self.assertEqual(cash_move, -commission)
def test_transact_transaction_4(self): position = BacktestEquityPosition(self.contract) quantity1 = -50 price1 = 100 commission1 = 5 cash_move1 = position.transact_transaction(Transaction(self.random_time, self.contract, quantity1, price1, commission1)) quantity2 = 30 price2 = 120 commission2 = 7 cash_move2 = position.transact_transaction(Transaction(self.random_time, self.contract, quantity2, price2, commission2)) self.assertEqual(cash_move1, -(quantity1 * price1 + commission1)) self.assertEqual(cash_move2, -(quantity2 * price2 + commission2))
def test_transact_transaction_1(self): position = BacktestEquityPosition(self.contract) quantity = 50 price = 100 commission = 5 cash_move = position.transact_transaction(Transaction(self.random_time, self.contract, quantity, price, commission)) self.assertEqual(cash_move, -(quantity * price + commission))
def test_both_orders_executed_when_both_stop_prices_hit(self): self._set_bar_for_today(open_price=100.0, high_price=110.0, low_price=90.0, close_price=105.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_ticker, -1, self.stop_loss_order_1.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_ticker, -1, self.stop_loss_order_2.execution_style.stop_price, 0), ] self.monitor.record_transaction.assert_has_calls(call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls(call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 2) self.assertEqual(self.portfolio.transact_transaction.call_count, 2)
def test_transact_transaction_4(self): position = BacktestFuturePosition(self.contract) quantity1 = -50 price1 = 100 commission1 = 5 cash_move1 = position.transact_transaction(Transaction(self.random_time, self.contract, quantity1, price1, commission1)) self.assertEqual(cash_move1, -commission1) quantity2 = 30 price2 = 120 commission2 = 7 cash_move2 = position.transact_transaction(Transaction(self.random_time, self.contract, quantity2, price2, commission2)) expected_move = (price2 - price1) * (-quantity2) * self.contract_size - commission2 self.assertEqual(cash_move2, expected_move)
def test_without_commission_position(self): position = BacktestPositionFactory.create_position(self.contract) transactions = [ Transaction(self.start_time, self.contract, 50, 100, 0), Transaction(self.start_time, self.contract, 30, 120, 0), Transaction(self.start_time, self.contract, -20, 175, 0), Transaction(self.start_time, self.contract, -30, 160, 0), Transaction(self.start_time, self.contract, 10, 150, 0), Transaction(self.start_time, self.contract, 10, 170, 0), Transaction(self.start_time, self.contract, -20, 150, 0) ] for transaction in transactions: position.transact_transaction(transaction) current_price = 110 position.update_price(current_price, 120) self.assertEqual(position.contract(), self.contract) position_quantity = sum(t.quantity for t in transactions) self.assertEqual(position.quantity(), position_quantity) self.assertEqual(position.current_price, current_price)
def test_position(self): position = BacktestPositionFactory.create_position(self.contract) transactions = (Transaction(self.start_time, self.contract, 50, 100, 5), Transaction(self.start_time, self.contract, 30, 120, 3), Transaction(self.start_time, self.contract, -20, 175, 2), Transaction(self.start_time, self.contract, -30, 160, 1), Transaction(self.start_time, self.contract, 10, 150, 7), Transaction(self.start_time, self.contract, 10, 170, 4), Transaction(self.start_time, self.contract, -20, 150, 5)) for transaction in transactions: position.transact_transaction(transaction) position.update_price(110, 120) self.assertEqual(position.contract(), self.contract) self.assertEqual(position.quantity(), 30) self.assertEqual(position.current_price, 110)
def test_closed_position(self): position = BacktestPositionFactory.create_position(self.contract) position.transact_transaction( Transaction(self.start_time, self.contract, 20, 100, 0)) position.transact_transaction( Transaction(self.start_time, self.contract, -20, 100, 0)) with self.assertRaises(AssertionError): # position is now closed and should not accept new transactions position.transact_transaction( Transaction(self.start_time, self.contract, 1, 100, 0)) with self.assertRaises(AssertionError): # position is now closed and should not accept new transactions position.transact_transaction( Transaction(self.start_time, self.contract, -1, 100, 0)) with self.assertRaises(AssertionError): # position is now closed position.update_price(110, 120)