#!/usr/bin/env node /** * REAL DRIFT PROTOCOL MINIMUM PERCENTAGE TEST * * This test places ACTUAL small orders on Drift Protocol to validate * that the reduced minimum percentages work in real trading conditions. * * SAFETY MEASURES: * - Uses very small position sizes ($0.50 - $2.00) * - Tests on SOL-PERP with high liquidity * - Automatically cancels test orders after validation * - Monitors for order rejection reasons */ const https = require('https'); const http = require('http'); // Test configuration const API_BASE = 'http://localhost:3000/api'; const TEST_SYMBOL = 'SOLUSD'; const TEST_AMOUNT = 0.5; // $0.50 position size for safety // Minimum percentages to test (progressively tighter) const TEST_CASES = [ { sl: 1.5, tp: 1.0, desc: 'Conservative scalping (1.5%/1.0%)' }, { sl: 1.0, tp: 0.75, desc: 'Moderate scalping (1.0%/0.75%)' }, { sl: 0.75, tp: 0.5, desc: 'Tight scalping (0.75%/0.5%)' }, { sl: 0.5, tp: 0.25, desc: 'Ultra-tight scalping (0.5%/0.25%)' }, { sl: 0.25, tp: 0.1, desc: 'Extreme scalping (0.25%/0.1%)' } ]; async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function makeApiCall(endpoint, method = 'GET', body = null) { return new Promise((resolve, reject) => { const url = `http://localhost:3000/api${endpoint}`; const parsedUrl = new URL(url); const options = { hostname: parsedUrl.hostname, port: parsedUrl.port, path: parsedUrl.pathname + parsedUrl.search, method: method, headers: { 'Content-Type': 'application/json', }, }; const req = http.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const jsonData = JSON.parse(data); resolve({ success: res.statusCode >= 200 && res.statusCode < 300, data: jsonData, status: res.statusCode }); } catch (error) { resolve({ success: false, error: `Parse error: ${error.message}`, data: data }); } }); }); req.on('error', (error) => { resolve({ success: false, error: error.message }); }); if (body) { req.write(JSON.stringify(body)); } req.end(); }); } async function getCurrentBalance() { console.log('๐Ÿ’ฐ Checking current Drift balance...'); const result = await makeApiCall('/drift/balance'); if (!result.success) { throw new Error(`Failed to get balance: ${result.error}`); } console.log(` Balance: $${result.data.totalCollateral}`); console.log(` Available: $${result.data.availableBalance}`); return parseFloat(result.data.availableBalance); } async function getCurrentPositions() { console.log('๐Ÿ“‹ Checking current positions...'); const result = await makeApiCall('/drift/positions'); if (!result.success) { throw new Error(`Failed to get positions: ${result.error}`); } console.log(` Active positions: ${result.data.positions.length}`); return result.data.positions; } async function testOrderPlacement(testCase, entryPrice) { console.log(`\n๐Ÿ”ฌ Testing: ${testCase.desc}`); console.log(` Entry: $${entryPrice.toFixed(4)}`); console.log(` Stop Loss: ${testCase.sl}% (${(entryPrice * (1 - testCase.sl/100)).toFixed(4)})`); console.log(` Take Profit: ${testCase.tp}% (${(entryPrice * (1 + testCase.tp/100)).toFixed(4)})`); // Calculate position size based on test amount const positionSize = TEST_AMOUNT / entryPrice; const tradeRequest = { symbol: TEST_SYMBOL, side: 'long', amount: positionSize.toFixed(6), stopLossPercent: testCase.sl, takeProfitPercent: testCase.tp, leverage: 1, // Conservative leverage for testing orderType: 'market' }; console.log(` Position size: ${positionSize.toFixed(6)} SOL ($${TEST_AMOUNT})`); try { // Place the order console.log(' ๐Ÿ“ค Placing order...'); const result = await makeApiCall('/drift/trade', 'POST', tradeRequest); if (result.success) { console.log(` โœ… SUCCESS: Order placed with ${testCase.sl}%/${testCase.tp}% levels`); console.log(` ๐Ÿ“‹ Transaction: ${result.data.signature || 'N/A'}`); // Wait a moment for order to process await delay(2000); // Check if position was created const positions = await getCurrentPositions(); const newPosition = positions.find(p => p.symbol === TEST_SYMBOL); if (newPosition) { console.log(` ๐Ÿ’ผ Position created: ${newPosition.size} SOL`); console.log(` ๐ŸŽฏ Stop Loss: $${newPosition.stopLoss || 'N/A'}`); console.log(` ๐Ÿ“ˆ Take Profit: $${newPosition.takeProfit || 'N/A'}`); // IMPORTANT: Close the test position immediately console.log(' ๐Ÿงน Closing test position...'); const closeResult = await makeApiCall('/drift/trade', 'POST', { symbol: TEST_SYMBOL, side: 'sell', amount: Math.abs(parseFloat(newPosition.size)), orderType: 'market' }); if (closeResult.success) { console.log(' โœ… Test position closed successfully'); } else { console.log(` โš ๏ธ Warning: Could not close position: ${closeResult.error}`); } } return { success: true, details: result.data }; } else { console.log(` โŒ FAILED: ${result.data.error || result.error}`); // Analyze the failure reason const errorMsg = result.data.error || result.error || ''; if (errorMsg.includes('trigger') || errorMsg.includes('price')) { console.log(` ๐Ÿ” Analysis: Price level too close to market`); return { success: false, reason: 'price_too_close' }; } else if (errorMsg.includes('margin') || errorMsg.includes('collateral')) { console.log(` ๐Ÿ” Analysis: Insufficient margin/collateral`); return { success: false, reason: 'insufficient_margin' }; } else { console.log(` ๐Ÿ” Analysis: Other error - ${errorMsg}`); return { success: false, reason: 'other', error: errorMsg }; } } } catch (error) { console.log(` โŒ EXCEPTION: ${error.message}`); return { success: false, reason: 'exception', error: error.message }; } } async function getCurrentPrice() { console.log('๐Ÿ“Š Getting current SOL price...'); return new Promise((resolve, reject) => { const options = { hostname: 'api.binance.com', port: 443, path: '/api/v3/ticker/price?symbol=SOLUSDT', method: 'GET', }; const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const jsonData = JSON.parse(data); const price = parseFloat(jsonData.price); console.log(` SOL Price: $${price.toFixed(4)}`); resolve(price); } catch (error) { console.log(` โš ๏ธ Could not parse price data: ${error.message}`); console.log(' Using fallback price: $200.00'); resolve(200.00); } }); }); req.on('error', (error) => { console.log(` โš ๏ธ Could not get price from Binance: ${error.message}`); console.log(' Using fallback price: $200.00'); resolve(200.00); }); req.end(); }); } async function runRealDriftTest() { console.log('๐Ÿš€ REAL DRIFT PROTOCOL MINIMUM PERCENTAGE TEST'); console.log('=' .repeat(60)); console.log('โš ๏ธ WARNING: This test places REAL orders with REAL money'); console.log('๐Ÿ’ก Position size limited to $0.50 for safety'); console.log('๐Ÿงน Test positions are automatically closed'); console.log('=' .repeat(60)); try { // Pre-flight checks const balance = await getCurrentBalance(); if (balance < 10) { throw new Error(`Insufficient balance: $${balance}. Minimum $10 required for safe testing.`); } const initialPositions = await getCurrentPositions(); const entryPrice = await getCurrentPrice(); // Run tests console.log('\n๐Ÿ“‹ Test Results Summary:'); console.log('-' .repeat(60)); const results = []; for (const testCase of TEST_CASES) { const result = await testOrderPlacement(testCase, entryPrice); results.push({ ...testCase, ...result }); // Wait between tests to avoid rate limits await delay(3000); } // Analysis console.log('\n๐ŸŽฏ FINAL ANALYSIS:'); console.log('=' .repeat(60)); const successful = results.filter(r => r.success); const failed = results.filter(r => !r.success); console.log(`โœ… Successful: ${successful.length}/${results.length} test cases`); console.log(`โŒ Failed: ${failed.length}/${results.length} test cases`); if (successful.length > 0) { const tightest = successful[successful.length - 1]; console.log(`\n๐Ÿ† TIGHTEST WORKING PERCENTAGES:`); console.log(` Stop Loss: ${tightest.sl}%`); console.log(` Take Profit: ${tightest.tp}%`); console.log(` Description: ${tightest.desc}`); console.log(`\n๐Ÿ’ก RECOMMENDED NEW MINIMUMS:`); console.log(` stopLossPercentCalc = Math.max(stopLossPercent / 100, ${(tightest.sl / 100).toFixed(4)}) // ${tightest.sl}%`); console.log(` takeProfitPercentCalc = Math.max(takeProfitPercent / 100, ${(tightest.tp / 100).toFixed(4)}) // ${tightest.tp}%`); } if (failed.length > 0) { console.log(`\nโš ๏ธ FAILURES ANALYSIS:`); failed.forEach(f => { console.log(` ${f.desc}: ${f.reason}`); }); } // Final position check console.log('\n๐Ÿงน POST-TEST CLEANUP CHECK:'); const finalPositions = await getCurrentPositions(); const newPositions = finalPositions.length - initialPositions.length; if (newPositions > 0) { console.log(`โš ๏ธ Warning: ${newPositions} test positions remain open`); console.log(' Manual cleanup may be required'); } else { console.log('โœ… All test positions cleaned up successfully'); } const finalBalance = await getCurrentBalance(); const balanceChange = finalBalance - balance; console.log(`๐Ÿ’ฐ Balance change: ${balanceChange >= 0 ? '+' : ''}$${balanceChange.toFixed(2)}`); } catch (error) { console.error('โŒ Test failed:', error.message); console.log('\nโ„น๏ธ This could be due to:'); console.log(' - Network connectivity issues'); console.log(' - Drift Protocol API changes'); console.log(' - Insufficient balance or margin'); console.log(' - Market conditions (low liquidity)'); } } // Safety confirmation console.log('โš ๏ธ SAFETY CONFIRMATION REQUIRED โš ๏ธ'); console.log('This test will place REAL orders on Drift Protocol'); console.log('Position size is limited to $0.50 per test'); console.log('Orders will be automatically closed'); console.log(''); console.log('To proceed, run: node test-drift-real-orders.js --confirm'); if (process.argv.includes('--confirm')) { runRealDriftTest(); } else { console.log('Test not run - confirmation required'); process.exit(0); }