Skip to content
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

Feat/XRPL connector v2 #6535

Merged
merged 22 commits into from Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c9ee872
Init xrpl client side code
mlguys Jul 31, 2023
71a0a7a
Add batch order create, cancel, get balances
mlguys Aug 1, 2023
edcd4d7
Add missing api source methods & Fix bugs
mlguys Aug 8, 2023
1ad1e98
fix trading rules
mlguys Aug 10, 2023
d841333
minor fix to order updates + comment on trading fees
mlguys Aug 15, 2023
66a102f
Add client test cases
mlguys Aug 22, 2023
40c30de
increase timeout for start and stop commands
mlguys Aug 22, 2023
4aa285d
add xrpl py client to environment
mlguys Aug 22, 2023
6b76631
Revert injective_utils.py
mlguys Aug 29, 2023
257b7f5
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Aug 29, 2023
f4e3d4a
Remove comments and empty file
mlguys Sep 7, 2023
fc3df24
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Sep 7, 2023
169ee1d
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Sep 7, 2023
4475afc
Merge branch 'feat/xrpl-connector-v2' of https://github.com/yourtradi…
mlguys Sep 7, 2023
22a29ab
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Oct 3, 2023
04dd072
Merge branch 'development' of https://github.com/yourtrading-ai/hummi…
mlguys Oct 11, 2023
b9fa1dd
Merge branch 'feat/xrpl-connector-v2' of https://github.com/yourtradi…
mlguys Nov 10, 2023
655932d
Merge branch 'development' of https://github.com/yourtrading-ai/hummi…
mlguys Nov 10, 2023
e498155
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Nov 10, 2023
abfbb7a
Fix getting ticker data
mlguys Nov 14, 2023
1caaa86
update test
mlguys Nov 14, 2023
60523b7
reduce client order id
mlguys Nov 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion hummingbot/client/command/start_command.py
Expand Up @@ -241,7 +241,7 @@ async def start_market_making(self, # type: HummingbotApplication
self.markets_recorder.restore_market_states(self.strategy_file_name, market)
if len(market.limit_orders) > 0:
self.notify(f"Canceling dangling limit orders on {market.name}...")
await market.cancel_all(5.0)
await market.cancel_all(10.0)
if self.strategy:
self.clock.add_iterator(self.strategy)
try:
Expand Down
2 changes: 1 addition & 1 deletion hummingbot/client/hummingbot_application.py
Expand Up @@ -49,7 +49,7 @@


class HummingbotApplication(*commands):
KILL_TIMEOUT = 10.0
KILL_TIMEOUT = 20.0
APP_WARNING_EXPIRY_DURATION = 3600.0
APP_WARNING_STATUS_LIMIT = 6

Expand Down
Empty file.

Large diffs are not rendered by default.

@@ -0,0 +1,42 @@
from bidict import bidict

from hummingbot.core.data_type.common import TradeType
from hummingbot.core.data_type.in_flight_order import OrderState

CONNECTOR_NAME = "xrpl"

MAX_ID_HEX_DIGITS = 16
MAX_ID_BIT_COUNT = MAX_ID_HEX_DIGITS * 4

BASE_PATH_URL = {
"mainnet": "https://xrplcluster.com/",
"testnet": "https://s.altnet.rippletest.net:51234/",
"devnet": "https://s.devnet.rippletest.net:51234/",
"amm-devnet": "https://amm.devnet.rippletest.net:51234/"
}

WS_PATH_URL = {
"mainnet": "wss://xrplcluster.com/",
"testnet": "wss://s.altnet.rippletest.net/",
"devnet": "wss://s.devnet.rippletest.net:51233/",
"amm-devnet": "wss://amm.devnet.rippletest.net:51233/ "
}

ORDER_SIDE_MAP = bidict(
{
"BUY": TradeType.BUY,
"SELL": TradeType.SELL
}
)

XRPL_TO_HB_STATUS_MAP = {
"OPEN": OrderState.OPEN,
"PENDING_OPEN": OrderState.PENDING_CREATE,
"PENDING_CANCEL": OrderState.PENDING_CANCEL,
"OFFER_EXPIRED_OR_UNFUNDED": OrderState.CANCELED,
"UNKNOWN": OrderState.FAILED,
"FAILED": OrderState.FAILED,
"PARTIALLY_FILLED": OrderState.PARTIALLY_FILLED,
"FILLED": OrderState.FILLED,
"CANCELED": OrderState.CANCELED,
}
2 changes: 1 addition & 1 deletion hummingbot/connector/gateway/gateway_in_flight_order.py
Expand Up @@ -9,7 +9,7 @@
from hummingbot.core.data_type.in_flight_order import InFlightOrder, OrderState, OrderUpdate, TradeUpdate

GET_GATEWAY_EX_ORDER_ID_TIMEOUT = 30 # seconds
GET_GATEWAY_TX_HASH = 1 # seconds
GET_GATEWAY_TX_HASH = 10 # seconds
cardosofede marked this conversation as resolved.
Show resolved Hide resolved

s_decimal_0 = Decimal("0")

Expand Down
1 change: 1 addition & 0 deletions hummingbot/core/utils/gateway_config_utils.py
Expand Up @@ -16,6 +16,7 @@
"injective": "INJ",
"xdc": "XDC",
"tezos": "XTZ",
"xrpl": "XRP",
"kujira": "KUJI"
}

Expand Down
1 change: 1 addition & 0 deletions setup/environment.yml
Expand Up @@ -65,3 +65,4 @@ dependencies:
- yarl==1.*
- git+https://github.com/CoinAlpha/python-signalr-client.git
- git+https://github.com/konichuvak/dydx-v3-python.git@web3
- xrpl-py

