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,
|
||||
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
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user