diff --git a/blueprints/brlogin.py b/blueprints/brlogin.py index aeff0fa54..8169024b8 100644 --- a/blueprints/brlogin.py +++ b/blueprints/brlogin.py @@ -750,9 +750,6 @@ def broker_callback(broker, para=None): auth_token = f"{auth_token}" # For brokers that have user_id and feed_token from authenticate_broker - if broker in ["angel", "aliceblue", "compositedge", "pocketful", "definedge", "dhan"]: - # For Compositedge, handle missing session user - if broker == "compositedge" and "user" not in session: if broker in ["angel", "compositedge", "pocketful", "definedge", "dhan", "rmoney"]: # For OAuth brokers, handle missing session user if broker in ("compositedge", "rmoney") and "user" not in session: diff --git a/broker/deltaexchange/api/order_api.py b/broker/deltaexchange/api/order_api.py index 79ca0bf0c..dd43a7af1 100644 --- a/broker/deltaexchange/api/order_api.py +++ b/broker/deltaexchange/api/order_api.py @@ -526,7 +526,25 @@ def cancel_order(orderid, auth): def cancel_all_orders_api(data, auth): - """Cancel all currently open orders (regardless of creation date).""" + """ + Cancel all currently open orders via DELETE /v2/orders/all. + + Uses the bulk cancel endpoint instead of cancelling orders one by one. + Falls back to individual cancellation if bulk endpoint fails. + """ + # Try bulk cancel first (single API call) + body = { + "cancel_limit_orders": True, + "cancel_stop_orders": True, + "cancel_reduce_only_orders": True, + } + result = get_api_response("/v2/orders/all", auth, method="DELETE", payload=json.dumps(body)) + if result.get("success"): + logger.info("[DeltaExchange] All open orders cancelled via /v2/orders/all") + return ["all"], [] + + # Fallback: cancel individually + logger.warning("[DeltaExchange] Bulk cancel failed, falling back to individual cancellation") order_book = _get_all_open_orders(auth) if not order_book: return [], [] @@ -573,7 +591,7 @@ def modify_order(data, auth): # --------------------------------------------------------------------------- def close_all_positions(current_api_key, auth): - """Square off all open positions using market orders.""" + """Square off all open positions (derivatives + spot) using market orders.""" positions = get_positions(auth) if not positions: return {"message": "No Open Positions Found"}, 200 @@ -581,7 +599,13 @@ def close_all_positions(current_api_key, auth): for pos in positions: if not isinstance(pos, dict): continue - size = int(pos.get("size", 0)) + is_spot = pos.get("_is_spot", False) + + # Use float() to handle fractional spot sizes (e.g. 0.0001 BTC) + try: + size = float(pos.get("size", 0)) + except (ValueError, TypeError): + size = 0 if size == 0: continue @@ -590,8 +614,13 @@ def close_all_positions(current_api_key, auth): action = "SELL" if size > 0 else "BUY" quantity = abs(size) - # Resolve OpenAlgo symbol from DB; fall back to product_symbol - symbol = get_symbol(str(product_id), "CRYPTO") or product_symbol + # Resolve OpenAlgo symbol from DB. + # For spot wallet entries, product_id is asset_id (not product token), + # so look up by brsymbol instead. + if is_spot: + symbol = get_oa_symbol(product_symbol, "CRYPTO") or product_symbol + else: + symbol = get_symbol(str(product_id), "CRYPTO") or product_symbol logger.info(f"[DeltaExchange] Close: {action} {quantity} {symbol}") order_payload = { @@ -601,7 +630,7 @@ def close_all_positions(current_api_key, auth): "action": action, "exchange": "CRYPTO", "pricetype": "MARKET", - "product": "NRML", + "product": "CNC" if is_spot else "NRML", "quantity": str(quantity), } _, api_response, _ = place_order_api(order_payload, auth) diff --git a/services/orderbook_service.py b/services/orderbook_service.py index 46c0843b5..7ec5a8afc 100644 --- a/services/orderbook_service.py +++ b/services/orderbook_service.py @@ -38,9 +38,9 @@ def format_order_data(order_data): formatted_item = {} for key, value in item.items(): if isinstance(value, (int, float)): - # Keep quantity fields as integers + # Keep quantity fields as integers when whole, preserve float for fractional (crypto spot) if key.lower() in quantity_fields: - formatted_item[key] = int(value) + formatted_item[key] = int(value) if value == int(value) else value else: formatted_item[key] = format_decimal(value) else: diff --git a/services/tradebook_service.py b/services/tradebook_service.py index 30efa57b2..b3ae199a8 100644 --- a/services/tradebook_service.py +++ b/services/tradebook_service.py @@ -31,7 +31,7 @@ def format_trade_data(trade_data): if isinstance(trade_data, list): return [ { - key: int(value) + key: (int(value) if value == int(value) else value) if (key.lower() in quantity_fields and isinstance(value, (int, float))) else (format_decimal(value) if isinstance(value, (int, float)) else value) for key, value in item.items()