Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/create_market_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
logging.basicConfig(level=logging.DEBUG)

# The API_KEY_PRIVATE_KEY provided belongs to a dummy account registered on Testnet.
# It was generated using the setup_system.py script, and servers as an example.
# It was generated using the setup_system.py script, and serves as an example.
BASE_URL = "https://testnet.zklighter.elliot.ai"
API_KEY_PRIVATE_KEY = "0xed636277f3753b6c0275f7a28c2678a7f3a95655e09deaebec15179b50c5da7f903152e50f594f7b"
ACCOUNT_INDEX = 65
API_KEY_INDEX = 1
API_KEY_INDEX = 3


def trim_exception(e: Exception) -> str:
Expand All @@ -28,7 +28,7 @@ async def main():
market_index=0,
client_order_index=0,
base_amount=1000, # 0.1 ETH
avg_execution_price=170000, # $1700
avg_execution_price=170000, # $1700 -- worst acceptable price for the order
is_ask=True,
)
print("Create Order Tx:", tx)
Expand Down
36 changes: 36 additions & 0 deletions examples/create_market_order_max_slippage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import asyncio
import logging
import lighter

logging.basicConfig(level=logging.DEBUG)

# The API_KEY_PRIVATE_KEY provided belongs to a dummy account registered on Testnet.
# It was generated using the setup_system.py script, and serves as an example.
BASE_URL = "https://testnet.zklighter.elliot.ai"
API_KEY_PRIVATE_KEY = "0xe0fa55e11d6b5575d54c0500bd2f3b240221ae90241e3b573f2307e27de20c04ea628de3f1936e56"
ACCOUNT_INDEX = 22
API_KEY_INDEX = 3


def trim_exception(e: Exception) -> str:
return str(e).strip().split("\n")[-1]


async def main():
client = lighter.SignerClient(
url=BASE_URL,
private_key=API_KEY_PRIVATE_KEY,
account_index=ACCOUNT_INDEX,
api_key_index=API_KEY_INDEX,
)

# tx = await client.create_market_order_limited_slippage(market_index=0, client_order_index=0, base_amount=30000000,
# max_slippage=0.001, is_ask=True)
tx = await client.create_market_order_if_slippage(market_index=0, client_order_index=0, base_amount=30000000,
max_slippage=0.01, is_ask=True, ideal_price=300000)
print("Create Order Tx:", tx)
await client.close()


if __name__ == "__main__":
asyncio.run(main())
83 changes: 83 additions & 0 deletions lighter/signer_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def __init__(
self.signer = _initialize_signer()
self.api_client = lighter.ApiClient(configuration=Configuration(host=url))
self.tx_api = lighter.TransactionApi(self.api_client)
self.order_api = lighter.OrderApi(self.api_client)
self.nonce_manager = nonce_manager.nonce_manager_factory(
nonce_manager_type=nonce_management_type,
account_index=account_index,
Expand Down Expand Up @@ -618,6 +619,88 @@ async def create_market_order(
api_key_index=api_key_index,
)

# will only do the amount such that the slippage is limited to the value provided
async def create_market_order_limited_slippage(
self,
market_index,
client_order_index,
base_amount,
max_slippage,
is_ask,
reduce_only: bool = False,
nonce=-1,
api_key_index=-1,
ideal_price=None
) -> (CreateOrder, TxHash, str):
if ideal_price is None:
order_book_orders = await self.order_api.order_book_orders(market_index, 1)
logging.debug("Create market order limited slippage is doing an API call to get the current ideal price. You can also provide it yourself to avoid this.")
ideal_price = int((order_book_orders.bids[0].price if is_ask else order_book_orders.asks[0].price).replace(".", ""))

acceptable_execution_price = round(ideal_price * (1 + max_slippage * (-1 if is_ask else 1)))
return await self.create_order(
market_index,
client_order_index,
base_amount,
price=acceptable_execution_price,
is_ask=is_ask,
order_type=self.ORDER_TYPE_MARKET,
time_in_force=self.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL,
order_expiry=self.DEFAULT_IOC_EXPIRY,
reduce_only=reduce_only,
nonce=nonce,
api_key_index=api_key_index,
)

# will only execute the order if it executes with slippage <= max_slippage
async def create_market_order_if_slippage(
self,
market_index,
client_order_index,
base_amount,
max_slippage,
is_ask,
reduce_only: bool = False,
nonce=-1,
api_key_index=-1,
ideal_price=None
) -> (CreateOrder, TxHash, str):
order_book_orders = await self.order_api.order_book_orders(market_index, 100)
if ideal_price is None:
ideal_price = int((order_book_orders.bids[0].price if is_ask else order_book_orders.asks[0].price).replace(".", ""))

matched_usd_amount, matched_size = 0, 0
for order_book_order in (order_book_orders.bids if is_ask else order_book_orders.asks):
if matched_size == base_amount:
break
curr_order_price = int(order_book_order.price.replace(".", ""))
curr_order_size = int(order_book_order.remaining_base_amount.replace(".", ""))
to_be_used_order_size = min(base_amount - matched_size, curr_order_size)
matched_usd_amount += curr_order_price * to_be_used_order_size
matched_size += to_be_used_order_size

potential_execution_price = matched_usd_amount / matched_size
acceptable_execution_price = ideal_price * (1 + max_slippage * (-1 if is_ask else 1))
if (is_ask and potential_execution_price < acceptable_execution_price) or (not is_ask and potential_execution_price > acceptable_execution_price):
return None, None, "Excessive slippage"

if matched_size < base_amount:
return None, None, "Cannot be sure slippage will be acceptable due to the high size"

return await self.create_order(
market_index,
client_order_index,
base_amount,
price=round(acceptable_execution_price),
is_ask=is_ask,
order_type=self.ORDER_TYPE_MARKET,
time_in_force=self.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL,
order_expiry=self.DEFAULT_IOC_EXPIRY,
reduce_only=reduce_only,
nonce=nonce,
api_key_index=api_key_index,
)

@process_api_key_and_nonce
async def cancel_order(self, market_index, order_index, nonce=-1, api_key_index=-1) -> (CancelOrder, TxHash, str):
tx_info, error = self.sign_cancel_order(market_index, order_index, nonce)
Expand Down