Fix position sizing and improve automation UI
Major fixes: - Fixed position size calculation: converts USD amount to SOL tokens properly - Fixed insufficient collateral error by using correct position sizing - Added proper TP/SL parameter passing through automation chain - Enhanced position sizing UI with balance percentage slider Position Sizing Fixes: - Convert 2 USD to SOL tokens using current price (2 ÷ 97.87 = ~0.162 SOL) - Remove incorrect 32 SOL token calculation (was 32,000,000,000 base units) - Use USD position value for perpetual futures trading correctly Take Profit & Stop Loss Improvements: - Pass TP/SL percentages from config through automation → trade → drift chain - Use actual config percentages instead of hardcoded 2:1 ratio - Enable proper risk management with user-defined TP/SL levels UI/UX Enhancements: - Remove redundant 'Risk Per Trade (%)' field that caused confusion - Remove conflicting 'Auto-Size (%)' dropdown - Keep clean balance percentage slider (10% - 100% of available balance) - Simplify position sizing to: Balance % → Position Size → Leverage → TP/SL Technical Changes: - Update Drift API position calculation from SOL tokens to USD conversion - Fix automation trade route parameter passing - Clean up AutomationConfig interface - Improve position size validation and safety margins These changes enable proper leveraged perpetual futures trading with correct position sizing, collateral usage, and automated TP/SL order placement.
This commit is contained in:
@@ -11,6 +11,10 @@ export async function POST(request) {
|
|||||||
amount,
|
amount,
|
||||||
side,
|
side,
|
||||||
leverage = 1,
|
leverage = 1,
|
||||||
|
stopLoss,
|
||||||
|
takeProfit,
|
||||||
|
stopLossPercent,
|
||||||
|
takeProfitPercent,
|
||||||
mode = 'SIMULATION'
|
mode = 'SIMULATION'
|
||||||
} = await request.json()
|
} = await request.json()
|
||||||
|
|
||||||
@@ -29,6 +33,10 @@ export async function POST(request) {
|
|||||||
amount,
|
amount,
|
||||||
side,
|
side,
|
||||||
leverage,
|
leverage,
|
||||||
|
stopLoss,
|
||||||
|
takeProfit,
|
||||||
|
stopLossPercent,
|
||||||
|
takeProfitPercent,
|
||||||
mode
|
mode
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -65,15 +73,16 @@ export async function POST(request) {
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
action: 'place_order', // This was missing! Was defaulting to 'get_balance'
|
action: 'place_order',
|
||||||
symbol: symbol.replace('USD', ''), // Convert SOLUSD to SOL
|
symbol: symbol.replace('USD', ''), // Convert SOLUSD to SOL
|
||||||
amount,
|
amount,
|
||||||
side,
|
side,
|
||||||
leverage,
|
leverage,
|
||||||
// Add stop loss and take profit parameters
|
// Pass through stop loss and take profit parameters
|
||||||
stopLoss: true,
|
stopLoss: stopLoss !== undefined ? stopLoss : true,
|
||||||
takeProfit: true,
|
takeProfit: takeProfit !== undefined ? takeProfit : true,
|
||||||
riskPercent: 2 // 2% risk per trade
|
riskPercent: stopLossPercent || 2, // Use actual stop loss percentage from config
|
||||||
|
takeProfitPercent: takeProfitPercent || 4 // Use actual take profit percentage from config
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ export async function POST(request) {
|
|||||||
leverage = 1,
|
leverage = 1,
|
||||||
stopLoss = true,
|
stopLoss = true,
|
||||||
takeProfit = true,
|
takeProfit = true,
|
||||||
riskPercent = 2
|
riskPercent = 2,
|
||||||
|
takeProfitPercent = 4
|
||||||
} = await request.json()
|
} = await request.json()
|
||||||
|
|
||||||
// Import Drift SDK components
|
// Import Drift SDK components
|
||||||
@@ -220,13 +221,17 @@ export async function POST(request) {
|
|||||||
|
|
||||||
console.log(`📊 Current ${symbol} price: $${currentPrice}`)
|
console.log(`📊 Current ${symbol} price: $${currentPrice}`)
|
||||||
|
|
||||||
// Convert amount to base units (SOL uses 9 decimals)
|
// For perpetual futures: amount is USD position size, convert to base asset amount
|
||||||
const baseAssetAmount = new BN(Math.floor(amount * 1e9))
|
// Example: $32 position at $197.87/SOL = 0.162 SOL base asset amount
|
||||||
|
const solTokenAmount = amount / currentPrice
|
||||||
|
const baseAssetAmount = new BN(Math.floor(solTokenAmount * 1e9))
|
||||||
|
|
||||||
console.log(`💰 Amount conversion:`, {
|
console.log(`💰 Position size conversion:`, {
|
||||||
inputAmount: amount,
|
usdPositionSize: amount,
|
||||||
calculatedAmount: amount * 1e9,
|
solPrice: currentPrice,
|
||||||
flooredAmount: Math.floor(amount * 1e9),
|
solTokenAmount: solTokenAmount,
|
||||||
|
calculatedBaseAsset: solTokenAmount * 1e9,
|
||||||
|
flooredBaseAsset: Math.floor(solTokenAmount * 1e9),
|
||||||
baseAssetAmount: baseAssetAmount.toString()
|
baseAssetAmount: baseAssetAmount.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -236,7 +241,8 @@ export async function POST(request) {
|
|||||||
console.log(`📊 Placing ${side} order:`, {
|
console.log(`📊 Placing ${side} order:`, {
|
||||||
symbol,
|
symbol,
|
||||||
marketIndex,
|
marketIndex,
|
||||||
amount,
|
usdAmount: amount,
|
||||||
|
solAmount: solTokenAmount,
|
||||||
leverage,
|
leverage,
|
||||||
currentPrice,
|
currentPrice,
|
||||||
baseAssetAmount: baseAssetAmount.toString()
|
baseAssetAmount: baseAssetAmount.toString()
|
||||||
@@ -257,25 +263,25 @@ export async function POST(request) {
|
|||||||
// Wait for main order to fill
|
// Wait for main order to fill
|
||||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||||
|
|
||||||
// 2. Calculate stop loss and take profit prices
|
// 2. Calculate stop loss and take profit prices using config percentages
|
||||||
const stopLossPercent = Math.max(riskPercent / 100, 0.02) // Minimum 2% to avoid "too close" issues
|
const stopLossPercent = Math.max(riskPercent / 100, 0.02) // Use riskPercent from config, minimum 2%
|
||||||
const takeProfitPercent = stopLossPercent * 2 // 2:1 risk reward ratio
|
const takeProfitPercentCalc = Math.max(takeProfitPercent / 100, 0.04) // Use takeProfitPercent from config, minimum 4%
|
||||||
|
|
||||||
let stopLossPrice, takeProfitPrice
|
let stopLossPrice, takeProfitPrice
|
||||||
|
|
||||||
if (direction === PositionDirection.LONG) {
|
if (direction === PositionDirection.LONG) {
|
||||||
stopLossPrice = currentPrice * (1 - stopLossPercent)
|
stopLossPrice = currentPrice * (1 - stopLossPercent)
|
||||||
takeProfitPrice = currentPrice * (1 + takeProfitPercent)
|
takeProfitPrice = currentPrice * (1 + takeProfitPercentCalc)
|
||||||
} else {
|
} else {
|
||||||
stopLossPrice = currentPrice * (1 + stopLossPercent)
|
stopLossPrice = currentPrice * (1 + stopLossPercent)
|
||||||
takeProfitPrice = currentPrice * (1 - takeProfitPercent)
|
takeProfitPrice = currentPrice * (1 - takeProfitPercentCalc)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🎯 Risk management:`, {
|
console.log(`🎯 Risk management:`, {
|
||||||
stopLossPrice: stopLossPrice.toFixed(4),
|
stopLossPrice: stopLossPrice.toFixed(4),
|
||||||
takeProfitPrice: takeProfitPrice.toFixed(4),
|
takeProfitPrice: takeProfitPrice.toFixed(4),
|
||||||
riskPercent: `${stopLossPercent * 100}%`,
|
stopLossPercent: `${stopLossPercent * 100}%`,
|
||||||
rewardRatio: '2:1',
|
takeProfitPercent: `${takeProfitPercentCalc * 100}%`,
|
||||||
priceDifference: Math.abs(currentPrice - stopLossPrice).toFixed(4)
|
priceDifference: Math.abs(currentPrice - stopLossPrice).toFixed(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ export default function AutomationPageV2() {
|
|||||||
timeframe: '1h', // Primary timeframe for backwards compatibility
|
timeframe: '1h', // Primary timeframe for backwards compatibility
|
||||||
selectedTimeframes: ['60'], // Multi-timeframe support
|
selectedTimeframes: ['60'], // Multi-timeframe support
|
||||||
tradingAmount: 100,
|
tradingAmount: 100,
|
||||||
|
balancePercentage: 50, // Default to 50% of available balance
|
||||||
maxLeverage: 5,
|
maxLeverage: 5,
|
||||||
stopLossPercent: 2,
|
stopLossPercent: 2,
|
||||||
takeProfitPercent: 6,
|
takeProfitPercent: 6
|
||||||
riskPercentage: 2
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const [status, setStatus] = useState(null)
|
const [status, setStatus] = useState(null)
|
||||||
@@ -242,7 +242,7 @@ export default function AutomationPageV2() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Symbol and Position Size */}
|
{/* Symbol and Position Size */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Symbol</label>
|
<label className="block text-sm font-medium text-gray-300 mb-2">Symbol</label>
|
||||||
<select
|
<select
|
||||||
@@ -261,51 +261,39 @@ export default function AutomationPageV2() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Position Size ($)</label>
|
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Balance to Use: {config.balancePercentage}%
|
||||||
|
{balance && ` ($${(parseFloat(balance.availableBalance) * config.balancePercentage / 100).toFixed(2)})`}
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="range"
|
||||||
className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:border-blue-500"
|
className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(to right, #3b82f6 0%, #3b82f6 ${config.balancePercentage}%, #374151 ${config.balancePercentage}%, #374151 100%)`
|
||||||
|
}}
|
||||||
min="10"
|
min="10"
|
||||||
step="10"
|
max="100"
|
||||||
value={config.tradingAmount}
|
step="5"
|
||||||
onChange={(e) => setConfig({...config, tradingAmount: parseFloat(e.target.value)})}
|
value={config.balancePercentage}
|
||||||
|
onChange={(e) => {
|
||||||
|
const percentage = parseFloat(e.target.value);
|
||||||
|
const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100;
|
||||||
|
setConfig({
|
||||||
|
...config,
|
||||||
|
balancePercentage: percentage,
|
||||||
|
tradingAmount: Math.round(newAmount)
|
||||||
|
});
|
||||||
|
}}
|
||||||
disabled={status?.isActive}
|
disabled={status?.isActive}
|
||||||
/>
|
/>
|
||||||
{balance && (
|
<div className="flex justify-between text-xs text-gray-400 mt-1">
|
||||||
<p className="text-xs text-gray-400 mt-1">
|
<span>10%</span>
|
||||||
Available: ${parseFloat(balance.availableBalance).toFixed(2)} • Using {((config.tradingAmount / balance.availableBalance) * 100).toFixed(1)}% of balance
|
<span>50%</span>
|
||||||
</p>
|
<span>100%</span>
|
||||||
)}
|
</div>
|
||||||
{balance && config.maxLeverage > 1 && (
|
{balance && config.maxLeverage > 1 && (
|
||||||
<p className="text-xs text-green-400 mt-1">
|
<p className="text-xs text-green-400 mt-1">
|
||||||
With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position size
|
With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position exposure
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Auto-Size (%)</label>
|
|
||||||
<select
|
|
||||||
className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:border-blue-500"
|
|
||||||
onChange={(e) => {
|
|
||||||
if (balance && e.target.value) {
|
|
||||||
const percentage = parseFloat(e.target.value);
|
|
||||||
const autoAmount = (balance.availableBalance * percentage / 100);
|
|
||||||
setConfig({...config, tradingAmount: Math.round(autoAmount)});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={status?.isActive || !balance}
|
|
||||||
>
|
|
||||||
<option value="">Manual</option>
|
|
||||||
<option value="10">10% of balance</option>
|
|
||||||
<option value="25">25% of balance</option>
|
|
||||||
<option value="50">50% of balance</option>
|
|
||||||
<option value="75">75% of balance</option>
|
|
||||||
<option value="90">90% of balance</option>
|
|
||||||
</select>
|
|
||||||
{balance && (
|
|
||||||
<p className="text-xs text-cyan-400 mt-1">
|
|
||||||
Quick calculation based on ${parseFloat(balance.availableBalance).toFixed(2)} balance
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -420,20 +408,6 @@ export default function AutomationPageV2() {
|
|||||||
disabled={status?.isActive}
|
disabled={status?.isActive}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Risk Per Trade (%)</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:border-blue-500"
|
|
||||||
min="0.5"
|
|
||||||
max="10"
|
|
||||||
step="0.5"
|
|
||||||
value={config.riskPercentage}
|
|
||||||
onChange={(e) => setConfig({...config, riskPercentage: parseFloat(e.target.value)})}
|
|
||||||
disabled={status?.isActive}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export interface AutomationConfig {
|
|||||||
stopLossPercent: number
|
stopLossPercent: number
|
||||||
takeProfitPercent: number
|
takeProfitPercent: number
|
||||||
maxDailyTrades: number
|
maxDailyTrades: number
|
||||||
riskPercentage: number
|
|
||||||
dexProvider: 'JUPITER' | 'DRIFT'
|
dexProvider: 'JUPITER' | 'DRIFT'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,7 +570,7 @@ export class AutomationService {
|
|||||||
console.log('⚠️ Failed to fetch balance, using fallback calculation')
|
console.log('⚠️ Failed to fetch balance, using fallback calculation')
|
||||||
// Fallback to config amount
|
// Fallback to config amount
|
||||||
let amount = Math.min(config.tradingAmount, 35) // Cap at $35 max
|
let amount = Math.min(config.tradingAmount, 35) // Cap at $35 max
|
||||||
const riskAdjustment = config.riskPercentage / 100
|
const riskAdjustment = 0.02 // Default 2% risk
|
||||||
return Math.max(amount * riskAdjustment, 5)
|
return Math.max(amount * riskAdjustment, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +584,7 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate position size based on risk percentage of available balance
|
// Calculate position size based on risk percentage of available balance
|
||||||
const riskAmount = availableBalance * (config.riskPercentage / 100)
|
const riskAmount = availableBalance * 0.02 // Default 2% risk
|
||||||
|
|
||||||
// Adjust based on confidence (reduce risk for low confidence signals)
|
// Adjust based on confidence (reduce risk for low confidence signals)
|
||||||
const confidenceMultiplier = Math.min(analysis.confidence / 100, 1)
|
const confidenceMultiplier = Math.min(analysis.confidence / 100, 1)
|
||||||
@@ -600,8 +599,7 @@ export class AutomationService {
|
|||||||
|
|
||||||
console.log(`📊 Position sizing calculation:`)
|
console.log(`📊 Position sizing calculation:`)
|
||||||
console.log(` - Available balance: $${availableBalance}`)
|
console.log(` - Available balance: $${availableBalance}`)
|
||||||
console.log(` - Risk percentage: ${config.riskPercentage}%`)
|
console.log(` - Risk amount: $${riskAmount.toFixed(2)} (2% default)`)
|
||||||
console.log(` - Risk amount: $${riskAmount.toFixed(2)}`)
|
|
||||||
console.log(` - Confidence multiplier: ${confidenceMultiplier}`)
|
console.log(` - Confidence multiplier: ${confidenceMultiplier}`)
|
||||||
console.log(` - Leverage: ${Math.min(config.maxLeverage, 10)}x`)
|
console.log(` - Leverage: ${Math.min(config.maxLeverage, 10)}x`)
|
||||||
console.log(` - Final position size: $${amount.toFixed(2)}`)
|
console.log(` - Final position size: $${amount.toFixed(2)}`)
|
||||||
|
|||||||
Reference in New Issue
Block a user