Spaces:
Running
Running
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 19)
da0ce7b verified Download scripts/traders/immediateWaveRider.ts from GaryLeckey/aureon-os: direct link, hf CLI and curl.
- Browser
- Download file 32 kB
-
https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/scripts/traders/immediateWaveRider.ts
- Command line
-
hf download hf://spaces/GaryLeckey/aureon-os/scripts/traders/immediateWaveRider.ts
-
curl -L -o immediateWaveRider.ts https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/scripts/traders/immediateWaveRider.ts
32 kB
| /** | |
| * π IMMEDIATE WAVE RIDER - LIVE MARKETS π | |
| * | |
| * AβZ β ZβA "They Can't Stop Them All!" | |
| * | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| * β IMMEDIATE ENTRY MODE β | |
| * β β | |
| * β β’ Enter 1 position per broker IMMEDIATELY on first scan β | |
| * β β’ Scout positions ride the wave from the start β | |
| * β β’ AβZ on odd cycles, ZβA on even cycles for new entries β | |
| * β β’ "They can't stop them all!" β | |
| * β β | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| * | |
| * Author: Gary Leckey - R&A Consulting | |
| */ | |
| import * as dotenv from 'dotenv'; | |
| import * as path from 'path'; | |
| import { fileURLToPath } from 'url'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| dotenv.config({ path: path.join(__dirname, '..', '.env') }); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Configuration | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const CONFIG = { | |
| BALANCE_PER_BROKER: 20, | |
| POSITION_SIZE_PCT: 0.10, // 10% per trade = Β£2 per position | |
| STOP_LOSS_PCT: 0.02, // 2% stop loss | |
| TAKE_PROFIT_PCT: 0.04, // 4% take profit | |
| MAX_POSITIONS_PER_BROKER: 5, | |
| SCAN_INTERVAL_MS: 5000, // 5 seconds | |
| SCOUT_ON_FIRST_SCAN: true, // Deploy scouts immediately | |
| }; | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Types | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| interface LivePrice { | |
| symbol: string; | |
| bid: number; | |
| ask: number; | |
| mid: number; | |
| spread: number; | |
| change24h?: number; | |
| } | |
| interface Position { | |
| broker: string; | |
| symbol: string; | |
| direction: 'LONG' | 'SHORT'; | |
| size: number; | |
| entryPrice: number; | |
| currentPrice: number; | |
| pnl: number; | |
| pnlPercent: number; | |
| taxFree: boolean; | |
| isScout: boolean; | |
| } | |
| interface BrokerState { | |
| name: string; | |
| emoji: string; | |
| balance: number; | |
| positions: number; | |
| wins: number; | |
| losses: number; | |
| pnl: number; | |
| taxFree: boolean; | |
| hasScout: boolean; | |
| scoutDeployed: boolean; | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Real Price Feeds | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // BINANCE - 20 top pairs | |
| async function fetchBinancePrices(): Promise<LivePrice[]> { | |
| const symbols = [ | |
| 'BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', | |
| 'DOGEUSDT', 'ADAUSDT', 'AVAXUSDT', 'DOTUSDT', 'LINKUSDT', | |
| 'MATICUSDT', 'LTCUSDT', 'NEARUSDT', 'UNIUSDT', 'AAVEUSDT', | |
| 'APTUSDT', 'ARBUSDT', 'OPUSDT', 'ATOMUSDT', 'SHIBUSDT' | |
| ]; | |
| try { | |
| const response = await fetch('https://api.binance.com/api/v3/ticker/24hr'); | |
| if (!response.ok) return []; | |
| const data = await response.json(); | |
| const prices: LivePrice[] = []; | |
| for (const sym of symbols) { | |
| const ticker = data.find((t: any) => t.symbol === sym); | |
| if (ticker) { | |
| const bid = parseFloat(ticker.bidPrice); | |
| const ask = parseFloat(ticker.askPrice); | |
| prices.push({ | |
| symbol: sym, | |
| bid, | |
| ask, | |
| mid: (bid + ask) / 2, | |
| spread: ask - bid, | |
| change24h: parseFloat(ticker.priceChangePercent) | |
| }); | |
| } | |
| } | |
| return prices; | |
| } catch { return []; } | |
| } | |
| // KRAKEN | |
| async function fetchKrakenPrices(): Promise<LivePrice[]> { | |
| const pairs = 'XXBTZGBP,XETHZGBP,XXBTZUSD,XETHZUSD,SOLUSD,XRPUSD,ADAUSD,DOTUSD,LINKUSD,ATOMUSD'; | |
| try { | |
| const response = await fetch(`https://api.kraken.com/0/public/Ticker?pair=${pairs}`); | |
| if (!response.ok) return []; | |
| const data = await response.json(); | |
| if (data.error?.length > 0) return []; | |
| const prices: LivePrice[] = []; | |
| for (const [key, ticker] of Object.entries(data.result) as any) { | |
| const bid = parseFloat(ticker.b[0]); | |
| const ask = parseFloat(ticker.a[0]); | |
| const open = parseFloat(ticker.o); | |
| const last = parseFloat(ticker.c[0]); | |
| prices.push({ | |
| symbol: key, | |
| bid, | |
| ask, | |
| mid: (bid + ask) / 2, | |
| spread: ask - bid, | |
| change24h: ((last - open) / open) * 100 | |
| }); | |
| } | |
| return prices; | |
| } catch { return []; } | |
| } | |
| // OKX | |
| async function fetchOKXPrices(): Promise<LivePrice[]> { | |
| const symbols = [ | |
| 'BTC-USDT', 'ETH-USDT', 'SOL-USDT', 'XRP-USDT', 'DOGE-USDT', | |
| 'ADA-USDT', 'DOT-USDT', 'AVAX-USDT', 'LINK-USDT', 'NEAR-USDT', | |
| 'ATOM-USDT', 'UNI-USDT', 'AAVE-USDT', 'LTC-USDT', 'ARB-USDT' | |
| ]; | |
| try { | |
| const response = await fetch('https://www.okx.com/api/v5/market/tickers?instType=SPOT'); | |
| if (!response.ok) return []; | |
| const data = await response.json(); | |
| if (!data.data) return []; | |
| const prices: LivePrice[] = []; | |
| for (const sym of symbols) { | |
| const ticker = data.data.find((t: any) => t.instId === sym); | |
| if (ticker) { | |
| const bid = parseFloat(ticker.bidPx); | |
| const ask = parseFloat(ticker.askPx); | |
| const open = parseFloat(ticker.open24h); | |
| const last = parseFloat(ticker.last); | |
| prices.push({ | |
| symbol: sym, | |
| bid, | |
| ask, | |
| mid: (bid + ask) / 2, | |
| spread: ask - bid, | |
| change24h: ((last - open) / open) * 100 | |
| }); | |
| } | |
| } | |
| return prices; | |
| } catch { return []; } | |
| } | |
| // COINBASE | |
| async function fetchCoinbasePrices(): Promise<LivePrice[]> { | |
| const symbols = ['BTC-GBP', 'ETH-GBP', 'SOL-GBP', 'BTC-USD', 'ETH-USD', 'SOL-USD', 'DOGE-USD', 'ADA-USD']; | |
| const prices: LivePrice[] = []; | |
| // Fetch in parallel | |
| const fetches = symbols.map(async sym => { | |
| try { | |
| const response = await fetch(`https://api.exchange.coinbase.com/products/${sym}/ticker`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const bid = parseFloat(data.bid); | |
| const ask = parseFloat(data.ask); | |
| return { | |
| symbol: sym, | |
| bid, | |
| ask, | |
| mid: (bid + ask) / 2, | |
| spread: ask - bid | |
| }; | |
| } | |
| } catch {} | |
| return null; | |
| }); | |
| const results = await Promise.all(fetches); | |
| return results.filter(p => p !== null) as LivePrice[]; | |
| } | |
| // BITSTAMP | |
| async function fetchBitstampPrices(): Promise<LivePrice[]> { | |
| const symbols = ['btcgbp', 'ethgbp', 'btcusd', 'ethusd', 'xrpusd', 'ltcusd', 'solusd']; | |
| const prices: LivePrice[] = []; | |
| const fetches = symbols.map(async sym => { | |
| try { | |
| const response = await fetch(`https://www.bitstamp.net/api/v2/ticker/${sym}/`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const bid = parseFloat(data.bid); | |
| const ask = parseFloat(data.ask); | |
| const open = parseFloat(data.open); | |
| const last = parseFloat(data.last); | |
| return { | |
| symbol: sym.toUpperCase(), | |
| bid, | |
| ask, | |
| mid: (bid + ask) / 2, | |
| spread: ask - bid, | |
| change24h: ((last - open) / open) * 100 | |
| }; | |
| } | |
| } catch {} | |
| return null; | |
| }); | |
| const results = await Promise.all(fetches); | |
| return results.filter(p => p !== null) as LivePrice[]; | |
| } | |
| // FOREX | |
| async function fetchForexPrices(): Promise<LivePrice[]> { | |
| try { | |
| const response = await fetch('https://api.exchangerate-api.com/v4/latest/USD'); | |
| if (!response.ok) return []; | |
| const data = await response.json(); | |
| const prices: LivePrice[] = []; | |
| const pairs = [ | |
| { base: 'EUR', symbol: 'EUR/USD' }, | |
| { base: 'GBP', symbol: 'GBP/USD' }, | |
| { base: 'JPY', symbol: 'USD/JPY' }, | |
| { base: 'CHF', symbol: 'USD/CHF' }, | |
| { base: 'AUD', symbol: 'AUD/USD' }, | |
| { base: 'CAD', symbol: 'USD/CAD' }, | |
| { base: 'NZD', symbol: 'NZD/USD' }, | |
| { base: 'SGD', symbol: 'USD/SGD' }, | |
| ]; | |
| for (const pair of pairs) { | |
| const rate = data.rates[pair.base]; | |
| if (rate) { | |
| let mid: number; | |
| if (pair.symbol.startsWith('USD/')) { | |
| mid = rate; | |
| } else { | |
| mid = 1 / rate; | |
| } | |
| const spread = mid * 0.0002; | |
| prices.push({ | |
| symbol: pair.symbol, | |
| bid: mid - spread / 2, | |
| ask: mid + spread / 2, | |
| mid, | |
| spread | |
| }); | |
| } | |
| } | |
| return prices; | |
| } catch { return []; } | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Immediate Wave Rider | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class ImmediateWaveRider { | |
| private positions: Map<string, Position> = new Map(); | |
| private brokers: Map<string, BrokerState> = new Map(); | |
| private scanDirection: 'AβZ' | 'ZβA' = 'AβZ'; | |
| private scans = 0; | |
| private startTime = new Date(); | |
| private totalPnL = 0; | |
| private wins = 0; | |
| private losses = 0; | |
| private priceHistory: Map<string, number[]> = new Map(); | |
| constructor() { | |
| this.initBrokers(); | |
| } | |
| private initBrokers() { | |
| const brokerList = [ | |
| { name: 'Binance', emoji: 'πͺ', taxFree: false }, | |
| { name: 'Kraken', emoji: 'π¦', taxFree: false }, | |
| { name: 'Coinbase', emoji: 'π ', taxFree: false }, | |
| { name: 'Bitstamp', emoji: 'π', taxFree: false }, | |
| { name: 'OKX', emoji: 'β', taxFree: false }, | |
| { name: 'OANDA', emoji: 'π±', taxFree: false }, | |
| { name: 'FXCM', emoji: 'πΉ', taxFree: false }, | |
| { name: 'IG', emoji: 'π', taxFree: true }, | |
| { name: 'CMC', emoji: 'π', taxFree: true }, | |
| { name: 'Capital', emoji: 'π', taxFree: false }, | |
| { name: 'Saxo', emoji: 'π¦', taxFree: false }, | |
| { name: 'IB', emoji: 'ποΈ', taxFree: false }, | |
| { name: 'Alpaca', emoji: 'π¦', taxFree: false }, | |
| { name: 'Gemini', emoji: 'π ', taxFree: false }, | |
| ]; | |
| for (const b of brokerList) { | |
| this.brokers.set(b.name, { | |
| name: b.name, | |
| emoji: b.emoji, | |
| balance: CONFIG.BALANCE_PER_BROKER, | |
| positions: 0, | |
| wins: 0, | |
| losses: 0, | |
| pnl: 0, | |
| taxFree: b.taxFree, | |
| hasScout: false, | |
| scoutDeployed: false | |
| }); | |
| } | |
| } | |
| async start(): Promise<void> { | |
| this.printHeader(); | |
| console.log('\n π‘ Connecting to live markets and deploying scouts...\n'); | |
| while (true) { | |
| this.scans++; | |
| this.scanDirection = this.scanDirection === 'AβZ' ? 'ZβA' : 'AβZ'; | |
| await this.executeScan(); | |
| this.displayStatus(); | |
| await new Promise(r => setTimeout(r, CONFIG.SCAN_INTERVAL_MS)); | |
| } | |
| } | |
| private async executeScan(): Promise<void> { | |
| // Fetch all prices in parallel | |
| const [binance, kraken, okx, coinbase, bitstamp, forex] = await Promise.all([ | |
| fetchBinancePrices(), | |
| fetchKrakenPrices(), | |
| fetchOKXPrices(), | |
| fetchCoinbasePrices(), | |
| fetchBitstampPrices(), | |
| fetchForexPrices() | |
| ]); | |
| // Sort by direction | |
| const sortFn = (a: LivePrice, b: LivePrice) => { | |
| const cmp = a.symbol.localeCompare(b.symbol); | |
| return this.scanDirection === 'AβZ' ? cmp : -cmp; | |
| }; | |
| // Process each exchange - sorted by AβZ or ZβA | |
| this.processPrices('Binance', binance.sort(sortFn)); | |
| this.processPrices('Kraken', kraken.sort(sortFn)); | |
| this.processPrices('OKX', okx.sort(sortFn)); | |
| this.processPrices('Coinbase', coinbase.sort(sortFn)); | |
| this.processPrices('Bitstamp', bitstamp.sort(sortFn)); | |
| // Forex goes to multiple brokers | |
| const sortedForex = forex.sort(sortFn); | |
| this.processPrices('OANDA', sortedForex); | |
| this.processPrices('FXCM', sortedForex); | |
| this.processPrices('IG', sortedForex); | |
| this.processPrices('CMC', sortedForex); | |
| this.processPrices('Saxo', sortedForex); | |
| this.processPrices('Capital', sortedForex); | |
| this.processPrices('IB', sortedForex); | |
| } | |
| private processPrices(broker: string, prices: LivePrice[]): void { | |
| const brokerState = this.brokers.get(broker); | |
| if (!brokerState || prices.length === 0) return; | |
| for (const price of prices) { | |
| const key = `${broker}:${price.symbol}`; | |
| // Track price history | |
| if (!this.priceHistory.has(key)) { | |
| this.priceHistory.set(key, []); | |
| } | |
| const history = this.priceHistory.get(key)!; | |
| history.push(price.mid); | |
| if (history.length > 50) history.shift(); | |
| // Check existing position | |
| const existingPos = this.positions.get(key); | |
| if (existingPos) { | |
| this.updatePosition(key, price.mid); | |
| continue; | |
| } | |
| } | |
| // SCOUT DEPLOYMENT: Deploy 1 scout per broker immediately if none exists | |
| if (!brokerState.scoutDeployed && prices.length > 0) { | |
| // Pick the first asset in sorted order (AβZ or ZβA direction) | |
| const scoutPrice = prices[0]; | |
| this.deployScout(broker, scoutPrice); | |
| } | |
| // Additional entries based on 24h momentum | |
| for (const price of prices) { | |
| const key = `${broker}:${price.symbol}`; | |
| if (this.positions.has(key)) continue; | |
| if (brokerState.positions >= CONFIG.MAX_POSITIONS_PER_BROKER) break; | |
| // Enter on strong 24h momentum | |
| if (price.change24h !== undefined) { | |
| if (price.change24h > 3) { | |
| // Strong bullish - LONG | |
| this.openPosition(broker, price, 'LONG', `π+${price.change24h.toFixed(1)}%`, false); | |
| } else if (price.change24h < -3) { | |
| // Strong bearish - SHORT | |
| this.openPosition(broker, price, 'SHORT', `π${price.change24h.toFixed(1)}%`, false); | |
| } | |
| } | |
| } | |
| } | |
| private deployScout(broker: string, price: LivePrice): void { | |
| const brokerState = this.brokers.get(broker); | |
| if (!brokerState) return; | |
| // Use 24h change to pick direction, or random if not available | |
| let direction: 'LONG' | 'SHORT'; | |
| if (price.change24h !== undefined) { | |
| direction = price.change24h >= 0 ? 'LONG' : 'SHORT'; | |
| } else { | |
| direction = Math.random() > 0.5 ? 'LONG' : 'SHORT'; | |
| } | |
| const key = `${broker}:${price.symbol}`; | |
| const entryPrice = direction === 'LONG' ? price.ask : price.bid; | |
| const size = (brokerState.balance * CONFIG.POSITION_SIZE_PCT) / entryPrice; | |
| this.positions.set(key, { | |
| broker, | |
| symbol: price.symbol, | |
| direction, | |
| size, | |
| entryPrice, | |
| currentPrice: price.mid, | |
| pnl: 0, | |
| pnlPercent: 0, | |
| taxFree: brokerState.taxFree, | |
| isScout: true | |
| }); | |
| brokerState.positions++; | |
| brokerState.scoutDeployed = true; | |
| brokerState.hasScout = true; | |
| const dir = this.scanDirection; | |
| const dirEmoji = direction === 'LONG' ? 'β¬οΈ' : 'β¬οΈ'; | |
| const taxLabel = brokerState.taxFree ? ' πTAX FREE' : ''; | |
| const priceStr = price.mid < 10 ? price.mid.toFixed(5) : price.mid.toFixed(2); | |
| console.log(` ${brokerState.emoji} [${dir}] π SCOUT ${dirEmoji}: ${price.symbol} @ ${priceStr}${taxLabel}`); | |
| } | |
| private openPosition(broker: string, price: LivePrice, direction: 'LONG' | 'SHORT', reason: string, isScout: boolean): void { | |
| const brokerState = this.brokers.get(broker); | |
| if (!brokerState) return; | |
| const key = `${broker}:${price.symbol}`; | |
| const entryPrice = direction === 'LONG' ? price.ask : price.bid; | |
| const size = (brokerState.balance * CONFIG.POSITION_SIZE_PCT) / entryPrice; | |
| this.positions.set(key, { | |
| broker, | |
| symbol: price.symbol, | |
| direction, | |
| size, | |
| entryPrice, | |
| currentPrice: price.mid, | |
| pnl: 0, | |
| pnlPercent: 0, | |
| taxFree: brokerState.taxFree, | |
| isScout | |
| }); | |
| brokerState.positions++; | |
| const dir = this.scanDirection; | |
| const dirEmoji = direction === 'LONG' ? 'β¬οΈ' : 'β¬οΈ'; | |
| const taxLabel = brokerState.taxFree ? ' π' : ''; | |
| const priceStr = price.mid < 10 ? price.mid.toFixed(5) : price.mid.toFixed(2); | |
| console.log(` ${brokerState.emoji} [${dir}] β‘ ${dirEmoji} ${price.symbol} @ ${priceStr} (${reason})${taxLabel}`); | |
| } | |
| private updatePosition(key: string, currentPrice: number): void { | |
| const pos = this.positions.get(key); | |
| if (!pos) return; | |
| const brokerState = this.brokers.get(pos.broker); | |
| if (!brokerState) return; | |
| pos.currentPrice = currentPrice; | |
| const priceDiff = currentPrice - pos.entryPrice; | |
| const multiplier = pos.direction === 'LONG' ? 1 : -1; | |
| pos.pnl = priceDiff * pos.size * multiplier; | |
| pos.pnlPercent = (priceDiff / pos.entryPrice) * 100 * multiplier; | |
| // Check stop loss | |
| if (pos.pnlPercent <= -CONFIG.STOP_LOSS_PCT * 100) { | |
| this.closePosition(key, 'STOP'); | |
| } | |
| // Check take profit | |
| else if (pos.pnlPercent >= CONFIG.TAKE_PROFIT_PCT * 100) { | |
| this.closePosition(key, 'PROFIT'); | |
| } | |
| } | |
| private closePosition(key: string, reason: 'STOP' | 'PROFIT'): void { | |
| const pos = this.positions.get(key); | |
| if (!pos) return; | |
| const brokerState = this.brokers.get(pos.broker); | |
| if (!brokerState) return; | |
| brokerState.balance += pos.pnl; | |
| brokerState.pnl += pos.pnl; | |
| this.totalPnL += pos.pnl; | |
| if (pos.pnl >= 0) { | |
| brokerState.wins++; | |
| this.wins++; | |
| } else { | |
| brokerState.losses++; | |
| this.losses++; | |
| } | |
| brokerState.positions--; | |
| if (pos.isScout) brokerState.hasScout = false; | |
| const emoji = pos.pnl >= 0 ? 'β ' : 'β'; | |
| const scoutLabel = pos.isScout ? ' π' : ''; | |
| const taxLabel = pos.taxFree ? ' πTAX FREE!' : ''; | |
| const pnlStr = pos.pnl >= 0 ? `+Β£${pos.pnl.toFixed(4)}` : `-Β£${Math.abs(pos.pnl).toFixed(4)}`; | |
| console.log(` ${brokerState.emoji} ${emoji} ${reason}${scoutLabel}: ${pos.symbol} | P&L: ${pnlStr}${taxLabel}`); | |
| this.positions.delete(key); | |
| } | |
| private printHeader(): void { | |
| console.log('\n'); | |
| console.log(' βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(' β β'); | |
| console.log(' β π IMMEDIATE WAVE RIDER - LIVE MARKETS π β'); | |
| console.log(' β β'); | |
| console.log(' β AβZ β ZβA "They Can\'t Stop Them All!" β'); | |
| console.log(' β β'); | |
| console.log(' β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£'); | |
| console.log(' β β'); | |
| console.log(' β CRYPTO: πͺ Binance π¦ Kraken π Coinbase π Bitstamp π Gemini β OKX β'); | |
| console.log(' β FOREX: π± OANDA πΉ FXCM π¦ Saxo ποΈ IB β'); | |
| console.log(' β CFD: π Capital π IG π CMC π¦ Alpaca β'); | |
| console.log(' β β'); | |
| console.log(' β π TAX FREE (Spread Betting): π IG π CMC β'); | |
| console.log(' β β'); | |
| console.log(' β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£'); | |
| console.log(` β Capital: Β£${CONFIG.BALANCE_PER_BROKER} Γ 14 = Β£${CONFIG.BALANCE_PER_BROKER * 14} | Size: ${CONFIG.POSITION_SIZE_PCT*100}% | SL: ${CONFIG.STOP_LOSS_PCT*100}% | TP: ${CONFIG.TAKE_PROFIT_PCT*100}% β`); | |
| console.log(' βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(''); | |
| console.log(' π Deploying SCOUTS immediately - 1 per broker in AβZ / ZβA order...'); | |
| console.log(' ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| } | |
| private displayStatus(): void { | |
| const elapsed = Math.floor((Date.now() - this.startTime.getTime()) / 1000); | |
| const minutes = Math.floor(elapsed / 60); | |
| const seconds = elapsed % 60; | |
| let totalBalance = 0; | |
| let totalPositions = 0; | |
| let scoutsActive = 0; | |
| let taxFreePnL = 0; | |
| const lines: string[] = []; | |
| for (const [name, state] of this.brokers) { | |
| totalBalance += state.balance; | |
| totalPositions += state.positions; | |
| if (state.hasScout) scoutsActive++; | |
| if (state.taxFree) taxFreePnL += state.pnl; | |
| const hitRate = state.wins + state.losses > 0 | |
| ? ((state.wins / (state.wins + state.losses)) * 100).toFixed(0) | |
| : '-'; | |
| const pnlStr = state.pnl >= 0 ? `+Β£${state.pnl.toFixed(2)}` : `-Β£${Math.abs(state.pnl).toFixed(2)}`; | |
| const pnlEmoji = state.pnl >= 0 ? 'π’' : 'π΄'; | |
| const taxLabel = state.taxFree ? 'π' : ' '; | |
| const scoutLabel = state.hasScout ? 'π' : ' '; | |
| lines.push( | |
| ` β ${state.emoji} ${state.name.padEnd(8)} β Β£${state.balance.toFixed(2).padStart(6)} β ${state.positions}/${CONFIG.MAX_POSITIONS_PER_BROKER} β ${hitRate.padStart(3)}% β ${pnlEmoji} ${pnlStr.padStart(8)} β ${taxLabel} β ${scoutLabel} β` | |
| ); | |
| } | |
| const totalHitRate = this.wins + this.losses > 0 | |
| ? ((this.wins / (this.wins + this.losses)) * 100).toFixed(1) | |
| : '0.0'; | |
| const totalPnLStr = this.totalPnL >= 0 ? `+Β£${this.totalPnL.toFixed(2)}` : `-Β£${Math.abs(this.totalPnL).toFixed(2)}`; | |
| const totalEmoji = this.totalPnL >= 0 ? 'π’' : 'π΄'; | |
| console.log(''); | |
| console.log(' βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(` β SCAN #${this.scans.toString().padStart(5)} [${this.scanDirection}] β Time: ${minutes}m ${seconds}s β Positions: ${totalPositions}/${14 * CONFIG.MAX_POSITIONS_PER_BROKER} β π Scouts: ${scoutsActive} β`); | |
| console.log(' βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€'); | |
| console.log(' β Broker β Balance β Pos β HR β P&L β π β π β'); | |
| console.log(' ββββββββββββββΌβββββββββββΌββββββΌβββββββΌβββββββββββββΌβββββΌβββββ€'); | |
| for (const line of lines) { | |
| console.log(line); | |
| } | |
| console.log(' ββββββββββββββΌβββββββββββΌββββββΌβββββββΌβββββββββββββΌβββββΌβββββ€'); | |
| console.log(` β π TOTAL β Β£${totalBalance.toFixed(2).padStart(6)} β ${totalPositions.toString().padStart(2)} β${totalHitRate.padStart(5)}% β ${totalEmoji} ${totalPnLStr.padStart(8)} β β β`); | |
| console.log(' βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| if (taxFreePnL !== 0) { | |
| const taxFreeStr = taxFreePnL >= 0 ? `+Β£${taxFreePnL.toFixed(4)}` : `-Β£${Math.abs(taxFreePnL).toFixed(4)}`; | |
| console.log(` π Tax-Free P&L (IG + CMC): ${taxFreeStr}`); | |
| } | |
| this.displayPositions(); | |
| } | |
| private displayPositions(): void { | |
| const allPositions = Array.from(this.positions.values()); | |
| if (allPositions.length === 0) { | |
| console.log(''); | |
| console.log(` π [${this.scanDirection}] All scouts closed - looking for re-entry opportunities...`); | |
| console.log(''); | |
| return; | |
| } | |
| allPositions.sort((a, b) => b.pnl - a.pnl); | |
| console.log(''); | |
| console.log(` π Active Positions [${this.scanDirection}]:`); | |
| console.log(''); | |
| for (const pos of allPositions.slice(0, 16)) { | |
| const emoji = pos.pnl >= 0 ? 'π’' : 'π΄'; | |
| const dir = pos.direction === 'LONG' ? 'β¬' : 'β¬'; | |
| const brokerState = this.brokers.get(pos.broker); | |
| const brokerEmoji = brokerState?.emoji || 'π'; | |
| const taxLabel = pos.taxFree ? ' π' : ''; | |
| const scoutLabel = pos.isScout ? ' π' : ''; | |
| const pnlStr = pos.pnl >= 0 ? `+Β£${pos.pnl.toFixed(4)}` : `-Β£${Math.abs(pos.pnl).toFixed(4)}`; | |
| console.log(` ${brokerEmoji} ${emoji} ${pos.symbol.padEnd(12)} ${dir} β ${pnlStr} (${pos.pnlPercent.toFixed(2)}%)${taxLabel}${scoutLabel}`); | |
| } | |
| if (allPositions.length > 16) { | |
| console.log(` ... and ${allPositions.length - 16} more positions`); | |
| } | |
| console.log(''); | |
| } | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Main | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function main() { | |
| console.log(` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β β | |
| β π IMMEDIATE WAVE RIDER π β | |
| β β | |
| β Strategy: β | |
| β β’ Deploy SCOUTS immediately - 1 per broker β | |
| β β’ AβZ on odd scans, ZβA on even scans β | |
| β β’ Enter additional positions on strong 24h momentum (>Β±3%) β | |
| β β’ Ride the wave with 2% SL / 4% TP β | |
| β β’ "They can't stop them all!" β | |
| β β | |
| β Live Data Sources: β | |
| β β’ Binance API (20 pairs) β | |
| β β’ Kraken API (10 pairs) β | |
| β β’ OKX API (15 pairs) β | |
| β β’ Coinbase API (8 pairs) β | |
| β β’ Bitstamp API (7 pairs) β | |
| β β’ Forex API (8 major pairs) β | |
| β β | |
| β Author: Gary Leckey - R&A Consulting β | |
| β β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| `); | |
| const rider = new ImmediateWaveRider(); | |
| process.on('SIGINT', () => { | |
| console.log('\n'); | |
| console.log(' ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(' π Wave Rider concludes...'); | |
| console.log(' ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(''); | |
| console.log(' "AβZ β ZβA - The wave rides eternal"'); | |
| console.log(''); | |
| process.exit(0); | |
| }); | |
| await rider.start(); | |
| } | |
| main().catch(console.error); | |