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:
mindesbunister
2025-07-23 14:16:55 +02:00
parent 2bbaa072d6
commit abc94c06e2
4 changed files with 68 additions and 81 deletions

View File

@@ -11,6 +11,10 @@ export async function POST(request) {
amount,
side,
leverage = 1,
stopLoss,
takeProfit,
stopLossPercent,
takeProfitPercent,
mode = 'SIMULATION'
} = await request.json()
@@ -29,6 +33,10 @@ export async function POST(request) {
amount,
side,
leverage,
stopLoss,
takeProfit,
stopLossPercent,
takeProfitPercent,
mode
})
@@ -65,15 +73,16 @@ export async function POST(request) {
'Content-Type': 'application/json'
},
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
amount,
side,
leverage,
// Add stop loss and take profit parameters
stopLoss: true,
takeProfit: true,
riskPercent: 2 // 2% risk per trade
// Pass through stop loss and take profit parameters
stopLoss: stopLoss !== undefined ? stopLoss : true,
takeProfit: takeProfit !== undefined ? takeProfit : true,
riskPercent: stopLossPercent || 2, // Use actual stop loss percentage from config
takeProfitPercent: takeProfitPercent || 4 // Use actual take profit percentage from config
})
})

View File

@@ -106,7 +106,8 @@ export async function POST(request) {
leverage = 1,
stopLoss = true,
takeProfit = true,
riskPercent = 2
riskPercent = 2,
takeProfitPercent = 4
} = await request.json()
// Import Drift SDK components
@@ -220,13 +221,17 @@ export async function POST(request) {
console.log(`📊 Current ${symbol} price: $${currentPrice}`)
// Convert amount to base units (SOL uses 9 decimals)
const baseAssetAmount = new BN(Math.floor(amount * 1e9))
// For perpetual futures: amount is USD position size, convert to base asset amount
// 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:`, {
inputAmount: amount,
calculatedAmount: amount * 1e9,
flooredAmount: Math.floor(amount * 1e9),
console.log(`💰 Position size conversion:`, {
usdPositionSize: amount,
solPrice: currentPrice,
solTokenAmount: solTokenAmount,
calculatedBaseAsset: solTokenAmount * 1e9,
flooredBaseAsset: Math.floor(solTokenAmount * 1e9),
baseAssetAmount: baseAssetAmount.toString()
})
@@ -236,7 +241,8 @@ export async function POST(request) {
console.log(`📊 Placing ${side} order:`, {
symbol,
marketIndex,
amount,
usdAmount: amount,
solAmount: solTokenAmount,
leverage,
currentPrice,
baseAssetAmount: baseAssetAmount.toString()
@@ -257,25 +263,25 @@ export async function POST(request) {
// Wait for main order to fill
await new Promise(resolve => setTimeout(resolve, 5000))
// 2. Calculate stop loss and take profit prices
const stopLossPercent = Math.max(riskPercent / 100, 0.02) // Minimum 2% to avoid "too close" issues
const takeProfitPercent = stopLossPercent * 2 // 2:1 risk reward ratio
// 2. Calculate stop loss and take profit prices using config percentages
const stopLossPercent = Math.max(riskPercent / 100, 0.02) // Use riskPercent from config, minimum 2%
const takeProfitPercentCalc = Math.max(takeProfitPercent / 100, 0.04) // Use takeProfitPercent from config, minimum 4%
let stopLossPrice, takeProfitPrice
if (direction === PositionDirection.LONG) {
stopLossPrice = currentPrice * (1 - stopLossPercent)
takeProfitPrice = currentPrice * (1 + takeProfitPercent)
takeProfitPrice = currentPrice * (1 + takeProfitPercentCalc)
} else {
stopLossPrice = currentPrice * (1 + stopLossPercent)
takeProfitPrice = currentPrice * (1 - takeProfitPercent)
takeProfitPrice = currentPrice * (1 - takeProfitPercentCalc)
}
console.log(`🎯 Risk management:`, {
stopLossPrice: stopLossPrice.toFixed(4),
takeProfitPrice: takeProfitPrice.toFixed(4),
riskPercent: `${stopLossPercent * 100}%`,
rewardRatio: '2:1',
stopLossPercent: `${stopLossPercent * 100}%`,
takeProfitPercent: `${takeProfitPercentCalc * 100}%`,
priceDifference: Math.abs(currentPrice - stopLossPrice).toFixed(4)
})

View File

@@ -20,10 +20,10 @@ export default function AutomationPageV2() {
timeframe: '1h', // Primary timeframe for backwards compatibility
selectedTimeframes: ['60'], // Multi-timeframe support
tradingAmount: 100,
balancePercentage: 50, // Default to 50% of available balance
maxLeverage: 5,
stopLossPercent: 2,
takeProfitPercent: 6,
riskPercentage: 2
takeProfitPercent: 6
})
const [status, setStatus] = useState(null)
@@ -242,7 +242,7 @@ export default function AutomationPageV2() {
</div>
{/* 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>
<label className="block text-sm font-medium text-gray-300 mb-2">Symbol</label>
<select
@@ -261,51 +261,39 @@ export default function AutomationPageV2() {
</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
type="number"
className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:border-blue-500"
type="range"
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"
step="10"
value={config.tradingAmount}
onChange={(e) => setConfig({...config, tradingAmount: parseFloat(e.target.value)})}
max="100"
step="5"
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}
/>
{balance && (
<p className="text-xs text-gray-400 mt-1">
Available: ${parseFloat(balance.availableBalance).toFixed(2)} Using {((config.tradingAmount / balance.availableBalance) * 100).toFixed(1)}% of balance
</p>
)}
<div className="flex justify-between text-xs text-gray-400 mt-1">
<span>10%</span>
<span>50%</span>
<span>100%</span>
</div>
{balance && config.maxLeverage > 1 && (
<p className="text-xs text-green-400 mt-1">
With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position size
</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
With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position exposure
</p>
)}
</div>
@@ -420,20 +408,6 @@ export default function AutomationPageV2() {
disabled={status?.isActive}
/>
</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>