Skip to content

Commit

Permalink
[PY] [CLOB-895] Python short term order explicit TIF enum + examples (#…
Browse files Browse the repository at this point in the history
…40)

* clean up short term python example

* vers bump

* update short term example
  • Loading branch information
jonfung-dydx authored Sep 21, 2023
1 parent d6709f3 commit fea7694
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 16 deletions.
23 changes: 12 additions & 11 deletions v4-client-py/examples/composite_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,26 @@ 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"]]

# 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

time_in_force_string = orderParams.get("timeInForce", "GTT")
time_in_force = OrderTimeInForce[time_in_force_string]
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_short_term_order(
tx = client.place_order(
subaccount,
market='ETH-USD',
type=type,
side=side,
price=price,
size=0.01,
client_id=randrange(0, 100000000),
good_til_block=good_til_block,
time_in_force=OrderExecution.DEFAULT,
time_in_force=time_in_force,
good_til_time_in_seconds=time_in_force_seconds,
execution=OrderExecution.DEFAULT,
post_only=post_only,
reduce_only=False
)
print('**Order Tx**')
Expand Down Expand Up @@ -82,4 +83,4 @@ async def main() -> None:
print(str(error))
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_until_complete(main())
42 changes: 42 additions & 0 deletions v4-client-py/examples/human_readable_short_term_orders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"timeInForce": "DEFAULT",
"side": "BUY",
"price": 40000
},
{
"timeInForce": "DEFAULT",
"side": "SELL",
"price": 1000
},
{
"timeInForce": "FOK",
"side": "BUY",
"price": 1000
},
{
"timeInForce": "FOK",
"side": "SELL",
"price": 40000
},
{
"timeInForce": "IOC",
"side": "BUY",
"price": 40000
},
{
"timeInForce": "IOC",
"side": "SELL",
"price": 1000
},
{
"timeInForce": "POST_ONLY",
"side": "BUY",
"price": 1000
},
{
"timeInForce": "POST_ONLY",
"side": "SELL",
"price": 40000
}
]
81 changes: 81 additions & 0 deletions v4-client-py/examples/short_term_composite_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'''Example for trading with human readable numbers
Usage: python -m examples.composite_example
'''
import asyncio
import logging
from random import randrange
from v4_client_py.chain.aerial.wallet import LocalWallet
from v4_client_py.clients import CompositeClient, Subaccount
from v4_client_py.clients.constants import BECH32_PREFIX, Network

from v4_client_py.clients.helpers.chain_helpers import (
Order_TimeInForce,
OrderType,
OrderSide,
OrderTimeInForce,
OrderExecution,
)
from examples.utils import loadJson

from tests.constants import DYDX_TEST_MNEMONIC


async def main() -> None:
wallet = LocalWallet.from_mnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX)
network = Network.staging()
client = CompositeClient(
network,
)
subaccount = Subaccount(wallet, 0)
ordersParams = loadJson('human_readable_short_term_orders.json')
for orderParams in ordersParams:
side = OrderSide[orderParams["side"]]

# 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

time_in_force = orderExecutionToTimeInForce(orderParams['timeInForce'])

price = orderParams.get("price", 1350)
# uint32
client_id = randrange(0, 2 ** 32 - 1)

try:
tx = client.place_short_term_order(
subaccount,
market='ETH-USD',
side=side,
price=price,
size=0.01,
client_id=client_id,
good_til_block=good_til_block,
time_in_force=time_in_force,
reduce_only=False
)
print('**Order Tx**')
print(tx.tx_hash)
except Exception as error:
print('**Order Failed**')
print(str(error))

await asyncio.sleep(5) # wait for placeOrder to complete

