```
chore: enhance .gitignore and remove obsolete documentation files - Reorganize .gitignore with categorized sections for better maintainability - Add comprehensive ignore patterns for Python, Node.js, databases, logs, and build artifacts - Add project-specific ignore rules for coordinator, explorer, and deployment files - Remove outdated documentation: BITCOIN-WALLET-SETUP.md, LOCAL_ASSETS_SUMMARY.md, README-CONTAINER-DEPLOYMENT.md, README-DOMAIN-DEPLOYMENT.md ```
This commit is contained in:
229
apps/trade-exchange/exchange_api.py
Normal file
229
apps/trade-exchange/exchange_api.py
Normal file
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
FastAPI backend for the AITBC Trade Exchange
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Optional
|
||||
from fastapi import FastAPI, Depends, HTTPException, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import desc, func, and_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from database import init_db, get_db_session
|
||||
from models import User, Order, Trade, Balance
|
||||
|
||||
# Initialize FastAPI app
|
||||
app = FastAPI(title="AITBC Trade Exchange API", version="1.0.0")
|
||||
|
||||
# Add CORS middleware
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Pydantic models
|
||||
class OrderCreate(BaseModel):
|
||||
order_type: str # 'BUY' or 'SELL'
|
||||
amount: float
|
||||
price: float
|
||||
|
||||
class OrderResponse(BaseModel):
|
||||
id: int
|
||||
order_type: str
|
||||
amount: float
|
||||
price: float
|
||||
total: float
|
||||
filled: float
|
||||
remaining: float
|
||||
status: str
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class TradeResponse(BaseModel):
|
||||
id: int
|
||||
amount: float
|
||||
price: float
|
||||
total: float
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class OrderBookResponse(BaseModel):
|
||||
buys: List[OrderResponse]
|
||||
sells: List[OrderResponse]
|
||||
|
||||
# Initialize database on startup
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
init_db()
|
||||
|
||||
# Create mock data if database is empty
|
||||
db = get_db_session()
|
||||
try:
|
||||
# Check if we have any trades
|
||||
if db.query(Trade).count() == 0:
|
||||
create_mock_trades(db)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def create_mock_trades(db: Session):
|
||||
"""Create some mock trades for demonstration"""
|
||||
import random
|
||||
|
||||
# Create mock trades over the last hour
|
||||
now = datetime.utcnow()
|
||||
trades = []
|
||||
|
||||
for i in range(20):
|
||||
# Generate random trade data
|
||||
amount = random.uniform(10, 500)
|
||||
price = random.uniform(0.000009, 0.000012)
|
||||
total = amount * price
|
||||
|
||||
trade = Trade(
|
||||
buyer_id=1, # Mock user ID
|
||||
seller_id=2, # Mock user ID
|
||||
order_id=1, # Mock order ID
|
||||
amount=amount,
|
||||
price=price,
|
||||
total=total,
|
||||
trade_hash=f"mock_tx_{i:04d}",
|
||||
created_at=now - timedelta(minutes=random.randint(0, 60))
|
||||
)
|
||||
trades.append(trade)
|
||||
|
||||
db.add_all(trades)
|
||||
db.commit()
|
||||
print(f"Created {len(trades)} mock trades")
|
||||
|
||||
@app.get("/api/trades/recent", response_model=List[TradeResponse])
|
||||
def get_recent_trades(limit: int = 20, db: Session = Depends(get_db_session)):
|
||||
"""Get recent trades"""
|
||||
trades = db.query(Trade).order_by(desc(Trade.created_at)).limit(limit).all()
|
||||
return trades
|
||||
|
||||
@app.get("/api/orders/orderbook", response_model=OrderBookResponse)
|
||||
def get_orderbook(db: Session = Depends(get_db_session)):
|
||||
"""Get current order book"""
|
||||
|
||||
# Get open buy orders (sorted by price descending)
|
||||
buys = db.query(Order).filter(
|
||||
and_(Order.order_type == 'BUY', Order.status == 'OPEN')
|
||||
).order_by(desc(Order.price)).limit(20).all()
|
||||
|
||||
# Get open sell orders (sorted by price ascending)
|
||||
sells = db.query(Order).filter(
|
||||
and_(Order.order_type == 'SELL', Order.status == 'OPEN')
|
||||
).order_by(Order.price).limit(20).all()
|
||||
|
||||
return OrderBookResponse(buys=buys, sells=sells)
|
||||
|
||||
@app.post("/api/orders", response_model=OrderResponse)
|
||||
def create_order(order: OrderCreate, db: Session = Depends(get_db_session)):
|
||||
"""Create a new order"""
|
||||
|
||||
# Validate order type
|
||||
if order.order_type not in ['BUY', 'SELL']:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Order type must be 'BUY' or 'SELL'"
|
||||
)
|
||||
|
||||
# Create order
|
||||
total = order.amount * order.price
|
||||
db_order = Order(
|
||||
user_id=1, # TODO: Get from authentication
|
||||
order_type=order.order_type,
|
||||
amount=order.amount,
|
||||
price=order.price,
|
||||
total=total,
|
||||
remaining=order.amount
|
||||
)
|
||||
|
||||
db.add(db_order)
|
||||
db.commit()
|
||||
db.refresh(db_order)
|
||||
|
||||
# Try to match the order
|
||||
try_match_order(db_order, db)
|
||||
|
||||
return db_order
|
||||
|
||||
def try_match_order(order: Order, db: Session):
|
||||
"""Try to match an order with existing orders"""
|
||||
|
||||
if order.order_type == 'BUY':
|
||||
# Match with sell orders at same or lower price
|
||||
matching_orders = db.query(Order).filter(
|
||||
and_(
|
||||
Order.order_type == 'SELL',
|
||||
Order.status == 'OPEN',
|
||||
Order.price <= order.price
|
||||
)
|
||||
).order_by(Order.price).all()
|
||||
else:
|
||||
# Match with buy orders at same or higher price
|
||||
matching_orders = db.query(Order).filter(
|
||||
and_(
|
||||
Order.order_type == 'BUY',
|
||||
Order.status == 'OPEN',
|
||||
Order.price >= order.price
|
||||
)
|
||||
).order_by(desc(Order.price)).all()
|
||||
|
||||
for match in matching_orders:
|
||||
if order.remaining <= 0:
|
||||
break
|
||||
|
||||
# Calculate trade amount
|
||||
trade_amount = min(order.remaining, match.remaining)
|
||||
trade_total = trade_amount * match.price
|
||||
|
||||
# Create trade record
|
||||
trade = Trade(
|
||||
buyer_id=order.user_id if order.order_type == 'BUY' else match.user_id,
|
||||
seller_id=match.user_id if order.order_type == 'BUY' else order.user_id,
|
||||
order_id=order.id,
|
||||
amount=trade_amount,
|
||||
price=match.price,
|
||||
total=trade_total,
|
||||
trade_hash=f"trade_{datetime.utcnow().timestamp()}"
|
||||
)
|
||||
|
||||
db.add(trade)
|
||||
|
||||
# Update orders
|
||||
order.filled += trade_amount
|
||||
order.remaining -= trade_amount
|
||||
match.filled += trade_amount
|
||||
match.remaining -= trade_amount
|
||||
|
||||
# Update order statuses
|
||||
if order.remaining <= 0:
|
||||
order.status = 'FILLED'
|
||||
else:
|
||||
order.status = 'PARTIALLY_FILLED'
|
||||
|
||||
if match.remaining <= 0:
|
||||
match.status = 'FILLED'
|
||||
else:
|
||||
match.status = 'PARTIALLY_FILLED'
|
||||
|
||||
db.commit()
|
||||
|
||||
@app.get("/api/health")
|
||||
def health_check():
|
||||
"""Health check endpoint"""
|
||||
return {"status": "ok", "timestamp": datetime.utcnow()}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=3003)
|
||||
Reference in New Issue
Block a user