-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Implement Aevo Perpetual Connector #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Conversation
sync / Client staging -> master for Hummingbot version 2.10.0
sync / Client development -> staging v2.11
sync / add modify side to avoid errors to staging
sync / staging to main v2.11
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is being reviewed by Cursor Bugbot
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| f"{CONSTANTS.WS_TOPIC_TRADES}:{pair}" | ||
| ] | ||
| } | ||
| await ws.send_json(subscribe_request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebSocket subscriptions missing symbol format conversion
The WebSocket subscription uses pair from self._trading_pairs directly without converting to exchange format (e.g., subscribing with "ETH-USD" instead of "ETH-PERP"). REST endpoints like get_new_order_book correctly call utils.convert_to_exchange_symbol() before making requests. Additionally, when parsing WebSocket responses, channel.split(":")[-1] extracts the trading pair in exchange format but never converts it back to HB format using utils.convert_to_hb_symbol(). This inconsistency causes trading pair mismatches between WebSocket data and the rest of the system.
Additional Locations (2)
| "asks": snapshot.get("asks", []) | ||
| }, | ||
| timestamp=timestamp * 1e-9 | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method returns OrderBookMessage but declares OrderBook return type
The get_new_order_book method signature declares a return type of OrderBook but the implementation returns an OrderBookMessage instance at line 60. Callers expecting an OrderBook object will receive an incompatible OrderBookMessage type, causing method calls and attribute access to fail at runtime. Other connectors in the codebase that implement get_new_order_book correctly return OrderBook objects.
| # Check base class: OrderBookTrackerDataSource usually returns OrderBookMessage from snapshot. | ||
| from hummingbot.core.data_type.order_book_message import OrderBookMessage, OrderBookMessageType | ||
|
|
||
| timestamp = snapshot.get("timestamp", self._time_synchronizer.time() * 1e9) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional time_synchronizer accessed without null check
The _time_synchronizer parameter is declared as Optional[Any] = None in __init__, but the code accesses self._time_synchronizer.time() at multiple locations without null checks. When the API response lacks a timestamp and _time_synchronizer is None, this causes an AttributeError crash. The fallback mechanism using .get(..., self._time_synchronizer.time() * 1e9) evaluates the default value before checking if the key exists, so the None access occurs even when a timestamp is present.
Additional Locations (2)
| }, | ||
| timestamp=payload.get("timestamp", self._time_synchronizer.time() * 1e9) * 1e-9 | ||
| ) | ||
| self._message_queue.put_nowait(order_book_message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling put_nowait on dict instead of Queue
The code calls self._message_queue.put_nowait() directly, but _message_queue inherited from OrderBookTrackerDataSource is a Dict[str, asyncio.Queue], not a Queue. Other connectors correctly use self._message_queue[self._snapshot_messages_queue_key].put_nowait() or similar keyed access. This will raise an AttributeError at runtime.
Additional Locations (1)
hummingbot/connector/derivative/aevo_perpetual/aevo_perpetual_api_order_book_data_source.py
Outdated
Show resolved
Hide resolved
| # Verify Auth was called | ||
| self.mock_auth.get_ws_auth_payload.assert_called_once() | ||
| # Verify Auth message sent | ||
| print(f"DEBUG calls: {self.mock_ws_connection.send_json.call_args_list}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix/commlib-bug
|
|
||
| async def start_network(self): | ||
| await self._stop_network() | ||
| self._stop_network_task = asyncio.create_task(self._start_network()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Network task not cancelled during stop causes resource leak
The start_network method creates a task stored in _stop_network_task, but the _stop_network method only cancels _status_polling_task and never cancels _stop_network_task. When start_network is called multiple times (which calls _stop_network first), the previous _start_network task becomes orphaned and continues running. This causes resource leaks and potentially duplicate trackers/polling loops running concurrently. The variable naming also appears wrong - it stores a start task in a stop-named variable.
Additional Locations (1)
hummingbot/connector/derivative/aevo_perpetual/aevo_perpetual_api_order_book_data_source.py
Outdated
Show resolved
Hide resolved
| exchange_order_id = str(response.get("order_id", order_id)) | ||
| transact_time = float(response.get("timestamp", self._time_synchronizer.time() * 1e9)) * 1e-9 | ||
|
|
||
| return exchange_order_id, transact_time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Order type parameter ignored in place order method
The _place_order method accepts an order_type parameter but completely ignores it. The connector claims to support both OrderType.LIMIT and OrderType.MARKET in supported_order_types, but the implementation always sends limit_price in the request params regardless of order type. Other perpetual connectors (like Binance, Bitmart) correctly use order_type to set the type field and conditionally include price only for limit orders. Market orders placed through this connector would either fail or be incorrectly submitted as limit orders.
Description
Implemented the Aevo Perpetual Connector for Hummingbot.
Changes
Verification
All unit tests passed. See walkthrough for details.
Note
Introduces a new Aevo Perpetual derivative connector with REST/WS market data, private streams, auth, and basic trading ops.
aevo_perpetual_derivative,aevo_perpetual_api_order_book_data_source,aevo_perpetual_user_stream_data_source,aevo_perpetual_auth,aevo_perpetual_constants,aevo_perpetual_utilsaevo_perpetual_constantsPOST /orders) and cancel (DELETE /orders/{id}); symbol mappingETH-USD↔ETH-PERPconf_fee_overrides_TEMPLATE.yml; pinscommlib-py==0.11.5in env filesWritten by Cursor Bugbot for commit 332e02f. This will update automatically on new commits. Configure here.