Skip to content

Commit c6efe4f

Browse files
authored
Merge pull request #41 from Reya-Labs/feat/support-new-spot-markets
Feat/support new spot markets
2 parents ce4d58e + 2be30fb commit c6efe4f

11 files changed

Lines changed: 500 additions & 219 deletions

examples/rest_api/spot/spot_transfer.py

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,21 @@
4747
logger.addHandler(handler)
4848
logger.propagate = False
4949

50-
ASSET_TO_SYMBOL = {"ETH": "WETHRUSD", "WETH": "WETHRUSD"}
51-
DEFAULT_TRANSFER_PRICE = "3156" # Realistic ETH price for transfers
50+
ASSET_TO_SYMBOL = {
51+
"ETH": "WETHRUSD",
52+
"WETH": "WETHRUSD",
53+
"BTC": "WBTCRUSD",
54+
"WBTC": "WBTCRUSD",
55+
}
56+
57+
# Mapping from asset to oracle symbol (perp symbol) for fetching oracle prices
58+
ASSET_TO_ORACLE_SYMBOL = {
59+
"ETH": "ETHRUSDPERP",
60+
"WETH": "ETHRUSDPERP",
61+
"BTC": "BTCRUSDPERP",
62+
"WBTC": "BTCRUSDPERP",
63+
}
64+
5265
ORDER_SETTLEMENT_RETRIES = 3
5366
ORDER_SETTLEMENT_DELAY = 1.0
5467

