Appearance
Architecture
AceTrader is a global low-latency cryptocurrency price feed and paper trading service built on Cloudflare Workers. It provides real-time and historical price data for 26+ crypto assets via Hyperliquid exchange.
Key Patterns
Event-Driven Order Execution
All orders flow through a deterministic pricing pipeline. Every order uses Math.floor(Date.now() / 1000) as its target timestamp — no "latest available" price, always a specific second.
Market Order Flow
1. User submits market order at T=100
2. UserPortfolio queries PriceAlert.getPriceData(T=100)
3a. Price available → Execute immediately
3b. Price NOT available →
├─ Store as PENDING in SQLite
├─ Register price arrival alert
└─ Return { status: 'PENDING', orderId }
4. PriceAlert alarm fetches T=100 from PriceCollector
5. PriceAlert triggers UserPortfolio.onPriceArrival(T=100, prices)
6. Execute all pending orders for T=100Limit Order Flow
1. User submits limit order at T=100
2. UserPortfolio queries PriceAlert.getPriceData(T=100)
3a. Price available + target met → Execute immediately
3b. Otherwise →
├─ Store limit order with reserved margin
├─ Register LIMIT_ORDER alert
└─ Return limitOrderId + status=PENDING
4. PriceAlert evaluates when price crosses threshold
5. PriceAlert triggers UserPortfolio.onAlert()
6. Execute limit order with trigger pricePriceAlert as Read Replica
UserPortfolio calls getPriceAlertDO().getPriceData() for all price queries. The in-memory cache in PriceAlert reduces load on PriceCollector.
Two Alert Types
- PRICE_CONDITION: Triggers when price crosses a threshold (limit orders, stop loss, take profit)
- PRICE_ARRIVAL: Triggers when a timestamp's price data arrives (market orders, close orders)
Geolocation Routing
DOs use automatic location hint detection via Cloudflare cf properties. Self-discovery via cdn-cgi/trace. Naming convention: ${locationHint} (e.g., wnam, weur, apac).
AccountValueTracker Callback Flow
UserPaperTradePortfolio AccountValueAggregationTracker PriceCollector
│ │ │
│ subscribe(capital, userId) │ │
│───────────────────────────────────>│ │
│ │ │
│ updateCoefficients(snapshot) │ │
│───────────────────────────────────>│ (on each position change) │
│ │ │
│ │ alarm() every 5 min │
│ │ getPriceData(range) ──────>│
│ │ <── prices[] │
│ │ │
│ │ Calculate account values │
│ │ using coefficient formula │
│ │ │
│ onHourlyExtremes(params) │ (at hour boundary) │
│<───────────────────────────────────│ │
│ │ │
│ onDailyExtremesFinalized() │ (at UTC midnight) │
│<───────────────────────────────────│ │
│ │ │
│ Check MLL breach │ │
│ if bottom <= minBalance: │ │
│ failChallenge() │ │Formula: accountValue = constantTerm + Σ(coefficient[token] × price[token])