Skip to content

Commit c3cecf5

Browse files
bvvvp009alexvelea
andauthored
Update toPython SDK to match lighter-go refactored CGO binaries (#85)
* Update Python SDK to match upstream lighter-go refactored CGO binaries - Added SignedTxResponse struct for new Go response format - Updated all function signatures to include api_key_index and account_index parameters - Updated response decoding to handle SignedTxResponse structure * update signers based on 0.1.5 lighter-go release * refactor nonce-manager and use API Key dict only in signer - pass API key directly to signer - pass a list of arbitrary API keys to nonce manager - setup stores multiple API keys * python 3.8 Tuple * remove duplicate * fix nonce manager * examples lint * typo --------- Co-authored-by: Alex Velea <dev@velea.cc>
1 parent b885804 commit c3cecf5

30 files changed

Lines changed: 1033 additions & 910 deletions

examples/README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
- the API key config is saved in a local file `./api_key_config.json`
1010

1111
## Start trading on testnet
12-
- `create_modify_cancel_order.py`
12+
- `create_modify_cancel_order_http.py`
1313
- creates an ask (sell) order for 0.1 ETH @ $4050
1414
- modified the order and increases the size to 0.11 ETH and increases the price to $4100
1515
- cancels the order
1616
- Note: all of these operations use the client order index of the order. You can use the order from the exchange as well
1717

18-
- `ws_send_tx.py`
19-
- same flow as `create_modify_cancel_order.py`
18+
- `create_modify_cancel_order_ws.py`
19+
- same flow as `create_modify_cancel_order_http.py`
2020
- sends TXs over WS instead of HTTP
2121

2222
- `create_grouped_ioc_with_attached_sl_tp.py`
@@ -25,7 +25,7 @@
2525
- the size of the SL/TP will be equal to the executed size of the order
2626
- the SL/TP orders are canceled when the sign of your position changes
2727

28-
- `create_position_tied_sl_tl.py`
28+
- `create_position_tied_sl_tp.py`
2929
- creates a bid (buy) Stop Loss (SL) and a Take Profit (TP) to close your short position
3030
- the size of the orders will be for your whole position (because BaseAmount=0)
3131
- the orders will grow / shrink as you accumulate more position
@@ -43,6 +43,24 @@ What about the order types? Just as normal orders, SL/TP orders trigger an order
4343
- market order
4444
- limit IOC / GTC
4545

46+
### Modify leverage / Margin Mode (Cross, Isolated) / Add Collateral to isolated-only positions
47+
- `margin_eth_20x_cross_http`
48+
- sets ETH market to 20x leverage and cross-margin mode, using HTTP
49+
- `margin_eth_50x_isolate_ws`
50+
- sets ETH market to 50x leverage and isolated margin mode, using HTTP
51+
- `margin_eth_add_collateral_http.py`
52+
- adds $10.5 USDC to the ETH position (must be opened and in isolated mode)
53+
- `margin_eth_remove_collateral_ws.py`
54+
- removes $5 USDC from the ETH position (must be opened and in isolated mode)
55+
56+
### Batch orders
57+
- `send_batch_tx_http.py`
58+
- sends multiple orders in a single HTTP request
59+
- `send_batch_tx_ws.py`
60+
- sends multiple orders in a single WS request`
61+
62+
Batch TXs will be executed back to back, without the possibility of other TXs interfering.
63+
4664
## Setup steps for mainnet
4765
- deposit money on Lighter to create an account first
4866
- change the URL to `mainnet.zklighter.elliot.ai`
Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
import asyncio
2-
import lighter
32
from lighter.signer_client import CreateOrderTxReq
43
from utils import default_example_setup
54

65

76
async def main():
87
client, api_client, _ = default_example_setup()
98

9+
# Sell some ETH at $2500
10+
# The size of the SL/TP orders will be equal to the size of the executed order
11+
12+
# set SL trigger price at 5000 and limit price at 5050
13+
# set TP trigger price at 1500 and limit price at 1550
14+
# Note: set the limit price to be higher than the SL/TP trigger price to ensure the order will be filled
15+
# If the mark price of ETH reaches 1500, there might be no one willing to sell you ETH at 1500, so trying to buy at 1550 would increase the fill rate
16+
1017
ioc_order = CreateOrderTxReq(
1118
MarketIndex=0,
1219
ClientOrderIndex=0,
1320
BaseAmount=1000, # 0.1 ETH
14-
Price=300000, # $3000
21+
Price=2500_00, # $2500
1522
IsAsk=1, # sell
16-
Type=lighter.SignerClient.ORDER_TYPE_LIMIT,
17-
TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL,
23+
Type=client.ORDER_TYPE_LIMIT,
24+
TimeInForce=client.ORDER_TIME_IN_FORCE_IMMEDIATE_OR_CANCEL,
1825
ReduceOnly=0,
1926
TriggerPrice=0,
2027
OrderExpiry=0,
@@ -25,30 +32,30 @@ async def main():
2532
MarketIndex=0,
2633
ClientOrderIndex=0,
2734
BaseAmount=0,
28-
Price=300000,
35+
Price=1550_00,
2936
IsAsk=0,
30-
Type=lighter.SignerClient.ORDER_TYPE_TAKE_PROFIT_LIMIT,
31-
TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
37+
Type=client.ORDER_TYPE_TAKE_PROFIT_LIMIT,
38+
TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
3239
ReduceOnly=1,
33-
TriggerPrice=300000,
40+
TriggerPrice=1500_00,
3441
OrderExpiry=-1,
3542
)
3643

3744
stop_loss_order = CreateOrderTxReq(
3845
MarketIndex=0,
3946
ClientOrderIndex=0,
4047
BaseAmount=0,
41-
Price=500000,
48+
Price=5050_00,
4249
IsAsk=0,
43-
Type=lighter.SignerClient.ORDER_TYPE_STOP_LOSS_LIMIT,
44-
TimeInForce=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
50+
Type=client.ORDER_TYPE_STOP_LOSS_LIMIT,
51+
TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
4552
ReduceOnly=1,
46-
TriggerPrice=500000,
53+
TriggerPrice=5000_00,
4754
OrderExpiry=-1,
4855
)
4956

5057
transaction = await client.create_grouped_orders(
51-
grouping_type=lighter.SignerClient.GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER,
58+
grouping_type=client.GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER,
5259
orders=[ioc_order, take_profit_order, stop_loss_order],
5360
)
5461

@@ -57,5 +64,6 @@ async def main():
5764
await client.close()
5865
await api_client.close()
5966

67+
6068
if __name__ == "__main__":
6169
asyncio.run(main())

examples/create_modify_cancel_order.py renamed to examples/create_modify_cancel_order_http.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,54 @@
11
import asyncio
2-
import lighter
32
from utils import default_example_setup
43

54

65
async def main():
76
client, api_client, _ = default_example_setup()
7+
client.check_client()
88

99
# create order
10+
api_key_index, nonce = client.nonce_manager.next_nonce()
1011
tx, tx_hash, err = await client.create_order(
1112
market_index=0,
1213
client_order_index=123,
1314
base_amount=1000, # 0.1 ETH
14-
price=405000, # $4050
15+
price=4050_00, # $4050
1516
is_ask=True,
16-
order_type=lighter.SignerClient.ORDER_TYPE_LIMIT,
17-
time_in_force=lighter.SignerClient.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
17+
order_type=client.ORDER_TYPE_LIMIT,
18+
time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
1819
reduce_only=False,
1920
trigger_price=0,
21+
nonce=nonce,
22+
api_key_index=api_key_index,
2023
)
2124
print(f"Create Order {tx=} {tx_hash=} {err=}")
2225
if err is not None:
2326
raise Exception(err)
2427

25-
# create order
28+
## modify order
29+
# use the same API key so the TX goes after the create order TX
30+
api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index)
2631
tx, tx_hash, err = await client.modify_order(
2732
market_index=0,
2833
order_index=123,
2934
base_amount=1100, # 0.11 ETH
30-
price=410000, # $4100
35+
price=4100_00, # $4100
3136
trigger_price=0,
37+
nonce=nonce,
38+
api_key_index=api_key_index,
3239
)
3340
print(f"Modify Order {tx=} {tx_hash=} {err=}")
3441
if err is not None:
3542
raise Exception(err)
3643

37-
# cancel order
44+
## cancel order
45+
# use the same API key so the TX goes after the modify order TX
46+
api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index)
3847
tx, tx_hash, err = await client.cancel_order(
3948
market_index=0,
4049
order_index=123,
50+
nonce=nonce,
51+
api_key_index=api_key_index,
4152
)
4253
print(f"Cancel Order {tx=} {tx_hash=} {err=}")
4354
if err is not None:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import websockets
2+
import asyncio
3+
from utils import default_example_setup, ws_send_tx
4+
5+
6+
# this example does the same thing as the create_modify_cancel_order.py example, but sends the TX over WS instead of HTTP
7+
async def main():
8+
client, api_client, ws_client_promise = default_example_setup()
9+
10+
# set up WS client and print a connected message
11+
ws_client: websockets.ClientConnection = await ws_client_promise
12+
print("Received:", await ws_client.recv())
13+
14+
# create order
15+
api_key_index, nonce = client.nonce_manager.next_nonce()
16+
tx_type, tx_info, tx_hash, err = client.sign_create_order(
17+
market_index=0,
18+
client_order_index=123,
19+
base_amount=1000, # 0.1 ETH
20+
price=4050_00, # $4050
21+
is_ask=True,
22+
order_type=client.ORDER_TYPE_LIMIT,
23+
time_in_force=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
24+
reduce_only=False,
25+
trigger_price=0,
26+
nonce=nonce,
27+
api_key_index=api_key_index,
28+
)
29+
if err is not None:
30+
raise Exception(err)
31+
await ws_send_tx(ws_client, tx_type, tx_info, tx_hash)
32+
33+
## modify order
34+
# use the same API key so the TX goes after the create order TX
35+
api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index)
36+
tx_type, tx_info, tx_hash, err = client.sign_modify_order(
37+
market_index=0,
38+
order_index=123,
39+
base_amount=1100, # 0.11 ETH
40+
price=4100_00, # $4100
41+
trigger_price=0,
42+
nonce=nonce,
43+
api_key_index=api_key_index,
44+
)
45+
if err is not None:
46+
raise Exception(err)
47+
await ws_send_tx(ws_client, tx_type, tx_info, tx_hash)
48+
49+
## cancel order
50+
# use the same API key so the TX goes after the modify order TX
51+
api_key_index, nonce = client.nonce_manager.next_nonce(api_key_index)
52+
tx_type, tx_info, tx_hash, err = client.sign_cancel_order(
53+
market_index=0,
54+
order_index=123,
55+
nonce=nonce,
56+
api_key_index=api_key_index,
57+
)
58+
if err is not None:
59+
raise Exception(err)
60+
await ws_send_tx(ws_client, tx_type, tx_info, tx_hash)
61+
62+
await client.close()
63+
await api_client.close()
64+
await ws_client.close()
65+
66+
67+
if __name__ == "__main__":
68+
asyncio.run(main())

examples/create_position_tied_sl_tl.py

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import asyncio
2+
from lighter.signer_client import CreateOrderTxReq
3+
from utils import default_example_setup
4+
5+
6+
async def main():
7+
client, api_client, _ = default_example_setup()
8+
9+
# Creates a position tied SL/TP pair
10+
# The SL/TP orders will close your whole position, even if you add/remove from it later on
11+
# if the positions reach 0 or switches from short -> long, the orders are canceled
12+
13+
# this particular example, sets the SL/TP for a short position
14+
# set SL trigger price at 5000 and limit price at 5050
15+
# set TP trigger price at 1500 and limit price at 1550
16+
# Note: set the limit price to be higher than the SL/TP trigger price to ensure the order will be filled
17+
# If the mark price of ETH reaches 1500, there might be no one willing to sell you ETH at 1500, so trying to buy at 1550 would increase the fill rate
18+
19+
# Create a One-Cancels-the-Other grouped order with a take-profit and a stop-loss order
20+
take_profit_order = CreateOrderTxReq(
21+
MarketIndex=0,
22+
ClientOrderIndex=0,
23+
BaseAmount=0,
24+
Price=1550_00,
25+
IsAsk=0,
26+
Type=client.ORDER_TYPE_TAKE_PROFIT_LIMIT,
27+
TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
28+
ReduceOnly=1,
29+
TriggerPrice=1500_00,
30+
OrderExpiry=-1,
31+
)
32+
33+
stop_loss_order = CreateOrderTxReq(
34+
MarketIndex=0,
35+
ClientOrderIndex=0,
36+
BaseAmount=0,
37+
Price=4050_00,
38+
IsAsk=0,
39+
Type=client.ORDER_TYPE_STOP_LOSS_LIMIT,
40+
TimeInForce=client.ORDER_TIME_IN_FORCE_GOOD_TILL_TIME,
41+
ReduceOnly=1,
42+
TriggerPrice=4000_00,
43+
OrderExpiry=-1,
44+
)
45+
46+
transaction = await client.create_grouped_orders(
47+
grouping_type=client.GROUPING_TYPE_ONE_CANCELS_THE_OTHER,
48+
orders=[take_profit_order, stop_loss_order],
49+
)
50+
51+
print("Create Grouped Order Tx:", transaction)
52+
await client.close()
53+
await api_client.close()
54+
55+
if __name__ == "__main__":
56+
asyncio.run(main())

0 commit comments

Comments
 (0)