Skip to content

PrivateNode

Signed trading actions exposed by the dYdX node client wrapper.

connect

Source code in pkg/src/dydx/node/core.py
@classmethod
async def connect(
  cls, mnemonic: str | None = None, *, url: str = OEGS_GRPC_URL,
  rest_indexer: str = INDEXER_HTTP_URL,
  websocket_indexer: str = INDEXER_WS_URL,
):
  if mnemonic is None:
    mnemonic = os.environ['DYDX_MNEMONIC']
  pair = KeyPair.from_mnemonic(mnemonic)
  address = Wallet(pair, 0, 0).address
  config = make_mainnet( 
    node_url=url,
    rest_indexer=rest_indexer, 
    websocket_indexer=websocket_indexer, 
  ).node
  node: NodeClient = await NodeClient.connect(config)
  wallet = await Wallet.from_mnemonic(node, mnemonic, address)
  return cls(node_client=node, wallet=wallet, address=address)

build_order

Source code in pkg/src/dydx/node/private/place_order.py
def build_order(
  self, *, market: PerpetualMarket, order: Order,
  good_til_block: int | None = None, good_til_block_time: int | None = None,
  subaccount: int = 0,
):
  mkt = Market(market) # type: ignore
  client_id = order.get('client_id') or rand_id()
  order_id = mkt.order_id(
    address=self.address, subaccount_number=subaccount,
    client_id=client_id, order_flags=parse_flags(order['flags']),
  )
  return OrderProto(
    order_id=order_id,
    side=parse_side(order['side']),
    quantums=mkt.calculate_quantums(float(order['size'])),
    subticks=mkt.calculate_subticks(float(order['price'])),
    good_til_block=good_til_block,
    good_til_block_time=good_til_block_time,
    time_in_force=parse_tif(order.get('time_in_force')),
    reduce_only=order.get('reduce_only') or False,
    client_metadata=order.get('client_metadata'),
    condition_type=order.get('condition_type'),
    conditional_order_trigger_subticks=order.get('conditional_order_trigger_subticks'),
  )

build_order_now

Source code in pkg/src/dydx/node/private/place_order.py
async def build_order_now(self, market: PerpetualMarket, order: Order, *, subaccount: int = 0):
  if (gtb := order.get('good_til_block')) is None and order['flags'] == 'SHORT_TERM':
    latest_block = await self.node_client.latest_block()
    gtb = latest_block.block.header.height + SHORT_BLOCK_WINDOW

  if (gtbt := order.get('good_til_block_time')) is None and order['flags'] == 'LONG_TERM':
    latest_block = await self.node_client.latest_block()
    gtbt = latest_block.block.header.time.seconds + STATEFUL_ORDER_TIME_WINDOW
    gtb = None

  return self.build_order(market=market, order=order, good_til_block=gtb, good_til_block_time=gtbt, subaccount=subaccount)

place_order

Place an order.

  • market: market to place the order on
  • order: order to place
  • subaccount: subaccount to place the order on

dYdX API docs

Source code in pkg/src/dydx/node/private/place_order.py
async def place_order(
  self, market: PerpetualMarket, order: Order, *, subaccount: int = 0,
  mode: BroadcastMode = BroadcastMode.BROADCAST_MODE_SYNC, tx_options: TxOptions | None = None,
) -> OrderResponse:
  """Place an order.

  - `market`: market to place the order on
  - `order`: order to place
  - `subaccount`: subaccount to place the order on

  > [dYdX API docs](https://docs.dydx.xyz/node-client/private#place-order)
  """
  order_proto = await self.build_order_now(market, order, subaccount=subaccount)
  tx: BroadcastTxResponse = await self.node_client.broadcast_message(
    self.wallet, MsgPlaceOrder(order=order_proto), # type: ignore
    mode=mode, tx_options=tx_options
  )
  if tx.tx_response.code != 0:
    raise ApiError(tx.tx_response.code, tx.tx_response, tx.tx_response.raw_log)
  return {
    'tx': tx,
    'order': order_proto,
  }

cancel_order

Cancel an order.

  • order_id: order to cancel
  • good_til_block: block number to cancel the order at (defaults to the current one)
  • good_til_block_time: timestamp to cancel the order at (defaults to now)

dYdX API docs

Source code in pkg/src/dydx/node/private/cancel_order.py
async def cancel_order(
  self, order_id: OrderId, *,
  good_til_block: int | None = None,
  good_til_block_time: int | None = None,
  mode: BroadcastMode = BroadcastMode.BROADCAST_MODE_SYNC,
  tx_options: TxOptions | None = None,
) -> BroadcastTxResponse:
  """Cancel an order.

  - `order_id`: order to cancel
  - `good_til_block`: block number to cancel the order at (defaults to the current one)
  - `good_til_block_time`: timestamp to cancel the order at (defaults to now)

  > [dYdX API docs](https://docs.dydx.xyz/node-client/private#cancel-order)
  """
  # SHORT-TERM orders required good_til_block
  if good_til_block is None and order_id.order_flags == OrderFlags.SHORT_TERM:
    latest_block = await self.node_client.latest_block()
    good_til_block = latest_block.block.header.height + SHORT_BLOCK_WINDOW
  # LONG-TERM orders required good_til_block_time
  elif good_til_block_time is None and order_id.order_flags == OrderFlags.LONG_TERM:
    latest_block = await self.node_client.latest_block()
    good_til_block_time = latest_block.block.header.time.seconds + STATEFUL_ORDER_TIME_WINDOW

  tx = MsgCancelOrder(order_id=order_id, good_til_block=good_til_block, good_til_block_time=good_til_block_time)
  r: BroadcastTxResponse = await self.node_client.broadcast_message(
    self.wallet, tx, mode=mode, tx_options=tx_options # type: ignore
  )
  if r.tx_response.code != 0:
    raise ApiError(r.tx_response.code, r.tx_response)
  return r

batch_cancel_orders

Cancel an order.

  • order_ids: orders to cancel
  • good_til_block: block number to cancel the orders at (defaults to the current one)

dYdX API docs

Source code in pkg/src/dydx/node/private/batch_cancel_orders.py
async def batch_cancel_orders(
  self, order_ids: Sequence[OrderId], *,
  good_til_block: int | None = None,
) -> BroadcastTxResponse:
  """Cancel an order.

  - `order_ids`: orders to cancel
  - `good_til_block`: block number to cancel the orders at (defaults to the current one)

  > [dYdX API docs](https://docs.dydx.xyz/node-client/private#cancel-order)
  """
  subaccount_id = order_ids[0].subaccount_id
  orders_by_pair = defaultdict[int, list[int]](list)
  for order_id in order_ids:
    if order_id.order_flags != OrderFlags.SHORT_TERM:
      raise UserError('Only short-term orders can be cancelled in a batch')
    orders_by_pair[order_id.clob_pair_id].append(order_id.client_id)

  batches = [
    OrderBatch(clob_pair_id=pair, client_ids=orders)
    for pair, orders in orders_by_pair.items()
  ]

  if good_til_block is None:
    latest_block = await self.node_client.latest_block()
    good_til_block = latest_block.block.header.height + SHORT_BLOCK_WINDOW

  r: BroadcastTxResponse = await self.node_client.batch_cancel_orders(
    wallet=self.wallet, subaccount_id=subaccount_id,
    short_term_cancels=batches,
    good_til_block=good_til_block,
  )
  if r.tx_response.code != 0:
    raise ApiError(r.tx_response.code, r.tx_response)
  return r