def orderExecutionToTimeInForce(orderExecution: str) -> Order_TimeInForce:
if orderExecution == "DEFAULT":
return Order_TimeInForce.TIME_IN_FORCE_UNSPECIFIED
elif orderExecution == "FOK":
return Order_TimeInForce.TIME_IN_FORCE_FILL_OR_KILL
elif orderExecution == "IOC":
return Order_TimeInForce.TIME_IN_FORCE_IOC
elif orderExecution == "POST_ONLY":
return Order_TimeInForce.TIME_IN_FORCE_POST_ONLY
else:
raise ValueError('Unrecognized order execution')

if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.get_event_loop().run_until_complete(main())
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.6"
version = "0.6.7"
description = "dYdX v4 Client"
authors = ["John Huang <contact@dydx.exchange>"]
license = "BSL-1.1"
Expand Down
5 changes: 3 additions & 2 deletions v4-client-py/v4_client_py/clients/dydx_composite_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from v4_client_py.clients.helpers.chain_helpers import (
QUOTE_QUANTUMS_ATOMIC_RESOLUTION,
Order,
Order_TimeInForce,
OrderType,
OrderSide,
OrderTimeInForce,
Expand Down Expand Up @@ -151,7 +152,7 @@ def place_short_term_order(
size: float,
client_id: int,
good_til_block: int,
time_in_force: OrderExecution,
time_in_force: Order_TimeInForce,
reduce_only: bool,
) -> SubmittedTx:
'''
Expand Down Expand Up @@ -334,7 +335,7 @@ def place_short_term_order_message(
price: float,
size: float,
client_id: int,
time_in_force: OrderExecution,
time_in_force: Order_TimeInForce,
good_til_block: int,
reduce_only: bool,
) -> MsgPlaceOrder:
Expand Down
39 changes: 37 additions & 2 deletions v4-client-py/v4_client_py/clients/helpers/chain_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

from enum import Flag, auto
from enum import Flag, auto, Enum
from v4_proto.dydxprotocol.clob.order_pb2 import Order

class OrderType(Flag):
Expand All @@ -13,7 +13,8 @@ class OrderType(Flag):
class OrderSide(Flag):
BUY = auto()
SELL = auto()


# FE enums. Do not pass these directly into the order proto TimeInForce field.
class OrderTimeInForce(Flag):
GTT = auto() # Good Til Time
IOC = auto() # Immediate or Cancel
Expand All @@ -25,6 +26,40 @@ class OrderExecution(Flag):
POST_ONLY = auto() # Post-only
FOK = auto() # Fill or Kill

# Enums to use in order proto fields. Use proto generated fields once that's fixed.
# should match https://github.com/dydxprotocol/v4-chain/blob/main/proto/dydxprotocol/clob/order.proto#L159
class Order_TimeInForce(Flag):

'''
TIME_IN_FORCE_UNSPECIFIED - TIME_IN_FORCE_UNSPECIFIED represents the default behavior where an
order will first match with existing orders on the book, and any
remaining size will be added to the book as a maker order.
'''
TIME_IN_FORCE_UNSPECIFIED = 0

'''
TIME_IN_FORCE_IOC - TIME_IN_FORCE_IOC enforces that an order only be matched with
maker orders on the book. If the order has remaining size after
matching with existing orders on the book, the remaining size
is not placed on the book.
'''
TIME_IN_FORCE_IOC = 1

'''
TIME_IN_FORCE_POST_ONLY - TIME_IN_FORCE_POST_ONLY enforces that an order only be placed
on the book as a maker order. Note this means that validators will cancel
any newly-placed post only orders that would cross with other maker
orders.
'''
TIME_IN_FORCE_POST_ONLY = 2

'''
TIME_IN_FORCE_FILL_OR_KILL - TIME_IN_FORCE_FILL_OR_KILL enforces that an order will either be filled
completely and immediately by maker orders on the book or canceled if the
entire amount can‘t be matched.
'''
TIME_IN_FORCE_FILL_OR_KILL = 3

ORDER_FLAGS_SHORT_TERM = 0
ORDER_FLAGS_LONG_TERM = 64
ORDER_FLAGS_CONDITIONAL = 32
Expand Down

0 comments on commit fea7694

Please sign in to comment.