Skip to content

Conversation

@SBALAVIGNESH123
Copy link
Owner

@SBALAVIGNESH123 SBALAVIGNESH123 commented Dec 28, 2025

Description

Implemented the Aevo Perpetual Connector for Hummingbot.

Changes

  • WebSocket Authentication: Added get_ws_auth_payload in �evo_perpetual_auth.py.
  • User Stream: Integrated auth with AevoPerpetualUserStreamDataSource.
  • Symbol Mapping: Implemented ETH-USD <-> ETH-PERP conversion.
  • Tests: Added unit tests for Order Book, User Stream, and Auth.
  • Config: Registered connector in conf_fee_overrides_TEMPLATE.yml.

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.

  • Adds connector modules: aevo_perpetual_derivative, aevo_perpetual_api_order_book_data_source, aevo_perpetual_user_stream_data_source, aevo_perpetual_auth, aevo_perpetual_constants, aevo_perpetual_utils
  • Implements order book snapshots/trades via WS and snapshots via REST; last traded price retrieval
  • Implements REST HMAC nanosecond signing and WS auth payload; rate limits and endpoints defined in aevo_perpetual_constants
  • Supports order place (POST /orders) and cancel (DELETE /orders/{id}); symbol mapping ETH-USDETH-PERP
  • Registers fee override entries in conf_fee_overrides_TEMPLATE.yml; pins commlib-py==0.11.5 in env files
  • Adds unit tests and mocks for auth, order book data source, user stream, and connector init

Written by Cursor Bugbot for commit 332e02f. This will update automatically on new commits. Configure here.

Copy link

@cursor cursor bot left a 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)
Copy link

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)

Fix in Cursor Fix in Web

"asks": snapshot.get("asks", [])
},
timestamp=timestamp * 1e-9
)
Copy link

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.

Fix in Cursor Fix in Web

# 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)
Copy link

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)

Fix in Cursor Fix in Web

},
timestamp=payload.get("timestamp", self._time_synchronizer.time() * 1e9) * 1e-9
)
self._message_queue.put_nowait(order_book_message)
Copy link

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)

Fix in Cursor Fix in Web

# 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}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statement left in test file

A debug print statement was left in the test file that outputs DEBUG calls: along with internal mock call details. This clutters test output and appears to be debugging code that was accidentally committed.

Fix in Cursor Fix in Web


async def start_network(self):
await self._stop_network()
self._stop_network_task = asyncio.create_task(self._start_network())
Copy link

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)

Fix in Cursor Fix in Web

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
Copy link

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.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants