REMOVED ARTIFICIAL CONSTRAINTS: - Eliminated 3% minimum stop loss requirement - Eliminated 1% minimum take profit requirement - AI can now choose ANY percentage based on market analysis - Updated app/api/drift/trade/route.js to use exact AI percentages - Removed Math.max() constraints that forced minimums - AI now has 0.1%+ to 50%+ percentage freedom - Modified AI_RISK_MANAGEMENT.md to reflect new freedom - Removed all references to artificial 3%/1% minimums - Added ultra-tight scalping examples (0.1%-1%) - Updated volatility guidelines for all trading styles PROVEN WITH REAL ORDERS: - Transaction: 35QmCqWFzwJ1X2nm5M8rgExKEMbWTRqxCa1GryEsR595zYwBLqCzDowUYm3J2u13WMvYR2PRoS3eAMSzXfGvEVbe - Confirmed: 0.5% SL / 0.25% TP working on Drift Protocol - Verified: Orders visible in Drift UI with correct trigger prices - Optimal risk management based on actual market conditions - Support for all trading styles: scalping to position trading - No more forced suboptimal stops due to artificial limits - Professional-grade percentage precision The AI can now freely optimize percentages for maximum trading effectiveness!
333 lines
11 KiB
JavaScript
333 lines
11 KiB
JavaScript
#!/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);
|
||
}
|