/** * Synor Perpetuals Trading Engine * Leveraged trading with 2x-100x positions * * Port: 17510 (REST), 17511 (WebSocket) * Endpoints: * GET /health - Health check * GET /markets - Available markets * GET /market/:id - Market details * GET /positions/:address - User positions * GET /funding - Current funding rates * POST /order - Place order (simulated) */ const http = require('http'); const PORT = process.env.PORT || 17510; const WS_PORT = process.env.WS_PORT || 17511; const ORACLE_URL = process.env.ORACLE_URL || 'http://localhost:17500'; const MIN_LEVERAGE = parseInt(process.env.MIN_LEVERAGE) || 2; const MAX_LEVERAGE = parseInt(process.env.MAX_LEVERAGE) || 100; const MAINTENANCE_MARGIN_BPS = parseInt(process.env.MAINTENANCE_MARGIN_BPS) || 50; const FUNDING_INTERVAL_HOURS = parseInt(process.env.FUNDING_INTERVAL_HOURS) || 8; // Markets (in-memory state) const markets = new Map([ [1, { id: 1, symbol: 'BTC/USD', tickSize: 0.1, lotSize: 0.001, maxLeverage: MAX_LEVERAGE }], [2, { id: 2, symbol: 'ETH/USD', tickSize: 0.01, lotSize: 0.01, maxLeverage: MAX_LEVERAGE }], [3, { id: 3, symbol: 'SYNOR/USD', tickSize: 0.0001, lotSize: 1.0, maxLeverage: 50 }], [4, { id: 4, symbol: 'SOL/USD', tickSize: 0.01, lotSize: 0.1, maxLeverage: MAX_LEVERAGE }], ]); // Open positions (in-memory) const positions = new Map(); let positionIdCounter = 1; // Funding rates (simulated) const fundingRates = new Map([ [1, { marketId: 1, rate: 0.0001, nextFundingTime: Date.now() + 8 * 3600 * 1000 }], [2, { marketId: 2, rate: 0.00012, nextFundingTime: Date.now() + 8 * 3600 * 1000 }], [3, { marketId: 3, rate: 0.0002, nextFundingTime: Date.now() + 8 * 3600 * 1000 }], [4, { marketId: 4, rate: 0.00008, nextFundingTime: Date.now() + 8 * 3600 * 1000 }], ]); // Insurance fund (simulated) let insuranceFund = 10000000; // $10M // Calculate PnL function calculatePnL(position, currentPrice) { const priceDiff = currentPrice - position.entryPrice; const multiplier = position.direction === 'long' ? 1 : -1; return priceDiff * position.size * multiplier; } // Calculate margin ratio function calculateMarginRatio(position, currentPrice) { const pnl = calculatePnL(position, currentPrice); const equity = position.collateral + pnl; const notional = position.size * currentPrice; return (equity / notional) * 10000; // in BPS } // Parse JSON body function parseBody(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => { try { resolve(body ? JSON.parse(body) : {}); } catch (e) { reject(e); } }); }); } // HTTP Server const server = http.createServer(async (req, res) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = new URL(req.url, `http://localhost:${PORT}`); const path = url.pathname; try { // Health check if (path === '/health') { res.writeHead(200); res.end(JSON.stringify({ status: 'healthy', markets: markets.size, openPositions: positions.size, insuranceFund, timestamp: Date.now(), })); return; } // All markets if (path === '/markets') { res.writeHead(200); res.end(JSON.stringify({ markets: Array.from(markets.values()), config: { minLeverage: MIN_LEVERAGE, maxLeverage: MAX_LEVERAGE, maintenanceMarginBps: MAINTENANCE_MARGIN_BPS }, })); return; } // Single market if (path.startsWith('/market/')) { const marketId = parseInt(path.slice(8)); const market = markets.get(marketId); if (market) { const funding = fundingRates.get(marketId); res.writeHead(200); res.end(JSON.stringify({ ...market, funding })); } else { res.writeHead(404); res.end(JSON.stringify({ error: 'Market not found' })); } return; } // User positions if (path.startsWith('/positions/')) { const address = path.slice(11); const userPositions = Array.from(positions.values()).filter(p => p.owner === address); res.writeHead(200); res.end(JSON.stringify({ address, positions: userPositions })); return; } // Funding rates if (path === '/funding') { res.writeHead(200); res.end(JSON.stringify({ rates: Array.from(fundingRates.values()), interval: `${FUNDING_INTERVAL_HOURS}h`, })); return; } // Insurance fund if (path === '/insurance') { res.writeHead(200); res.end(JSON.stringify({ insuranceFund, timestamp: Date.now() })); return; } // Place order (simulated) if (path === '/order' && req.method === 'POST') { const body = await parseBody(req); const { marketId, direction, size, collateral, leverage, owner } = body; // Validate if (!marketId || !direction || !size || !collateral || !leverage || !owner) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing required fields' })); return; } if (leverage < MIN_LEVERAGE || leverage > MAX_LEVERAGE) { res.writeHead(400); res.end(JSON.stringify({ error: `Leverage must be between ${MIN_LEVERAGE}x and ${MAX_LEVERAGE}x` })); return; } const market = markets.get(marketId); if (!market) { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid market' })); return; } // Create position (simulated - in production would interact with smart contract) const positionId = positionIdCounter++; const position = { id: positionId, owner, marketId, direction, size, collateral, entryPrice: 45000, // Would fetch from oracle leverage, openedAt: Date.now(), isOpen: true, }; positions.set(positionId, position); res.writeHead(201); res.end(JSON.stringify({ success: true, position })); return; } // Default res.writeHead(404); res.end(JSON.stringify({ error: 'Not found' })); } catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); } }); server.listen(PORT, '0.0.0.0', () => { console.log(`Perpetuals Engine running on port ${PORT}`); console.log(`Leverage: ${MIN_LEVERAGE}x - ${MAX_LEVERAGE}x`); console.log(`Maintenance Margin: ${MAINTENANCE_MARGIN_BPS / 100}%`); console.log(`Markets: ${markets.size}`); console.log('Endpoints:'); console.log(' GET /health - Health check'); console.log(' GET /markets - Available markets'); console.log(' GET /market/:id - Market details'); console.log(' GET /positions/:address - User positions'); console.log(' GET /funding - Current funding rates'); console.log(' POST /order - Place order'); });