Skip to content

Commit

Permalink
[PY] v0.6.2: Update python client code to place Short-Term orders (#32
Browse files Browse the repository at this point in the history
)
  • Loading branch information
lucas-dydx authored Sep 19, 2023
1 parent 39b3639 commit 555564d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 17 deletions.
21 changes: 10 additions & 11 deletions v4-client-py/examples/composite_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,25 @@ async def main() -> None:
subaccount = Subaccount(wallet, 0)
ordersParams = loadJson('human_readable_orders.json')
for orderParams in ordersParams:
type = OrderType[orderParams["type"]]
side = OrderSide[orderParams["side"]]
time_in_force_string = orderParams.get("timeInForce", "GTT")
time_in_force = OrderTimeInForce[time_in_force_string]

# Get the expiration block.
current_block = client.get_current_block()
next_valid_block_height = current_block + 1
# Note, you can change this to any number between `next_valid_block_height` to `next_valid_block_height + SHORT_BLOCK_WINDOW`
good_til_block = next_valid_block_height + 3

price = orderParams.get("price", 1350)
time_in_force_seconds = 60 if time_in_force == OrderTimeInForce.GTT else 0
post_only = orderParams.get("postOnly", False)
try:
tx = client.place_order(
tx = client.place_short_term_order(
subaccount,
market='ETH-USD',
type=type,
side=side,
price=price,
size=0.01,
client_id=randrange(0, 100000000),
time_in_force=time_in_force,
good_til_time_in_seconds=time_in_force_seconds,
execution=OrderExecution.DEFAULT,
post_only=post_only,
good_til_block=good_til_block,
time_in_force=OrderExecution.DEFAULT,
reduce_only=False
)
print('**Order Tx**')
Expand Down
2 changes: 1 addition & 1 deletion v4-client-py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package-dir = {"" = "v4_client_py"}

[tool.poetry]
name = "v4-client-py"
version = "0.6.1"
version = "0.6.2"
description = "dYdX v4 Client"
authors = ["John Huang <contact@dydx.exchange>"]
license = "BSL-1.1"
Expand Down
125 changes: 122 additions & 3 deletions v4-client-py/v4_client_py/clients/dydx_composite_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ORDER_FLAGS_SHORT_TERM,
ORDER_FLAGS_LONG_TERM,
ORDER_FLAGS_CONDITIONAL,
SHORT_BLOCK_WINDOW,
)

