Files
oib 15427c96c0 chore: update file permissions to executable across repository
- Change file mode from 644 to 755 for all project files
- Add chain_id parameter to get_balance RPC endpoint with default "ait-devnet"
- Rename Miner.extra_meta_data to extra_metadata for consistency
2026-03-06 22:17:54 +01:00

325 lines
9.8 KiB
Python
Executable File

"""
Production Exchange API Integration Service
Handles real exchange connections and trading operations
"""
import asyncio
import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, List, Optional
import aiohttp
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="AITBC Exchange Integration Service",
description="Production exchange API integration for AITBC trading",
version="1.0.0"
)
# Data models
class ExchangeRegistration(BaseModel):
name: str
api_key: str
sandbox: bool = True
description: Optional[str] = None
class TradingPair(BaseModel):
symbol: str
base_asset: str
quote_asset: str
min_order_size: float
price_precision: int
quantity_precision: int
class OrderRequest(BaseModel):
symbol: str
side: str # buy/sell
type: str # market/limit
quantity: float
price: Optional[float] = None
# In-memory storage (in production, use database)
exchanges: Dict[str, Dict] = {}
trading_pairs: Dict[str, Dict] = {}
orders: Dict[str, Dict] = {}
@app.get("/")
async def root():
return {
"service": "AITBC Exchange Integration",
"status": "running",
"timestamp": datetime.utcnow().isoformat(),
"version": "1.0.0"
}
@app.get("/health")
async def health_check():
return {
"status": "healthy",
"exchanges_connected": len([e for e in exchanges.values() if e.get("connected")]),
"active_pairs": len(trading_pairs),
"total_orders": len(orders)
}
@app.post("/api/v1/exchanges/register")
async def register_exchange(registration: ExchangeRegistration):
"""Register a new exchange connection"""
exchange_id = registration.name.lower()
if exchange_id in exchanges:
raise HTTPException(status_code=400, detail="Exchange already registered")
# Create exchange configuration
exchange_config = {
"exchange_id": exchange_id,
"name": registration.name,
"api_key": registration.api_key,
"sandbox": registration.sandbox,
"description": registration.description,
"connected": False,
"created_at": datetime.utcnow().isoformat(),
"last_sync": None,
"trading_pairs": []
}
exchanges[exchange_id] = exchange_config
logger.info(f"Exchange registered: {registration.name}")
return {
"exchange_id": exchange_id,
"status": "registered",
"name": registration.name,
"sandbox": registration.sandbox,
"created_at": exchange_config["created_at"]
}
@app.post("/api/v1/exchanges/{exchange_id}/connect")
async def connect_exchange(exchange_id: str):
"""Connect to a registered exchange"""
if exchange_id not in exchanges:
raise HTTPException(status_code=404, detail="Exchange not found")
exchange = exchanges[exchange_id]
if exchange["connected"]:
return {"status": "already_connected", "exchange_id": exchange_id}
# Simulate exchange connection
# In production, this would make actual API calls to the exchange
await asyncio.sleep(1) # Simulate connection delay
exchange["connected"] = True
exchange["last_sync"] = datetime.utcnow().isoformat()
logger.info(f"Exchange connected: {exchange_id}")
return {
"exchange_id": exchange_id,
"status": "connected",
"connected_at": exchange["last_sync"]
}
@app.post("/api/v1/pairs/create")
async def create_trading_pair(pair: TradingPair):
"""Create a new trading pair"""
pair_id = f"{pair.symbol.lower()}"
if pair_id in trading_pairs:
raise HTTPException(status_code=400, detail="Trading pair already exists")
# Create trading pair configuration
pair_config = {
"pair_id": pair_id,
"symbol": pair.symbol,
"base_asset": pair.base_asset,
"quote_asset": pair.quote_asset,
"min_order_size": pair.min_order_size,
"price_precision": pair.price_precision,
"quantity_precision": pair.quantity_precision,
"status": "active",
"created_at": datetime.utcnow().isoformat(),
"current_price": None,
"volume_24h": 0.0,
"orders": []
}
trading_pairs[pair_id] = pair_config
logger.info(f"Trading pair created: {pair.symbol}")
return {
"pair_id": pair_id,
"symbol": pair.symbol,
"status": "created",
"created_at": pair_config["created_at"]
}
@app.get("/api/v1/pairs")
async def list_trading_pairs():
"""List all trading pairs"""
return {
"pairs": list(trading_pairs.values()),
"total_pairs": len(trading_pairs)
}
@app.get("/api/v1/pairs/{pair_id}")
async def get_trading_pair(pair_id: str):
"""Get specific trading pair information"""
if pair_id not in trading_pairs:
raise HTTPException(status_code=404, detail="Trading pair not found")
return trading_pairs[pair_id]
@app.post("/api/v1/orders")
async def create_order(order: OrderRequest):
"""Create a new trading order"""
pair_id = order.symbol.lower()
if pair_id not in trading_pairs:
raise HTTPException(status_code=404, detail="Trading pair not found")
# Generate order ID
order_id = f"order_{int(datetime.utcnow().timestamp())}"
# Create order
order_data = {
"order_id": order_id,
"symbol": order.symbol,
"side": order.side,
"type": order.type,
"quantity": order.quantity,
"price": order.price,
"status": "submitted",
"created_at": datetime.utcnow().isoformat(),
"filled_quantity": 0.0,
"remaining_quantity": order.quantity,
"average_price": None
}
orders[order_id] = order_data
# Add to trading pair
trading_pairs[pair_id]["orders"].append(order_id)
# Simulate order processing
await asyncio.sleep(0.5) # Simulate processing delay
# Mark as filled (for demo)
order_data["status"] = "filled"
order_data["filled_quantity"] = order.quantity
order_data["remaining_quantity"] = 0.0
order_data["average_price"] = order.price or 0.00001 # Default price for demo
order_data["filled_at"] = datetime.utcnow().isoformat()
logger.info(f"Order created and filled: {order_id}")
return order_data
@app.get("/api/v1/orders")
async def list_orders():
"""List all orders"""
return {
"orders": list(orders.values()),
"total_orders": len(orders)
}
@app.get("/api/v1/orders/{order_id}")
async def get_order(order_id: str):
"""Get specific order information"""
if order_id not in orders:
raise HTTPException(status_code=404, detail="Order not found")
return orders[order_id]
@app.get("/api/v1/exchanges")
async def list_exchanges():
"""List all registered exchanges"""
return {
"exchanges": list(exchanges.values()),
"total_exchanges": len(exchanges)
}
@app.get("/api/v1/exchanges/{exchange_id}")
async def get_exchange(exchange_id: str):
"""Get specific exchange information"""
if exchange_id not in exchanges:
raise HTTPException(status_code=404, detail="Exchange not found")
return exchanges[exchange_id]
@app.post("/api/v1/market-data/{pair_id}/price")
async def update_market_price(pair_id: str, price_data: Dict[str, Any]):
"""Update market price for a trading pair"""
if pair_id not in trading_pairs:
raise HTTPException(status_code=404, detail="Trading pair not found")
pair = trading_pairs[pair_id]
pair["current_price"] = price_data.get("price")
pair["volume_24h"] = price_data.get("volume", pair["volume_24h"])
pair["last_price_update"] = datetime.utcnow().isoformat()
return {
"pair_id": pair_id,
"current_price": pair["current_price"],
"updated_at": pair["last_price_update"]
}
@app.get("/api/v1/market-data")
async def get_market_data():
"""Get market data for all pairs"""
market_data = {}
for pair_id, pair in trading_pairs.items():
market_data[pair_id] = {
"symbol": pair["symbol"],
"current_price": pair.get("current_price"),
"volume_24h": pair.get("volume_24h"),
"last_update": pair.get("last_price_update")
}
return {
"market_data": market_data,
"total_pairs": len(market_data),
"generated_at": datetime.utcnow().isoformat()
}
# Background task for simulating market data
async def simulate_market_data():
"""Background task to simulate market data updates"""
while True:
await asyncio.sleep(30) # Update every 30 seconds
for pair_id, pair in trading_pairs.items():
if pair["status"] == "active":
# Simulate price changes
import random
base_price = 0.00001 # Base price for AITBC
variation = random.uniform(-0.02, 0.02) # ±2% variation
new_price = round(base_price * (1 + variation), 8)
pair["current_price"] = new_price
pair["volume_24h"] += random.uniform(100, 1000)
pair["last_price_update"] = datetime.utcnow().isoformat()
# Start background task on startup
@app.on_event("startup")
async def startup_event():
logger.info("Starting AITBC Exchange Integration Service")
# Start background market data simulation
asyncio.create_task(simulate_market_data())
@app.on_event("shutdown")
async def shutdown_event():
logger.info("Shutting down AITBC Exchange Integration Service")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8010, log_level="info")