def _from_ib_trade(self, ib_trade: _IBTrade) -> Trade: contract = self._from_ib_contract(ib_contract=ib_trade.contract) order = self._from_ib_order(ib_order=ib_trade.order) status = self._from_ib_status(ib_order_status=ib_trade.orderStatus) trade = Trade(contract=contract, order=order, status=status) return trade
def test_subscribe_to_position_updates(): con = StockContract(symbol="SPY") pos = Position( account=DEFAULT_SIM_ACC, contract=con, position=10, ave_fill_price=345.6, ) starting_positions = { DEFAULT_SIM_ACC: {con: pos}, } untethered_sim_broker = SimulationBroker( sim_streamer=SimulationDataStreamer(), starting_funds={Currency.USD: 10_000}, transaction_cost=1, starting_positions=starting_positions, ) pos_updates = [] def pos_updates_receiver(pos_: Position): pos_updates.append(pos_) untethered_sim_broker.subscribe_to_position_updates( func=pos_updates_receiver, ) sim_order = LimitOrder(action=OrderAction.BUY, quantity=2, limit_price=346) sim_trade = Trade(contract=con, order=sim_order) untethered_sim_broker.place_trade(trade=sim_trade) untethered_sim_broker.simulate_trade_execution( trade=sim_trade, price=345.8, n_shares=1, ) untethered_sim_broker.step() assert len(pos_updates) == 1 updated_pos: Position = pos_updates[0] target_ave_price = (10 * 345.6 + 345.8) / 11 assert updated_pos.contract == con assert updated_pos.position == 11 assert updated_pos.ave_fill_price == target_ave_price assert updated_pos.account == DEFAULT_SIM_ACC untethered_sim_broker.simulate_trade_execution( trade=sim_trade, price=345.9, n_shares=1, ) untethered_sim_broker.step() updated_pos: Position = pos_updates[1] target_ave_price = (target_ave_price * 11 + 345.9) / 12 assert updated_pos.contract == con assert updated_pos.position == 12 assert updated_pos.ave_fill_price == target_ave_price assert updated_pos.account == DEFAULT_SIM_ACC
def get_1_spy_mkt_trade(buy: bool) -> Trade: contract = StockContract(symbol="SPY") if buy: action = OrderAction.BUY else: action = OrderAction.SELL order = MarketOrder(action=action, quantity=1) trade = Trade(contract=contract, order=order) return trade
def _execute_trade( self, trade: Trade, price: Optional[float] = None, n_shares: Optional[float] = None, ): trade_idx = self._placed_trades.index(trade) trade = self._placed_trades[trade_idx] self._validate_trade_execution( trade=trade, price=price, n_shares=n_shares, ) contract = trade.contract order = trade.order filled = trade.status.filled if n_shares is None: n_shares = order.quantity - filled if price is None: price = self._get_current_price(contract=contract) if order.action == OrderAction.BUY: funds_delta = -(n_shares * price + self.get_transaction_fee()) shares_delta = n_shares else: funds_delta = n_shares * price + self.get_transaction_fee() shares_delta = -n_shares self.acc_cash[contract.currency] += funds_delta self._add_to_position( contract=contract, n_shares=shares_delta, fill_price=price, ) filled = trade.status.filled new_filled = filled + n_shares ave_price = trade.status.ave_fill_price new_ave_price = (ave_price * filled + price * n_shares) / new_filled new_status = TradeStatus( state=TradeState.FILLED, filled=new_filled, remaining=order.quantity - new_filled, ave_fill_price=new_ave_price, order_id=order.order_id, ) trade.status = new_status self._update_trade_updates_subscribers(trade=trade)
def cancel_trade(self, trade: Trade): # TODO: test cancelled_status = TradeStatus( state=TradeState.CANCELLED, filled=trade.status.filled, remaining=trade.status.remaining, ave_fill_price=trade.status.ave_fill_price, order_id=trade.status.order_id, ) new_trade = Trade( contract=trade.contract, order=trade.order, status=cancelled_status, ) trade_idx = self._placed_trades.index(trade) self._placed_trades[trade_idx] = new_trade self._update_trade_updates_subscribers(trade=trade)
def test_simulation_broker_limit_order(sim_broker_runner_and_streamer_15m): broker, runner, _ = sim_broker_runner_and_streamer_15m assert len(broker.open_trades) == 0 contract = StockContract(symbol="SPY") order = LimitOrder(action=OrderAction.SELL, quantity=2, limit_price=99,) trade = Trade(contract=contract, order=order) runner.run_sim(step_count=1) _, trade = broker.place_trade(trade=trade, order=order) runner.run_sim(step_count=1) assert trade.status.state == TradeState.SUBMITTED broker.simulate_trade_execution( trade=trade, price=100, n_shares=1, ) # does not update without sim step assert trade.status.state == TradeState.SUBMITTED runner.run_sim(step_count=1) assert trade.status.state == TradeState.FILLED assert trade.status.filled == 1 assert trade.status.remaining == 1 broker.simulate_trade_execution( trade=trade, price=100, n_shares=1, ) runner.run_sim(step_count=1) assert trade.status.state == TradeState.FILLED assert trade.status.filled == 2 assert trade.status.remaining == 0
def place_trade( self, trade: Trade, *args, trade_execution_price: Optional[float] = None, trade_execution_size: Optional[float] = None, **kwargs, ) -> Tuple[bool, Trade]: trade_id = self._get_increment_valid_id() order = trade.order order._order_id = trade_id new_status = TradeStatus( state=TradeState.SUBMITTED, filled=0, remaining=order.quantity, ave_fill_price=0, order_id=trade_id, ) new_trade = Trade( contract=trade.contract, order=order, status=new_status, ) self._placed_trades.append(new_trade) self._update_trade_updates_subscribers(trade=new_trade) if isinstance(trade.order, MarketOrder): self._scheduled_trade_executions.append( (trade, trade_execution_price, trade_execution_size)) # update trade placed subscribers for func, fn_kwargs in self._new_trade_subscribers: func(trade, **fn_kwargs) return True, new_trade