@@ -228,20 +228,61 @@ def get_trade_book(auth):
228228# ---------------------------------------------------------------------------
229229
230230def get_positions (auth ):
231- """Fetch all open margined positions."""
231+ """
232+ Fetch all open positions — both derivatives (margined) and spot (wallet).
233+
234+ Derivatives come from GET /v2/positions/margined.
235+ Spot holdings come from GET /v2/wallet/balances — non-INR assets with
236+ a non-zero balance are synthesised into position-like dicts so they
237+ appear in the OpenAlgo position book alongside derivative positions.
238+ """
239+ positions = []
240+
241+ # 1. Derivative positions (perpetual futures, options)
232242 try :
233243 result = get_api_response ("/v2/positions/margined" , auth , method = "GET" )
234244 if result .get ("success" ):
235- return result .get ("result" , [])
236- logger . warning ( f"[DeltaExchange] get_positions unexpected response: { result } " )
237- return []
245+ positions . extend ( result .get ("result" , []) )
246+ else :
247+ logger . warning ( f"[DeltaExchange] get_positions/margined unexpected: { result } " )
238248 except Exception as e :
239- logger .error (f"[DeltaExchange] Exception in get_positions: { e } " )
240- return []
249+ logger .error (f"[DeltaExchange] Exception in get_positions/margined: { e } " )
250+
251+ # 2. Spot holdings from wallet balances
252+ try :
253+ wallet_result = get_api_response ("/v2/wallet/balances" , auth , method = "GET" )
254+ if wallet_result .get ("success" ):
255+ for asset in wallet_result .get ("result" , []):
256+ if not isinstance (asset , dict ):
257+ continue
258+ symbol = asset .get ("asset_symbol" , "" ) or asset .get ("symbol" , "" )
259+ # Skip INR (settlement currency) and zero-balance assets
260+ if symbol in ("INR" , "USD" , "" ) or not symbol :
261+ continue
262+ balance = float (asset .get ("balance" , 0 ) or 0 )
263+ blocked = float (asset .get ("blocked_margin" , 0 ) or 0 )
264+ size = balance - blocked # available spot holding
265+ if size <= 0 :
266+ continue
267+ # Synthesise a position-like dict matching /v2/positions/margined structure
268+ spot_symbol = f"{ symbol } _INR"
269+ positions .append ({
270+ "product_id" : asset .get ("asset_id" , "" ),
271+ "product_symbol" : spot_symbol ,
272+ "size" : size ,
273+ "entry_price" : "0" , # Wallet doesn't track entry price
274+ "realized_pnl" : "0" ,
275+ "unrealized_pnl" : "0" ,
276+ "_is_spot" : True , # Internal flag for downstream mapping
277+ })
278+ except Exception as e :
279+ logger .error (f"[DeltaExchange] Exception fetching spot wallet positions: { e } " )
280+
281+ return positions
241282
242283
243284def get_holdings (auth ):
244- """Delta Exchange is a derivatives-only exchange; equity holdings are not applicable ."""
285+ """Delta Exchange has no equity holdings concept; spot is shown in positions ."""
245286 return []
246287
247288
@@ -406,22 +447,22 @@ def place_smartorder_api(data, auth):
406447 symbol = data .get ("symbol" )
407448 exchange = data .get ("exchange" )
408449 product = data .get ("product" )
409- position_size = int (data .get ("position_size" , "0" ))
450+ position_size = float (data .get ("position_size" , "0" ))
410451
411- current_position = int (
452+ current_position = float (
412453 get_open_position (symbol , exchange , map_product_type (product ), auth )
413454 )
414455 logger .info (
415456 f"[DeltaExchange] SmartOrder: target={ position_size } current={ current_position } "
416457 )
417458
418- if position_size == 0 and current_position == 0 and int (data ["quantity" ]) != 0 :
459+ if position_size == 0 and current_position == 0 and float (data ["quantity" ]) != 0 :
419460 return place_order_api (data , auth )
420461
421462 if position_size == current_position :
422463 msg = (
423464 "No OpenPosition Found. Not placing Exit order."
424- if int (data ["quantity" ]) == 0
465+ if float (data ["quantity" ]) == 0
425466 else "No action needed. Position size matches current position"
426467 )
427468 return res , {"status" : "success" , "message" : msg }, None
0 commit comments