from v4_client_py.clients.constants import Network
Expand All @@ -42,13 +43,25 @@ def __init__(
def calculate_good_til_block(self) -> int:
response = self.validator_client.get.latest_block()
return response.block.header.height + 3

def calculate_good_til_block_time(self, good_til_time_in_seconds: int) -> int:
now = datetime.now()
interval = timedelta(seconds=good_til_time_in_seconds)
future = now + interval
return int(future.timestamp())

def validate_good_til_block(self, good_til_block: int) -> None:
response = self.validator_client.get.latest_block()
next_valid_block_height = response.block.header.height + 1
lower_bound = next_valid_block_height
upper_bound = next_valid_block_height + SHORT_BLOCK_WINDOW
if good_til_block < lower_bound or good_til_block > upper_bound:
raise Exception(
f"Invalid Short-Term order GoodTilBlock. "
f"Should be greater-than-or-equal-to {lower_bound} "
f"and less-than-or-equal-to {upper_bound}. "
f"Provided good til block: {good_til_block}"
)

# Only MARKET and LIMIT types are supported right now
# Use human readable form of input, including price and size
Expand Down Expand Up @@ -124,6 +137,66 @@ def place_order(
trigger_price=trigger_price,
)
return self.validator_client.post.send_message(subaccount=subaccount, msg=msg, zeroFee=True)

def place_short_term_order(
self,
subaccount: Subaccount,
market: str,
side: OrderSide,
price: float,
size: float,
client_id: int,
good_til_block: int,
execution: OrderExecution,
reduce_only: bool,
trigger_price: float = None,
) -> SubmittedTx:
'''
Place Short-Term order
:param subaccount: required
:type subaccount: Subaccount
:param market: required
:type market: str
:param side: required
:type side: Order.Side
:param price: required
:type price: float
:param size: required
:type size: float
:param client_id: required
:type client_id: int
:param good_til_block: required
:type good_til_block: int
:param execution: required
:type execution: OrderExecution
:param reduce_only: required
:type reduce_only: bool
:returns: Tx information
'''
msg = self.place_short_term_order_message(
subaccount=subaccount,
market=market,
type=type,
side=side,
price=price,
size=size,
client_id=client_id,
good_til_block=good_til_block,
execution=execution,
reduce_only=reduce_only,
trigger_price=trigger_price,
)
return self.validator_client.post.send_message(subaccount=subaccount, msg=msg, zeroFee=True)

def calculate_client_metadata(self, order_type: OrderType) -> int:
'''
Expand Down Expand Up @@ -249,7 +322,53 @@ def place_order_message(
condition_type=condition_type,
conditional_order_trigger_subticks=conditional_order_trigger_subticks,
)


def place_short_term_order_message(
self,
subaccount: Subaccount,
market: str,
type: OrderType,
side: OrderSide,
price: float,
size: float,
client_id: int,
time_in_force: OrderExecution,
good_til_block: int,
reduce_only: bool,
) -> MsgPlaceOrder:
# Validate the GoodTilBlock.
self.validate_good_til_block(good_til_block=good_til_block)

# Construct the MsgPlaceOrder.
markets_response = self.indexer_client.markets.get_perpetual_markets(market)
market = markets_response.data['markets'][market]
clob_pair_id = market['clobPairId']
atomic_resolution = market['atomicResolution']
step_base_quantums = market['stepBaseQuantums']
quantum_conversion_exponent = market['quantumConversionExponent']
subticks_per_tick = market['subticksPerTick']
order_side = calculate_side(side)
quantums = calculate_quantums(size, atomic_resolution, step_base_quantums)
subticks = calculate_subticks(price, atomic_resolution, quantum_conversion_exponent, subticks_per_tick)
order_flags = ORDER_FLAGS_SHORT_TERM
client_metadata = self.calculate_client_metadata(type)
return self.validator_client.post.composer.compose_msg_place_order(
address=subaccount.address,
subaccount_number=subaccount.subaccount_number,
client_id=client_id,
clob_pair_id=clob_pair_id,
order_flags=order_flags,
good_til_block=good_til_block,
good_til_block_time=0,
side=order_side,
quantums=quantums,
subticks=subticks,
time_in_force=time_in_force,
reduce_only=reduce_only,
client_metadata=client_metadata,
condition_type=Order.CONDITION_TYPE_UNSPECIFIED,
conditional_order_trigger_subticks=0,
)

def cancel_order(
self,
Expand Down Expand Up @@ -367,4 +486,4 @@ def withdraw_from_subaccount(
subaccount=subaccount,
asset_id=0,
quantums=amount * 10 ** (- QUOTE_QUANTUMS_ATOMIC_RESOLUTION),
)
)
6 changes: 4 additions & 2 deletions v4-client-py/v4_client_py/clients/helpers/chain_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ class OrderTimeInForce(Flag):
FOK = auto() # Fill or Kill

class OrderExecution(Flag):
DEFAULT = auto() # Default, STOP_LIMIT and TAKE_PROFIT_LIMIT
POST_ONLY = auto() # Post-only, STOP_LIMIT and TAKE_PROFIT_LIMIT
DEFAULT = 0 # Default. Note proto enums start at 0, which is why this start at 0.
IOC = auto() # Immediate or Cancel
POST_ONLY = auto() # Post-only
FOK = auto() # Fill or Kill

ORDER_FLAGS_SHORT_TERM = 0
ORDER_FLAGS_LONG_TERM = 64
ORDER_FLAGS_CONDITIONAL = 32

SHORT_BLOCK_WINDOW = 20

QUOTE_QUANTUMS_ATOMIC_RESOLUTION = -6


Expand Down

0 comments on commit 555564d

Please sign in to comment.