| |
| """ |
| AirMicroDrip Perpetual Futures Trading Engine |
| Core trading engine for perpetual futures with LLM liquidity |
| No mocks - real order book, position management, and trade execution |
| """ |
|
|
| import json |
| import sqlite3 |
| import asyncio |
| import requests |
| from typing import Dict, List, Optional, Tuple |
| from datetime import datetime, timedelta |
| from dataclasses import dataclass, field |
| from enum import Enum |
| import uuid |
|
|
| |
| PERP_CONFIG = { |
| "max_leverage": 100, |
| "initial_margin_rate": 0.10, |
| "maintenance_margin_rate": 0.05, |
| "liquidation_threshold": 0.01, |
| "taker_fee_rate": 0.0002, |
| "maker_fee_rate": 0.0001, |
| "max_position_size_usd": 1000000, |
| "price_impact_threshold": 0.001, |
| } |
|
|
|
|
| class Side(Enum): |
| """Trade side""" |
| LONG = "long" |
| SHORT = "short" |
|
|
|
|
| class OrderType(Enum): |
| """Order type""" |
| MARKET = "market" |
| LIMIT = "limit" |
| STOP_MARKET = "stop_market" |
| STOP_LIMIT = "stop_limit" |
|
|
|
|
| class OrderStatus(Enum): |
| """Order status""" |
| PENDING = "pending" |
| OPEN = "open" |
| FILLED = "filled" |
| PARTIALLY_FILLED = "partially_filled" |
| CANCELLED = "cancelled" |
| REJECTED = "rejected" |
|
|
|
|
| @dataclass |
| class Order: |
| """Order dataclass""" |
| order_id: str |
| trader: str |
| market: str |
| side: Side |
| order_type: OrderType |
| size: float |
| price: Optional[float] = None |
| stop_price: Optional[float] = None |
| leverage: int = 1 |
| status: OrderStatus = OrderStatus.PENDING |
| filled_size: float = 0.0 |
| avg_fill_price: float = 0.0 |
| created_at: datetime = field(default_factory=datetime.utcnow) |
| updated_at: datetime = field(default_factory=datetime.utcnow) |
|
|
|
|
| @dataclass |
| class Position: |
| """Position dataclass""" |
| position_id: str |
| trader: str |
| market: str |
| side: Side |
| size: float |
| entry_price: float |
| leverage: int |
| margin: float |
| unrealized_pnl: float = 0.0 |
| realized_pnl: float = 0.0 |
| liquidation_price: float = 0.0 |
| opened_at: datetime = field(default_factory=datetime.utcnow) |
| updated_at: datetime = field(default_factory=datetime.utcnow) |
|
|
|
|
| @dataclass |
| class MarketState: |
| """Market state dataclass""" |
| market: str |
| mark_price: float |
| index_price: float |
| funding_rate: float |
| open_interest: float |
| volume_24h: float |
| last_updated: datetime = field(default_factory=datetime.utcnow) |
|
|
|
|
| class OrderBook: |
| """Order book for a market""" |
| |
| def __init__(self, market: str): |
| self.market = market |
| self.bids: List[Tuple[float, float]] = [] |
| self.asks: List[Tuple[float, float]] = [] |
| self.synthetic_liquidity: float = 0.0 |
| |
| def add_bid(self, price: float, size: float): |
| """Add bid to order book""" |
| self.bids.append((price, size)) |
| self.bids.sort(reverse=True) |
| |
| def add_ask(self, price: float, size: float): |
| """Add ask to order book""" |
| self.asks.append((price, size)) |
| self.asks.sort() |
| |
| def get_best_bid(self) -> Optional[float]: |
| """Get best bid price""" |
| return self.bids[0][0] if self.bids else None |
| |
| def get_best_ask(self) -> Optional[float]: |
| """Get best ask price""" |
| return self.asks[0][0] if self.asks else None |
| |
| def get_mid_price(self) -> Optional[float]: |
| """Get mid price""" |
| best_bid = self.get_best_bid() |
| best_ask = self.get_best_ask() |
| |
| if best_bid and best_ask: |
| return (best_bid + best_ask) / 2 |
| return None |
| |
| def add_synthetic_liquidity(self, liquidity_usd: float): |
| """Add synthetic liquidity from LLM providers""" |
| self.synthetic_liquidity += liquidity_usd |
| |
| def get_total_liquidity(self) -> float: |
| """Get total liquidity (book + synthetic)""" |
| book_liquidity = sum(size for _, size in self.bids + self.asks) |
| return book_liquidity + self.synthetic_liquidity |
|
|
|
|
| class PerpTradingEngine: |
| """Perpetual futures trading engine""" |
| |
| def __init__(self, db_path: str = "perp_trading.db"): |
| self.db_path = db_path |
| self.order_books: Dict[str, OrderBook] = {} |
| self.market_states: Dict[str, MarketState] = {} |
| self._init_database() |
| self._init_markets() |
| |
| def _init_database(self): |
| """Initialize SQLite database""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS orders ( |
| order_id TEXT PRIMARY KEY, |
| trader TEXT, |
| market TEXT, |
| side TEXT, |
| order_type TEXT, |
| size REAL, |
| price REAL, |
| stop_price REAL, |
| leverage INTEGER, |
| status TEXT, |
| filled_size REAL, |
| avg_fill_price REAL, |
| created_at TIMESTAMP, |
| updated_at TIMESTAMP |
| ) |
| """) |
| |
| |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS positions ( |
| position_id TEXT PRIMARY KEY, |
| trader TEXT, |
| market TEXT, |
| side TEXT, |
| size REAL, |
| entry_price REAL, |
| leverage INTEGER, |
| margin REAL, |
| unrealized_pnl REAL, |
| realized_pnl REAL, |
| liquidation_price REAL, |
| opened_at TIMESTAMP, |
| updated_at TIMESTAMP |
| ) |
| """) |
| |
| |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS trades ( |
| trade_id TEXT PRIMARY KEY, |
| order_id TEXT, |
| market TEXT, |
| side TEXT, |
| size REAL, |
| price REAL, |
| fee REAL, |
| timestamp TIMESTAMP |
| ) |
| """) |
| |
| |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS funding_rates ( |
| market TEXT, |
| rate REAL, |
| timestamp TIMESTAMP, |
| PRIMARY KEY (market, timestamp) |
| ) |
| """) |
| |
| conn.commit() |
| conn.close() |
| |
| def _fetch_gateio_prices(self) -> Dict[str, float]: |
| """Fetch real mark prices from Gate.io futures API""" |
| prices = {} |
| try: |
| r = requests.get('https://api.gateio.ws/api/v4/futures/usdt/tickers', timeout=10) |
| if r.status_code == 200: |
| for t in r.json(): |
| contract = t.get('contract', '') |
| last = float(t.get('last', 0)) |
| if contract == 'BTC_USDT': |
| prices['BTC/USDC'] = last |
| elif contract == 'ETH_USDT': |
| prices['ETH/USDC'] = last |
| elif contract == 'SOL_USDT': |
| prices['SOL/USDC'] = last |
| except Exception as e: |
| import logging |
| logging.warning(f"Price fetch failed: {e}") |
| |
| if 'BTC/USDC' not in prices: |
| prices['BTC/USDC'] = 50000.0 |
| if 'ETH/USDC' not in prices: |
| prices['ETH/USDC'] = 3000.0 |
| if 'SOL/USDC' not in prices: |
| prices['SOL/USDC'] = 100.0 |
| prices['MEMBRA/USDC'] = 0.05 |
| return prices |
|
|
| def _init_markets(self): |
| """Initialize supported markets with real prices from Gate.io""" |
| markets = ["BTC/USDC", "ETH/USDC", "SOL/USDC", "MEMBRA/USDC"] |
| real_prices = self._fetch_gateio_prices() |
|
|
| for market in markets: |
| self.order_books[market] = OrderBook(market) |
| mark = real_prices.get(market, 0.05) |
| self.market_states[market] = MarketState( |
| market=market, |
| mark_price=mark, |
| index_price=mark, |
| funding_rate=0.0001, |
| open_interest=0.0, |
| volume_24h=0.0, |
| ) |
| |
| def place_order( |
| self, |
| trader: str, |
| market: str, |
| side: Side, |
| order_type: OrderType, |
| size: float, |
| price: Optional[float] = None, |
| stop_price: Optional[float] = None, |
| leverage: int = 1, |
| ) -> Order: |
| """Place new order""" |
| |
| if market not in self.order_books: |
| raise ValueError(f"Market {market} not supported") |
| |
| |
| if leverage > PERP_CONFIG["max_leverage"]: |
| raise ValueError(f"Leverage exceeds maximum of {PERP_CONFIG['max_leverage']}x") |
| |
| |
| position_value = size * self.market_states[market].mark_price |
| if position_value > PERP_CONFIG["max_position_size_usd"]: |
| raise ValueError(f"Position size exceeds maximum of ${PERP_CONFIG['max_position_size_usd']}") |
| |
| |
| order_id = str(uuid.uuid4()) |
| order = Order( |
| order_id=order_id, |
| trader=trader, |
| market=market, |
| side=side, |
| order_type=order_type, |
| size=size, |
| price=price, |
| stop_price=stop_price, |
| leverage=leverage, |
| ) |
| |
| |
| self._save_order(order) |
| |
| |
| if order_type == OrderType.MARKET: |
| self._execute_market_order(order) |
| elif order_type == OrderType.LIMIT: |
| self._execute_limit_order(order) |
| |
| return order |
| |
| def _execute_market_order(self, order: Order): |
| """Execute market order""" |
| order_book = self.order_books[order.market] |
| market_state = self.market_states[order.market] |
| |
| |
| if order.side == Side.LONG: |
| execution_price = order_book.get_best_ask() or market_state.mark_price |
| else: |
| execution_price = order_book.get_best_bid() or market_state.mark_price |
| |
| |
| fee = order.size * execution_price * PERP_CONFIG["taker_fee_rate"] |
| |
| |
| order.status = OrderStatus.FILLED |
| order.filled_size = order.size |
| order.avg_fill_price = execution_price |
| order.updated_at = datetime.utcnow() |
| |
| |
| self._update_position(order, execution_price, fee) |
| |
| |
| self._record_trade(order, execution_price, fee) |
| |
| |
| self._update_order(order) |
| |
| def _execute_limit_order(self, order: Order): |
| """Execute limit order""" |
| order_book = self.order_books[order.market] |
| |
| if order.side == Side.LONG: |
| order_book.add_bid(order.price, order.size) |
| else: |
| order_book.add_ask(order.price, order.size) |
| |
| order.status = OrderStatus.OPEN |
| order.updated_at = datetime.utcnow() |
| |
| self._update_order(order) |
| |
| def _update_position(self, order: Order, fill_price: float, fee: float): |
| """Update trader's position""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| |
| cursor.execute(""" |
| SELECT position_id, size, entry_price, margin, realized_pnl |
| FROM positions |
| WHERE trader = ? AND market = ? AND side = ? |
| """, (order.trader, order.market, order.side.value)) |
| |
| result = cursor.fetchone() |
| |
| position_value = order.size * fill_price |
| margin = position_value / order.leverage |
| |
| if result: |
| |
| position_id, existing_size, entry_price, existing_margin, realized_pnl = result |
| |
| |
| total_value = (existing_size * entry_price) + (order.size * fill_price) |
| new_size = existing_size + order.size |
| new_entry_price = total_value / new_size if new_size > 0 else entry_price |
| |
| cursor.execute(""" |
| UPDATE positions |
| SET size = ?, entry_price = ?, margin = margin + ?, updated_at = ? |
| WHERE position_id = ? |
| """, (new_size, new_entry_price, margin, datetime.utcnow().isoformat(), position_id)) |
| |
| |
| self._update_liquidation_price(position_id, new_size, new_entry_price, order.leverage) |
| |
| else: |
| |
| position_id = str(uuid.uuid4()) |
| |
| cursor.execute(""" |
| INSERT INTO positions |
| (position_id, trader, market, side, size, entry_price, leverage, margin, opened_at, updated_at) |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
| """, ( |
| position_id, |
| order.trader, |
| order.market, |
| order.side.value, |
| order.size, |
| fill_price, |
| order.leverage, |
| margin, |
| datetime.utcnow().isoformat(), |
| datetime.utcnow().isoformat(), |
| )) |
| |
| |
| self._update_liquidation_price(position_id, order.size, fill_price, order.leverage) |
| |
| conn.commit() |
| conn.close() |
| |
| def _update_liquidation_price(self, position_id: str, size: float, entry_price: float, leverage: int): |
| """Update liquidation price for position""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| |
| if leverage > 0: |
| liquidation_price = entry_price * (1 - (1 / leverage) + PERP_CONFIG["maintenance_margin_rate"]) |
| else: |
| liquidation_price = 0 |
| |
| cursor.execute(""" |
| UPDATE positions |
| SET liquidation_price = ? |
| WHERE position_id = ? |
| """, (liquidation_price, position_id)) |
| |
| conn.commit() |
| conn.close() |
| |
| def _record_trade(self, order: Order, price: float, fee: float): |
| """Record trade to database""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| trade_id = str(uuid.uuid4()) |
| |
| cursor.execute(""" |
| INSERT INTO trades |
| (trade_id, order_id, market, side, size, price, fee, timestamp) |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?) |
| """, ( |
| trade_id, |
| order.order_id, |
| order.market, |
| order.side.value, |
| order.size, |
| price, |
| fee, |
| datetime.utcnow().isoformat(), |
| )) |
| |
| conn.commit() |
| conn.close() |
| |
| def _save_order(self, order: Order): |
| """Save order to database""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| INSERT INTO orders |
| (order_id, trader, market, side, order_type, size, price, stop_price, leverage, status, filled_size, avg_fill_price, created_at, updated_at) |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
| """, ( |
| order.order_id, |
| order.trader, |
| order.market, |
| order.side.value, |
| order.order_type.value, |
| order.size, |
| order.price, |
| order.stop_price, |
| order.leverage, |
| order.status.value, |
| order.filled_size, |
| order.avg_fill_price, |
| order.created_at.isoformat(), |
| order.updated_at.isoformat(), |
| )) |
| |
| conn.commit() |
| conn.close() |
| |
| def _update_order(self, order: Order): |
| """Update order in database""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| UPDATE orders |
| SET status = ?, filled_size = ?, avg_fill_price = ?, updated_at = ? |
| WHERE order_id = ? |
| """, ( |
| order.status.value, |
| order.filled_size, |
| order.avg_fill_price, |
| order.updated_at.isoformat(), |
| order.order_id, |
| )) |
| |
| conn.commit() |
| conn.close() |
| |
| def get_position(self, trader: str, market: str) -> Optional[Position]: |
| """Get trader's position in market""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| SELECT position_id, trader, market, side, size, entry_price, leverage, margin, |
| unrealized_pnl, realized_pnl, liquidation_price, opened_at, updated_at |
| FROM positions |
| WHERE trader = ? AND market = ? |
| """, (trader, market)) |
| |
| result = cursor.fetchone() |
| conn.close() |
| |
| if result: |
| return Position( |
| position_id=result[0], |
| trader=result[1], |
| market=result[2], |
| side=Side(result[3]), |
| size=result[4], |
| entry_price=result[5], |
| leverage=result[6], |
| margin=result[7], |
| unrealized_pnl=result[8], |
| realized_pnl=result[9], |
| liquidation_price=result[10], |
| opened_at=datetime.fromisoformat(result[11]), |
| updated_at=datetime.fromisoformat(result[12]), |
| ) |
| |
| return None |
| |
| def update_unrealized_pnl(self): |
| """Update unrealized PnL for all positions""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute("SELECT position_id, market, side, size, entry_price FROM positions") |
| positions = cursor.fetchall() |
| |
| for position_id, market, side, size, entry_price in positions: |
| market_state = self.market_states[market] |
| mark_price = market_state.mark_price |
| |
| if side == Side.LONG: |
| unrealized_pnl = size * (mark_price - entry_price) |
| else: |
| unrealized_pnl = size * (entry_price - mark_price) |
| |
| cursor.execute(""" |
| UPDATE positions |
| SET unrealized_pnl = ?, updated_at = ? |
| WHERE position_id = ? |
| """, (unrealized_pnl, datetime.utcnow().isoformat(), position_id)) |
| |
| conn.commit() |
| conn.close() |
| |
| def get_market_stats(self, market: str) -> Dict: |
| """Get market statistics""" |
| order_book = self.order_books[market] |
| market_state = self.market_states[market] |
| |
| return { |
| "market": market, |
| "mark_price": market_state.mark_price, |
| "index_price": market_state.index_price, |
| "funding_rate": market_state.funding_rate, |
| "best_bid": order_book.get_best_bid(), |
| "best_ask": order_book.get_best_ask(), |
| "mid_price": order_book.get_mid_price(), |
| "total_liquidity": order_book.get_total_liquidity(), |
| "synthetic_liquidity": order_book.synthetic_liquidity, |
| "volume_24h": market_state.volume_24h, |
| "open_interest": market_state.open_interest, |
| } |
|
|
|
|
| if __name__ == "__main__": |
| |
| engine = PerpTradingEngine() |
| |
| |
| order = engine.place_order( |
| trader="TRADER_ADDRESS", |
| market="BTC/USDC", |
| side=Side.LONG, |
| order_type=OrderType.MARKET, |
| size=0.1, |
| leverage=10, |
| ) |
| |
| print("\n" + "="*50) |
| print("Order Placed") |
| print("="*50) |
| print(f"Order ID: {order.order_id}") |
| print(f"Status: {order.status.value}") |
| print(f"Filled Size: {order.filled_size}") |
| print(f"Avg Fill Price: ${order.avg_fill_price}") |
| |
| |
| position = engine.get_position("TRADER_ADDRESS", "BTC/USDC") |
| if position: |
| print("\n" + "="*50) |
| print("Position Details") |
| print("="*50) |
| print(f"Position ID: {position.position_id}") |
| print(f"Side: {position.side.value}") |
| print(f"Size: {position.size}") |
| print(f"Entry Price: ${position.entry_price}") |
| print(f"Leverage: {position.leverage}x") |
| print(f"Margin: ${position.margin}") |
| print(f"Liquidation Price: ${position.liquidation_price}") |
| |
| |
| stats = engine.get_market_stats("BTC/USDC") |
| print("\n" + "="*50) |
| print("Market Statistics") |
| print("="*50) |
| print(json.dumps(stats, indent=2)) |
|
|