249 lines
6.8 KiB
Python
249 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Demo script for WebSocket market data feeds.
|
|
|
|
This script demonstrates how to use the WebSocketManager to subscribe to
|
|
real-time market data from multiple exchanges (Binance, OKX, Bybit).
|
|
|
|
Usage:
|
|
python demo_websocket.py
|
|
|
|
Press Ctrl+C to stop.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import signal
|
|
import sys
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from loguru import logger
|
|
|
|
from openclaw.exchange.websocket_feed import (
|
|
BinanceWebSocketFeed,
|
|
BybitWebSocketFeed,
|
|
OKXWebSocketFeed,
|
|
WebSocketManager,
|
|
OrderBook,
|
|
Trade,
|
|
KlineData,
|
|
Ticker,
|
|
)
|
|
|
|
# Global flag for graceful shutdown
|
|
running = True
|
|
|
|
|
|
async def on_ticker(symbol: str, ticker: Ticker) -> None:
|
|
"""Handle ticker updates."""
|
|
print(f"\n[TICKER] {symbol}")
|
|
print(f" Last: {ticker.last}")
|
|
print(f" Bid: {ticker.bid} | Ask: {ticker.ask}")
|
|
print(f" Spread: {ticker.spread:.4f} ({ticker.spread_pct:.4f}%)")
|
|
if ticker.high and ticker.low:
|
|
print(f" 24h High: {ticker.high} | Low: {ticker.low}")
|
|
if ticker.volume:
|
|
print(f" 24h Volume: {ticker.volume:,.2f}")
|
|
|
|
|
|
async def on_orderbook(symbol: str, orderbook: OrderBook) -> None:
|
|
"""Handle order book updates."""
|
|
print(f"\n[ORDERBOOK] {symbol}")
|
|
print(f" Best Bid: {orderbook.best_bid.price if orderbook.best_bid else 'N/A'}")
|
|
print(f" Best Ask: {orderbook.best_ask.price if orderbook.best_ask else 'N/A'}")
|
|
print(f" Spread: {orderbook.spread:.4f}")
|
|
print(f" Mid Price: {orderbook.mid_price:.4f}")
|
|
|
|
# Show top 5 levels
|
|
print(" Bids (top 5):")
|
|
for level in orderbook.bids[:5]:
|
|
print(f" {level.price:.2f} x {level.amount:.4f}")
|
|
print(" Asks (top 5):")
|
|
for level in orderbook.asks[:5]:
|
|
print(f" {level.price:.2f} x {level.amount:.4f}")
|
|
|
|
|
|
async def on_trade(symbol: str, trade: Trade) -> None:
|
|
"""Handle trade updates."""
|
|
side_emoji = "🟢" if trade.side == "buy" else "🔴"
|
|
print(f"\n[TRADE] {side_emoji} {symbol}")
|
|
print(f" Price: {trade.price}")
|
|
print(f" Amount: {trade.amount}")
|
|
print(f" Side: {trade.side}")
|
|
print(f" Time: {trade.timestamp.strftime('%H:%M:%S.%f')[:-3]}")
|
|
|
|
|
|
async def on_kline(symbol: str, kline: KlineData) -> None:
|
|
"""Handle kline/candlestick updates."""
|
|
status = "✓" if kline.is_closed else "..."
|
|
print(f"\n[KLINE {status}] {symbol} ({kline.interval})")
|
|
print(f" O: {kline.open:.2f} | H: {kline.high:.2f} | L: {kline.low:.2f} | C: {kline.close:.2f}")
|
|
print(f" Volume: {kline.volume:.4f}")
|
|
if kline.trades:
|
|
print(f" Trades: {kline.trades}")
|
|
|
|
|
|
def signal_handler(sig: int, frame: Optional[object]) -> None:
|
|
"""Handle shutdown signals."""
|
|
global running
|
|
print("\n\nShutting down...")
|
|
running = False
|
|
|
|
|
|
async def demo_single_exchange() -> None:
|
|
"""Demo with a single exchange (Binance)."""
|
|
print("=" * 60)
|
|
print("Demo 1: Single Exchange (Binance)")
|
|
print("=" * 60)
|
|
|
|
# Create a WebSocket manager
|
|
manager = WebSocketManager()
|
|
|
|
# Add Binance feed
|
|
binance = BinanceWebSocketFeed(futures=False)
|
|
manager.add_feed(binance)
|
|
|
|
# Start the manager
|
|
await manager.start()
|
|
|
|
# Subscribe to market data
|
|
symbol = "BTC/USDT"
|
|
await manager.subscribe_ticker(symbol, on_ticker)
|
|
await manager.subscribe_orderbook(symbol, on_orderbook)
|
|
await manager.subscribe_trades(symbol, on_trade)
|
|
await manager.subscribe_klines(symbol, "1m", on_kline)
|
|
|
|
print(f"\nSubscribed to {symbol} on Binance")
|
|
print("Waiting for data... (Press Ctrl+C to stop)\n")
|
|
|
|
# Run for 60 seconds
|
|
try:
|
|
for _ in range(60):
|
|
if not running:
|
|
break
|
|
await asyncio.sleep(1)
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
# Cleanup
|
|
await manager.stop()
|
|
print("\nDemo 1 completed.\n")
|
|
|
|
|
|
async def demo_multiple_exchanges() -> None:
|
|
"""Demo with multiple exchanges."""
|
|
print("=" * 60)
|
|
print("Demo 2: Multiple Exchanges (Binance, OKX, Bybit)")
|
|
print("=" * 60)
|
|
|
|
# Create a WebSocket manager
|
|
manager = WebSocketManager()
|
|
|
|
# Add multiple exchange feeds
|
|
manager.add_feed(BinanceWebSocketFeed(futures=False))
|
|
manager.add_feed(OKXWebSocketFeed())
|
|
manager.add_feed(BybitWebSocketFeed(market="spot"))
|
|
|
|
# Start the manager
|
|
await manager.start()
|
|
|
|
# Subscribe to market data on all exchanges
|
|
symbols = ["BTC/USDT", "ETH/USDT"]
|
|
|
|
for symbol in symbols:
|
|
await manager.subscribe_ticker(symbol, on_ticker)
|
|
print(f"Subscribed to {symbol} ticker on all exchanges")
|
|
|
|
print("\nWaiting for data from multiple exchanges... (Press Ctrl+C to stop)\n")
|
|
|
|
# Run for 60 seconds
|
|
try:
|
|
for _ in range(60):
|
|
if not running:
|
|
break
|
|
await asyncio.sleep(1)
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
# Cleanup
|
|
await manager.stop()
|
|
print("\nDemo 2 completed.\n")
|
|
|
|
|
|
async def demo_default_manager() -> None:
|
|
"""Demo using the default manager with all exchanges pre-configured."""
|
|
print("=" * 60)
|
|
print("Demo 3: Default Manager (All Exchanges)")
|
|
print("=" * 60)
|
|
|
|
# Create default manager with all exchanges
|
|
from openclaw.exchange.websocket_feed import create_default_manager
|
|
|
|
manager = create_default_manager()
|
|
await manager.start()
|
|
|
|
# Subscribe to popular symbols
|
|
symbols = ["BTC/USDT", "ETH/USDT", "SOL/USDT"]
|
|
|
|
for symbol in symbols:
|
|
await manager.subscribe_ticker(symbol, on_ticker)
|
|
await manager.subscribe_orderbook(symbol, on_orderbook)
|
|
print(f"Subscribed to {symbol} on all available exchanges")
|
|
|
|
print("\nWaiting for data... (Press Ctrl+C to stop)\n")
|
|
|
|
# Run for 60 seconds
|
|
try:
|
|
for _ in range(60):
|
|
if not running:
|
|
break
|
|
await asyncio.sleep(1)
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
# Cleanup
|
|
await manager.stop()
|
|
print("\nDemo 3 completed.\n")
|
|
|
|
|
|
async def main() -> None:
|
|
"""Main entry point."""
|
|
global running
|
|
|
|
# Setup signal handlers
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
print("\n" + "=" * 60)
|
|
print("WebSocket Market Data Feed Demo")
|
|
print("=" * 60)
|
|
print("\nThis demo shows how to use WebSocket feeds from multiple")
|
|
print("cryptocurrency exchanges to receive real-time market data.\n")
|
|
|
|
try:
|
|
# Run demos
|
|
if running:
|
|
await demo_single_exchange()
|
|
|
|
if running:
|
|
await asyncio.sleep(2)
|
|
await demo_multiple_exchanges()
|
|
|
|
if running:
|
|
await asyncio.sleep(2)
|
|
await demo_default_manager()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Demo error: {e}")
|
|
raise
|
|
|
|
print("\n" + "=" * 60)
|
|
print("All demos completed!")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run the async main function
|
|
asyncio.run(main())
|