@@ -62,6 +75,43 @@ async def get_account_balance(client: ReyaTradingClient, account_id: int, asset:
6275
return Decimal("0")
6376

6477

78+
async def get_oracle_price(client: ReyaTradingClient, asset: str) -> Decimal:
79+
"""
80+
Fetch the current oracle price for an asset from the v2/prices API.
81+
82+
The oracle price is fetched from the corresponding perp market (e.g., ETHRUSDPERP for ETH).
83+
This ensures transfers happen at a price within the ±5% circuit breaker limit.
84+
85+
Args:
86+
client: ReyaTradingClient instance
87+
asset: Asset symbol (e.g., "ETH", "BTC")
88+
89+
Returns:
90+
Oracle price as Decimal
91+
92+
Raises:
93+
SystemExit: If oracle price cannot be fetched
94+
"""
95+
oracle_symbol = ASSET_TO_ORACLE_SYMBOL.get(asset.upper())
96+
97+
if not oracle_symbol:
98+
logger.error(f"❌ No oracle symbol mapping for {asset}. Supported: {list(ASSET_TO_ORACLE_SYMBOL.keys())}")
99+
sys.exit(1)
100+
101+
try:
102+
price_data = await client.markets.get_price(symbol=oracle_symbol)
103+
if price_data and price_data.oracle_price:
104+
oracle_price = Decimal(price_data.oracle_price)
105+
logger.info(f"📈 Fetched oracle price for {asset}: ${oracle_price:.2f}")
106+
return oracle_price
107+
else:
108+
logger.error(f"❌ No oracle price returned for {oracle_symbol}")
109+
sys.exit(1)
110+
except (OSError, RuntimeError, ValueError) as e:
111+
logger.error(f"❌ Failed to fetch oracle price for {oracle_symbol}: {e}")
112+
sys.exit(1)
113+
114+
65115
async def log_account_balances(
66116
sender_client: ReyaTradingClient,
67117
receiver_client: ReyaTradingClient | None,
@@ -275,18 +325,21 @@ async def balance_accounts_mode() -> None:
275325
target_eth = total_eth / 2
276326
_ = total_rusd / 2 # target_rusd - calculated but not used directly
277327

328+
# Fetch current oracle price for ETH
329+
oracle_price = await get_oracle_price(client_1, "ETH")
330+
278331
logger.info("🎯 TARGET ETH BALANCE")
279332
logger.info(f" Each account: {target_eth} ETH")
280-
logger.info(f" (RUSD will flow at market price ${DEFAULT_TRANSFER_PRICE})")
333+
logger.info(f" (RUSD will flow at oracle price ${oracle_price:.2f})")
281334

282335
# Determine transfer direction and amounts
283336
eth_diff_1 = eth_1 - target_eth # Positive = account 1 has excess
284337

285338
# We transfer ETH from the account with excess
286-
# Note: We use the market price (DEFAULT_TRANSFER_PRICE) for the transfer.
287-
# This balances ETH between accounts but RUSD will flow at market price,
339+
# Note: We use the oracle price for the transfer to stay within circuit breaker.
340+
# This balances ETH between accounts but RUSD will flow at oracle price,
288341
# not at an arbitrary price to balance both assets simultaneously.
289-
# On-chain price validation enforces orders must be within ~5% of oracle price.
342+
# On-chain price validation enforces orders must be within ±5% of oracle price.
290343
if abs(eth_diff_1) < Decimal("0.001"):
291344
logger.info("✅ Accounts already balanced (ETH difference < 0.001)")
292345
return
@@ -306,8 +359,8 @@ async def balance_accounts_mode() -> None:
306359
receiver_client = client_1
307360
eth_to_transfer = -eth_diff_1
308361

309-
# Use market price for transfer (on-chain enforces price limits)
310-
transfer_price = Decimal(DEFAULT_TRANSFER_PRICE)
362+
# Use oracle price for transfer (on-chain enforces ±5% price limits)
363+
transfer_price = oracle_price
311364

312365
# Round to reasonable precision
313366
eth_qty = str(eth_to_transfer.quantize(Decimal("0.001")))
@@ -350,7 +403,7 @@ async def main():
350403
)
351404
parser.add_argument("--from-account", type=int, help="Account ID to transfer FROM (sender)")
352405
parser.add_argument("--to-account", type=int, help="Account ID to transfer TO (receiver)")
353-
parser.add_argument("--asset", type=str, choices=["ETH", "WETH"], help="Asset to transfer")
406+
parser.add_argument("--asset", type=str, choices=["ETH", "WETH", "BTC", "WBTC"], help="Asset to transfer")
354407
parser.add_argument("--qty", type=str, help="Quantity to transfer (e.g., 5)")
355408
parser.add_argument("--price", type=str, default=None, help="Custom price for transfer (default: 0.01)")
356409

@@ -370,7 +423,7 @@ async def main():
370423
to_account_id = args.to_account
371424
asset = args.asset.upper()
372425
qty = args.qty
373-
transfer_price = args.price if args.price else DEFAULT_TRANSFER_PRICE
426+
custom_price = args.price # None if not provided - will fetch oracle price dynamically
374427

375428
symbol = ASSET_TO_SYMBOL.get(asset)
376429
if not symbol:
@@ -420,7 +473,10 @@ async def main():
420473
logger.info(f" Asset: {asset}")
421474
logger.info(f" Quantity: {qty}")
422475
logger.info(f" Symbol: {symbol}")
423-
logger.info(f" Price: ${transfer_price}")
476+
if custom_price:
477+
logger.info(f" Price: ${custom_price} (custom)")
478+
else:
479+
logger.info(" Price: (will fetch oracle price)")
424480

425481
# Get base config to inherit api_url and chain_id
426482
base_client = ReyaTradingClient()
@@ -431,6 +487,15 @@ async def main():
431487
async with ReyaTradingClient(config=sender_config) as sender_client:
432488
await sender_client.start()
433489

490+
# Fetch oracle price if no custom price provided
491+
if custom_price:
492+
transfer_price = custom_price
493+
else:
494+
oracle_price = await get_oracle_price(sender_client, asset)
495+
# Round to 2 decimal places for transfer price
496+
transfer_price = str(oracle_price.quantize(Decimal("0.01")))
497+
logger.info(f" Transfer Price: ${transfer_price} (from oracle)")
498+
434499
# Validate sender has sufficient balance
435500
qty_decimal = Decimal(qty)
436501
if not await validate_sufficient_balance(sender_client, from_account_id, asset, qty_decimal):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "reya-python-sdk"
3-
version = "2.1.3.0"
3+
version = "2.1.3.1"
44
description = "SDK for interacting with Reya Labs APIs"
55
authors = [
66
{name = "Reya Labs"}

0 commit comments

Comments
 (0)