3131
3232
3333class Exchange (API ):
34+
35+ # Default Max Slippage for Market Orders 5%
36+ DEFAULT_SLIPPAGE = 0.05
37+
3438 def __init__ (
3539 self ,
3640 wallet : LocalAccount ,
@@ -41,9 +45,9 @@ def __init__(
4145 super ().__init__ (base_url )
4246 self .wallet = wallet
4347 self .vault_address = vault_address
48+ self .info = Info (base_url , skip_ws = True )
4449 if meta is None :
45- info = Info (base_url , skip_ws = True )
46- self .meta = info .meta ()
50+ self .meta = self .info .meta ()
4751 else :
4852 self .meta = meta
4953 self .coin_to_asset = {asset_info ["name" ]: asset for (asset , asset_info ) in enumerate (self .meta ["universe" ])}
@@ -58,6 +62,22 @@ def _post_action(self, action, signature, nonce):
5862 logging .debug (payload )
5963 return self .post ("/exchange" , payload )
6064
65+ def _slippage_price (
66+ self ,
67+ coin : str ,
68+ is_buy : bool ,
69+ slippage : float ,
70+ px : Optional [float ] = None ,
71+ ) -> float :
72+
73+ if not px :
74+ # Get midprice
75+ px = float (self .info .all_mids ()[coin ])
76+ # Calculate Slippage
77+ px *= (1 + slippage ) if is_buy else (1 - slippage )
78+ # We round px to 5 significant figures and 6 decimals
79+ return round (float (f"{ px :.5g} " ), 6 )
80+
6181 def order (
6282 self ,
6383 coin : str ,
@@ -68,7 +88,7 @@ def order(
6888 reduce_only : bool = False ,
6989 cloid : Optional [Cloid ] = None ,
7090 ) -> Any :
71- order = {
91+ order : OrderRequest = {
7292 "coin" : coin ,
7393 "is_buy" : is_buy ,
7494 "sz" : sz ,
@@ -122,6 +142,93 @@ def bulk_orders(self, order_requests: List[OrderRequest]) -> Any:
122142 timestamp ,
123143 )
124144
145+ def modify_order (
146+ self ,
147+ oid : int ,
148+ coin : str ,
149+ is_buy : bool ,
150+ sz : float ,
151+ limit_px : float ,
152+ order_type : OrderType ,
153+ reduce_only : bool = False ,
154+ cloid : Optional [Cloid ] = None ,
155+ ) -> Any :
156+ order : OrderRequest = {
157+ "coin" : coin ,
158+ "is_buy" : is_buy ,
159+ "sz" : sz ,
160+ "limit_px" : limit_px ,
161+ "order_type" : order_type ,
162+ "reduce_only" : reduce_only ,
163+ }
164+ if cloid :
165+ order ["cloid" ] = cloid
166+
167+ order_spec = order_request_to_order_spec (order , self .coin_to_asset [order ["coin" ]])
168+
169+ timestamp = get_timestamp_ms ()
170+
171+ if cloid :
172+ signature_types = ["uint64" , "(uint32,bool,uint64,uint64,bool,uint8,uint64,bytes16)" ]
173+ else :
174+ signature_types = ["uint64" , "(uint32,bool,uint64,uint64,bool,uint8,uint64)" ]
175+
176+ signature = sign_l1_action (
177+ self .wallet ,
178+ signature_types ,
179+ [oid , order_spec_preprocessing (order_spec )],
180+ ZERO_ADDRESS if self .vault_address is None else self .vault_address ,
181+ timestamp ,
182+ self .base_url == MAINNET_API_URL ,
183+ )
184+
185+ return self ._post_action (
186+ {
187+ "type" : "modify" ,
188+ "oid" : oid ,
189+ "order" : order_spec_to_order_wire (order_spec ),
190+ },
191+ signature ,
192+ timestamp ,
193+ )
194+
195+ def market_open (
196+ self ,
197+ coin : str ,
198+ is_buy : bool ,
199+ sz : float ,
200+ px : Optional [float ] = None ,
201+ slippage : float = DEFAULT_SLIPPAGE ,
202+ cloid : Optional [Cloid ] = None ,
203+ ) -> Any :
204+
205+ # Get aggressive Market Price
206+ px = self ._slippage_price (coin , is_buy , slippage , px )
207+ # Market Order is an aggressive Limit Order IoC
208+ return self .order (coin , is_buy , sz , px , order_type = {"limit" : {"tif" : "Ioc" }}, reduce_only = False , cloid = cloid )
209+
210+ def market_close (
211+ self ,
212+ coin : str ,
213+ sz : Optional [float ] = None ,
214+ px : Optional [float ] = None ,
215+ slippage : float = DEFAULT_SLIPPAGE ,
216+ cloid : Optional [Cloid ] = None ,
217+ ) -> Any :
218+ positions = self .info .user_state (self .wallet .address )["assetPositions" ]
219+ for position in positions :
220+ item = position ["position" ]
221+ if coin != item ["coin" ]:
222+ continue
223+ szi = float (item ["szi" ])
224+ if not sz :
225+ sz = szi
226+ is_buy = True if szi < 0 else False
227+ # Get aggressive Market Price
228+ px = self ._slippage_price (coin , is_buy , slippage , px )
229+ # Market Order is an aggressive Limit Order IoC
230+ return self .order (coin , is_buy , sz , px , order_type = {"limit" : {"tif" : "Ioc" }}, reduce_only = True , cloid = cloid )
231+
125232 def cancel (self , coin : str , oid : int ) -> Any :
126233 return self .bulk_cancel ([{"coin" : coin , "oid" : oid }])
127234
0 commit comments