Large diffs are not rendered by default.

@@ -0,0 +1,182 @@
import asyncio
from decimal import Decimal
from typing import List, Optional, Tuple
from unittest.mock import AsyncMock, patch


class XrplClientMock:
def __init__(
self, initial_timestamp: float, wallet_address: str, base: str, quote: str,
):
self.initial_timestamp = initial_timestamp
self.base = base
self.base_coin_issuer = "rh8LssQyeBdEXk7Zv86HxHrx8k2R2DBUrx"
self.base_decimals = 15
self.quote = quote
self.quote_coin_issuer = "rh8LssQyeBdEXk7Zv86HxHrx8k2R2DBUrx"
self.quote_decimals = 8
self.market_id = f'{base}-{quote}'
self.wallet_address = wallet_address

self.gateway_instance_mock_patch = patch(
target=(
"hummingbot.connector.gateway.clob_spot.data_sources.xrpl.xrpl_api_data_source"
".GatewayHttpClient"
),
autospec=True,
)

self.gateway_instance_mock: Optional[AsyncMock] = None

self.place_order_called_event = asyncio.Event()
self.cancel_order_called_event = asyncio.Event()
self.update_market_called_event = asyncio.Event()
self.update_ticker_called_event = asyncio.Event()
self.update_balances_called_event = asyncio.Event()
self.orderbook_snapshot_called_event = asyncio.Event()
self.transaction_status_update_called_event = asyncio.Event()

def start(self):
self.gateway_instance_mock = self.gateway_instance_mock_patch.start()
self.gateway_instance_mock.get_instance.return_value = self.gateway_instance_mock

def stop(self):
self.gateway_instance_mock_patch.stop()

def run_until_place_order_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.place_order_called_event.wait(), timeout=timeout)
)

def run_until_cancel_order_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.cancel_order_called_event.wait(), timeout=timeout)
)

def run_until_update_market_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.update_market_called_event.wait(), timeout=timeout)
)

def run_until_transaction_status_update_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.transaction_status_update_called_event.wait(), timeout=timeout)
)

def run_until_update_ticker_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.update_ticker_called_event.wait(), timeout=timeout)
)

def run_until_orderbook_snapshot_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.orderbook_snapshot_called_event.wait(), timeout=timeout)
)

def run_until_update_balances_called(self, timeout: float = 1):
asyncio.get_event_loop().run_until_complete(
asyncio.wait_for(fut=self.update_balances_called_event.wait(), timeout=timeout)
)

def configure_place_order_response(
self,
timestamp: int,
transaction_hash: str,
exchange_order_id: str,
):
def place_and_return(*_, **__):
self.place_order_called_event.set()
return {
"network": "xrpl",
"timestamp": timestamp,
"latency": 2,
"txHash": transaction_hash,
}

def transaction_update_and_return(*_, **__):
self.transaction_status_update_called_event.set()
return {
"sequence": exchange_order_id
}

self.gateway_instance_mock.clob_place_order.side_effect = place_and_return
self.gateway_instance_mock.get_transaction_status.side_effect = transaction_update_and_return

def configure_cancel_order_response(self, timestamp: int, transaction_hash: str):
def cancel_and_return(*_, **__):
self.cancel_order_called_event.set()
return {
"network": "xrpl",
"timestamp": timestamp,
"latency": 2,
"txHash": transaction_hash,
}

self.gateway_instance_mock.clob_cancel_order.side_effect = cancel_and_return

def configure_trading_rules_response(self, minimum_order_size: str, base_transfer_rate: str,
quote_transfer_rate: str):
def update_market_and_return(*_, **__):
self.update_market_called_event.set()
return {
"markets": [
{"marketId": self.market_id,
"minimumOrderSize": minimum_order_size,
"smallestTickSize": str(min(self.base_decimals, self.quote_decimals)),
"baseTickSize": self.base_decimals,
"quoteTickSize": self.quote_decimals,
"baseTransferRate": base_transfer_rate,
"quoteTransferRate": quote_transfer_rate,
"baseIssuer": self.base_coin_issuer,
"quoteIssuer": self.quote_coin_issuer,
"baseCurrency": self.base,
"quoteCurrency": self.quote, }
],

}

self.gateway_instance_mock.get_clob_markets.side_effect = update_market_and_return

def configure_last_traded_price_response(self, price: str, trading_pair: str):
def update_market_and_return(*_, **__):
self.update_ticker_called_event.set()
return {
"markets": [
{
"marketId": trading_pair,
"midprice": price
}
]
}

self.gateway_instance_mock.get_clob_ticker.side_effect = update_market_and_return

def configure_orderbook_snapshot(self, timestamp: float, bids: List[Tuple[float, float]],
asks: List[Tuple[float, float]]):
def update_orderbook_and_return(*_, **__):
self.orderbook_snapshot_called_event.set()
transformed_bids = [{"price": price, "quantity": quantity} for price, quantity in bids]
transformed_asks = [{"price": price, "quantity": quantity} for price, quantity in asks]

return {
"timestamp": timestamp,
"buys": transformed_bids,
"sells": transformed_asks
}

self.gateway_instance_mock.get_clob_orderbook_snapshot.side_effect = update_orderbook_and_return

def configure_get_account_balances_response(self, base: str, quote: str,
base_balance: Decimal,
quote_balance: Decimal):
def update_balances_and_return(*_, **__):
self.update_balances_called_event.set()

return {
"balances": {
base: base_balance,
quote: quote_balance
}
}

self.gateway_instance_mock.get_balances.side_effect = update_balances_and_return