Files
trading_bot_v3/test-drift-minimum-percentages.js
mindesbunister 9c4bee0dd7 feat: Remove artificial percentage minimums - AI now has complete freedom
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!
2025-07-24 09:58:30 +02:00

387 lines
13 KiB
JavaScript

const { DriftClient, initialize, OrderType, PositionDirection, OrderTriggerCondition, PostOnlyParams } = require('@drift-labs/sdk');
const { Connection, Keypair } = require('@solana/web3.js');
const { Wallet } = require('@coral-xyz/anchor');
/**
* Test minimum percentages for stop loss and take profit with real small positions
* This script will test progressively smaller percentages to find the actual minimum
* that Drift Protocol accepts for order placement.
*/
class DriftMinimumPercentageTester {
constructor() {
this.driftClient = null;
this.connection = null;
this.testResults = [];
}
async initialize() {
console.log('🚀 Initializing Drift Protocol connection...');
try {
// Initialize connection with fallback RPC endpoints
const rpcEndpoints = [
process.env.SOLANA_RPC_URL_SECONDARY || 'https://api.mainnet-beta.solana.com',
process.env.SOLANA_RPC_URL_TERTIARY || 'https://solana-mainnet.g.alchemy.com/v2/demo',
process.env.SOLANA_RPC_URL_BACKUP || 'https://rpc.ankr.com/solana'
];
let connection = null;
for (const endpoint of rpcEndpoints) {
try {
console.log(`🔄 Trying RPC endpoint: ${endpoint}`);
connection = new Connection(endpoint, 'confirmed');
// Test the connection
await connection.getVersion();
console.log(`✅ Connected to: ${endpoint}`);
break;
} catch (error) {
console.log(`❌ Failed to connect to ${endpoint}: ${error.message}`);
}
}
if (!connection) {
throw new Error('Could not connect to any RPC endpoint');
}
this.connection = connection;
// Create wallet from private key
if (!process.env.SOLANA_PRIVATE_KEY) {
throw new Error('SOLANA_PRIVATE_KEY environment variable not set');
}
const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY);
const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray));
const wallet = new Wallet(keypair);
console.log(`📍 Wallet address: ${keypair.publicKey.toString()}`);
// Initialize Drift SDK
const sdkConfig = initialize({ env: 'mainnet-beta' });
this.driftClient = new DriftClient({
connection: this.connection,
wallet,
programID: sdkConfig.DRIFT_PROGRAM_ID,
opts: {
commitment: 'confirmed',
skipPreflight: false,
preflightCommitment: 'confirmed'
}
});
await this.driftClient.subscribe();
console.log('✅ Drift client initialized and subscribed');
// Check account balance
await this.checkAccountStatus();
} catch (error) {
console.error('❌ Initialization failed:', error.message);
throw error;
}
}
async checkAccountStatus() {
try {
const user = this.driftClient.getUser();
const userAccount = user.getUserAccount();
// Get collateral and free collateral
const totalCollateral = user.getTotalCollateral();
const freeCollateral = user.getFreeCollateral();
console.log('\n💰 Account Status:');
console.log(` Total Collateral: $${(totalCollateral / 1e6).toFixed(2)}`);
console.log(` Free Collateral: $${(freeCollateral / 1e6).toFixed(2)}`);
if (freeCollateral < 1e6) { // Less than $1
console.log('⚠️ Warning: Low free collateral - tests will use very small positions');
}
// Get current positions
const positions = user.getPositions().filter(pos => !pos.baseAssetAmount.eq(0));
console.log(` Open Positions: ${positions.length}`);
positions.forEach((pos, index) => {
const market = this.driftClient.getPerpMarketAccount(pos.marketIndex);
const marketName = market.name || `Market ${pos.marketIndex}`;
const size = Number(pos.baseAssetAmount) / 1e9;
console.log(` ${index + 1}. ${marketName}: ${size.toFixed(4)} SOL`);
});
} catch (error) {
console.error('❌ Failed to check account status:', error.message);
}
}
async getCurrentPrice(marketIndex = 0) {
try {
const perpMarketAccount = this.driftClient.getPerpMarketAccount(marketIndex);
const currentPrice = Number(perpMarketAccount.amm.lastMarkPriceTwap) / 1e6;
return currentPrice;
} catch (error) {
console.error('❌ Failed to get current price:', error.message);
return null;
}
}
async testMinimumPercentage(stopLossPercent, takeProfitPercent, marketIndex = 0) {
console.log(`\n🧪 Testing SL: ${stopLossPercent}% / TP: ${takeProfitPercent}%`);
try {
const currentPrice = await this.getCurrentPrice(marketIndex);
if (!currentPrice) {
throw new Error('Could not get current price');
}
console.log(` 📊 Current SOL price: $${currentPrice.toFixed(4)}`);
// Calculate prices
const stopLossPrice = currentPrice * (1 - stopLossPercent / 100);
const takeProfitPrice = currentPrice * (1 + takeProfitPercent / 100);
console.log(` 🎯 Entry: $${currentPrice.toFixed(4)}`);
console.log(` 🛑 Stop Loss: $${stopLossPrice.toFixed(4)} (${stopLossPercent}% below)`);
console.log(` 💰 Take Profit: $${takeProfitPrice.toFixed(4)} (${takeProfitPercent}% above)`);
// Use very small position size for testing
const baseAssetAmount = Math.floor(0.001 * 1e9); // 0.001 SOL (~$0.20)
const result = {
stopLossPercent,
takeProfitPercent,
currentPrice,
stopLossPrice,
takeProfitPrice,
baseAssetAmount,
success: false,
error: null,
orderIds: []
};
try {
// Step 1: Place a small long position
console.log(' 📝 Step 1: Placing small long position...');
const longOrderParams = {
orderType: OrderType.MARKET,
marketIndex,
direction: PositionDirection.LONG,
baseAssetAmount,
reduceOnly: false,
};
const longOrderTx = await this.driftClient.placePerpOrder(longOrderParams);
console.log(` ✅ Long position placed. TX: ${longOrderTx}`);
result.orderIds.push(longOrderTx);
// Wait a moment for order to settle
await new Promise(resolve => setTimeout(resolve, 2000));
// Step 2: Place stop loss order
console.log(' 📝 Step 2: Placing stop loss order...');
const stopLossParams = {
orderType: OrderType.TRIGGER_LIMIT,
marketIndex,
direction: PositionDirection.SHORT,
baseAssetAmount,
price: Math.floor(stopLossPrice * 1e6),
triggerPrice: Math.floor(stopLossPrice * 1e6),
triggerCondition: OrderTriggerCondition.BELOW,
reduceOnly: true,
};
const stopLossTx = await this.driftClient.placePerpOrder(stopLossParams);
console.log(` ✅ Stop loss placed. TX: ${stopLossTx}`);
result.orderIds.push(stopLossTx);
// Step 3: Place take profit order
console.log(' 📝 Step 3: Placing take profit order...');
const takeProfitParams = {
orderType: OrderType.TRIGGER_LIMIT,
marketIndex,
direction: PositionDirection.SHORT,
baseAssetAmount,
price: Math.floor(takeProfitPrice * 1e6),
triggerPrice: Math.floor(takeProfitPrice * 1e6),
triggerCondition: OrderTriggerCondition.ABOVE,
reduceOnly: true,
};
const takeProfitTx = await this.driftClient.placePerpOrder(takeProfitParams);
console.log(` ✅ Take profit placed. TX: ${takeProfitTx}`);
result.orderIds.push(takeProfitTx);
result.success = true;
console.log(` ✅ SUCCESS: ${stopLossPercent}%/${takeProfitPercent}% percentages work!`);
// Cancel orders immediately to clean up
await this.cleanupTestOrders(result.orderIds);
} catch (orderError) {
result.error = orderError.message;
console.log(` ❌ FAILED: ${orderError.message}`);
// Try to clean up any partial orders
if (result.orderIds.length > 0) {
await this.cleanupTestOrders(result.orderIds);
}
}
this.testResults.push(result);
return result;
} catch (error) {
console.error(` ❌ Test setup failed: ${error.message}`);
return {
stopLossPercent,
takeProfitPercent,
success: false,
error: error.message,
orderIds: []
};
}
}
async cleanupTestOrders(orderIds) {
console.log(' 🧹 Cleaning up test orders...');
for (const orderId of orderIds) {
try {
// Cancel all open orders for the user
const openOrders = this.driftClient.getUser().getOpenOrders();
for (const order of openOrders) {
if (order.marketIndex === 0) { // SOL-PERP market
await this.driftClient.cancelOrder(order.orderId);
console.log(` 🗑️ Cancelled order ${order.orderId}`);
}
}
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.log(` ⚠️ Could not cancel order: ${error.message}`);
}
}
}
async runComprehensiveTest() {
console.log('🎯 Running Comprehensive Minimum Percentage Test');
console.log(' Goal: Find the actual minimum SL/TP percentages for Drift Protocol');
console.log(' Method: Test with real small positions, progressively reducing percentages\n');
// Test cases - start with current minimums and work down
const testCases = [
// Current minimums (should work)
{ sl: 3.0, tp: 1.0, desc: 'Current minimums' },
// Moderate reductions
{ sl: 2.0, tp: 0.8, desc: 'Moderate reduction' },
{ sl: 1.5, tp: 0.6, desc: 'Moderate-aggressive' },
// Aggressive reductions for scalping
{ sl: 1.0, tp: 0.5, desc: 'Aggressive scalping' },
{ sl: 0.8, tp: 0.4, desc: 'Very aggressive' },
{ sl: 0.5, tp: 0.3, desc: 'Ultra-tight scalping' },
// Extreme minimums (likely to fail)
{ sl: 0.3, tp: 0.2, desc: 'Extreme tight' },
{ sl: 0.1, tp: 0.1, desc: 'Minimal possible' }
];
for (let i = 0; i < testCases.length; i++) {
const testCase = testCases[i];
console.log(`\n📋 Test ${i + 1}/${testCases.length}: ${testCase.desc}`);
const result = await this.testMinimumPercentage(testCase.sl, testCase.tp);
if (!result.success) {
console.log(`⚠️ Stopping tests - found minimum threshold at previous test`);
break;
}
// Wait between tests to avoid rate limiting
if (i < testCases.length - 1) {
console.log(' ⏳ Waiting 5 seconds before next test...');
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
this.generateReport();
}
generateReport() {
console.log('\n📊 TEST RESULTS SUMMARY');
console.log('=' .repeat(50));
const successfulTests = this.testResults.filter(r => r.success);
const failedTests = this.testResults.filter(r => !r.success);
console.log(`✅ Successful tests: ${successfulTests.length}`);
console.log(`❌ Failed tests: ${failedTests.length}`);
if (successfulTests.length > 0) {
const tightestSuccessful = successfulTests[successfulTests.length - 1];
console.log('\n🎯 RECOMMENDED NEW MINIMUMS:');
console.log(` Stop Loss: ${tightestSuccessful.stopLossPercent}%`);
console.log(` Take Profit: ${tightestSuccessful.takeProfitPercent}%`);
console.log('\n📝 Code Update Needed:');
console.log(' File: app/api/drift/trade/route.js');
console.log(' Lines 273-274:');
console.log(` const stopLossPercentCalc = Math.max(stopLossPercent / 100, ${(tightestSuccessful.stopLossPercent / 100).toFixed(3)}) // ${tightestSuccessful.stopLossPercent}% minimum`);
console.log(` const takeProfitPercentCalc = Math.max(takeProfitPercent / 100, ${(tightestSuccessful.takeProfitPercent / 100).toFixed(3)}) // ${tightestSuccessful.takeProfitPercent}% minimum`);
}
if (failedTests.length > 0) {
console.log('\n❌ Failed Percentages:');
failedTests.forEach(test => {
console.log(` ${test.stopLossPercent}%/${test.takeProfitPercent}%: ${test.error}`);
});
}
console.log('\n💡 Next Steps:');
console.log('1. Update the minimum percentages in the trading API');
console.log('2. Test with real trading scenarios');
console.log('3. Monitor order execution success rates');
console.log('4. Consider implementing dynamic minimums based on volatility');
}
async cleanup() {
if (this.driftClient) {
try {
await this.driftClient.unsubscribe();
console.log('✅ Drift client unsubscribed');
} catch (error) {
console.error('⚠️ Error during cleanup:', error.message);
}
}
}
}
// Main execution
async function main() {
const tester = new DriftMinimumPercentageTester();
try {
await tester.initialize();
await tester.runComprehensiveTest();
} catch (error) {
console.error('❌ Test execution failed:', error.message);
} finally {
await tester.cleanup();
}
}
// Run if called directly
if (require.main === module) {
main().catch(console.error);
}
module.exports = { DriftMinimumPercentageTester };