CRITICAL FIX: TP/SL orders using wrong size calculation
**ROOT CAUSE:** placeExitOrders() calculated position size using TP/SL prices instead of entry price
**Problem:**
- TP1 order size: 85 / TP1_price (00.746) = 2.914 SOL
- Actual position: 80 / entry_price (99.946) = 3.901 SOL
- TP1 should close: 3.901 * 75% = 2.926 SOL
- But it only closed: 2.914 SOL = 74.7% ❌ WRONG!
**Result:** TP1 closed ~25% instead of 75%, no runner left
**Fix:**
- Changed usdToBase() to use entryPrice for ALL size calculations
- Added entryPrice param to PlaceExitOrdersOptions interface
- Updated all API routes to pass entryPrice
**Testing:** Next trade will have correctly sized TP/SL orders
This commit is contained in:
2
.env
2
.env
@@ -61,7 +61,7 @@ PYTH_HERMES_URL=https://hermes.pyth.network
|
|||||||
# Position sizing
|
# Position sizing
|
||||||
# Base position size in USD (default: 50 for safe testing)
|
# Base position size in USD (default: 50 for safe testing)
|
||||||
# Example: 50 with 10x leverage = $500 notional position
|
# Example: 50 with 10x leverage = $500 notional position
|
||||||
MAX_POSITION_SIZE_USD=78
|
MAX_POSITION_SIZE_USD=54
|
||||||
|
|
||||||
# Leverage multiplier (1-20, default: 10)
|
# Leverage multiplier (1-20, default: 10)
|
||||||
# Higher leverage = bigger gains AND bigger losses
|
# Higher leverage = bigger gains AND bigger losses
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
const exitRes = await placeExitOrders({
|
const exitRes = await placeExitOrders({
|
||||||
symbol: driftSymbol,
|
symbol: driftSymbol,
|
||||||
positionSizeUSD: positionSizeUSD,
|
positionSizeUSD: positionSizeUSD,
|
||||||
|
entryPrice: entryPrice,
|
||||||
tp1Price,
|
tp1Price,
|
||||||
tp2Price,
|
tp2Price,
|
||||||
stopLossPrice,
|
stopLossPrice,
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ReducePos
|
|||||||
symbol: trade.symbol,
|
symbol: trade.symbol,
|
||||||
direction: trade.direction,
|
direction: trade.direction,
|
||||||
positionSizeUSD: remainingSizeUSD,
|
positionSizeUSD: remainingSizeUSD,
|
||||||
|
entryPrice: trade.entryPrice,
|
||||||
tp1Price: newTP1,
|
tp1Price: newTP1,
|
||||||
tp2Price: newTP2,
|
tp2Price: newTP2,
|
||||||
stopLossPrice: newSL,
|
stopLossPrice: newSL,
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ScalePosi
|
|||||||
symbol: trade.symbol,
|
symbol: trade.symbol,
|
||||||
direction: trade.direction,
|
direction: trade.direction,
|
||||||
positionSizeUSD: newTotalValue,
|
positionSizeUSD: newTotalValue,
|
||||||
|
entryPrice: newAvgEntry,
|
||||||
tp1Price: newTP1,
|
tp1Price: newTP1,
|
||||||
tp2Price: newTP2,
|
tp2Price: newTP2,
|
||||||
stopLossPrice: newSL,
|
stopLossPrice: newSL,
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<TestTrade
|
|||||||
const exitRes = await placeExitOrders({
|
const exitRes = await placeExitOrders({
|
||||||
symbol,
|
symbol,
|
||||||
positionSizeUSD,
|
positionSizeUSD,
|
||||||
|
entryPrice,
|
||||||
tp1Price,
|
tp1Price,
|
||||||
tp2Price,
|
tp2Price,
|
||||||
stopLossPrice,
|
stopLossPrice,
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<TestTrade
|
|||||||
const exitRes = await placeExitOrders({
|
const exitRes = await placeExitOrders({
|
||||||
symbol: driftSymbol,
|
symbol: driftSymbol,
|
||||||
positionSizeUSD: positionSizeUSD,
|
positionSizeUSD: positionSizeUSD,
|
||||||
|
entryPrice: entryPrice,
|
||||||
tp1Price,
|
tp1Price,
|
||||||
tp2Price,
|
tp2Price,
|
||||||
stopLossPrice,
|
stopLossPrice,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export interface PlaceExitOrdersResult {
|
|||||||
export interface PlaceExitOrdersOptions {
|
export interface PlaceExitOrdersOptions {
|
||||||
symbol: string
|
symbol: string
|
||||||
positionSizeUSD: number
|
positionSizeUSD: number
|
||||||
|
entryPrice: number // CRITICAL: Entry price for calculating position size in base assets
|
||||||
tp1Price: number
|
tp1Price: number
|
||||||
tp2Price: number
|
tp2Price: number
|
||||||
stopLossPrice: number
|
stopLossPrice: number
|
||||||
@@ -222,8 +223,10 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
|
|||||||
const signatures: string[] = []
|
const signatures: string[] = []
|
||||||
|
|
||||||
// Helper to compute base asset amount from USD notional and price
|
// Helper to compute base asset amount from USD notional and price
|
||||||
const usdToBase = (usd: number, price: number) => {
|
// CRITICAL: Use ENTRY price to calculate position size, not TP price!
|
||||||
const base = usd / price
|
// This ensures we close the correct percentage of the actual position
|
||||||
|
const usdToBase = (usd: number) => {
|
||||||
|
const base = usd / options.entryPrice // Use entry price for size calculation
|
||||||
return Math.floor(base * 1e9) // 9 decimals expected by SDK
|
return Math.floor(base * 1e9) // 9 decimals expected by SDK
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +247,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
|
|||||||
|
|
||||||
// Place TP1 LIMIT reduce-only
|
// Place TP1 LIMIT reduce-only
|
||||||
if (tp1USD > 0) {
|
if (tp1USD > 0) {
|
||||||
const baseAmount = usdToBase(tp1USD, options.tp1Price)
|
const baseAmount = usdToBase(tp1USD)
|
||||||
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
||||||
const orderParams: any = {
|
const orderParams: any = {
|
||||||
orderType: OrderType.LIMIT,
|
orderType: OrderType.LIMIT,
|
||||||
@@ -266,7 +269,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
|
|||||||
|
|
||||||
// Place TP2 LIMIT reduce-only
|
// Place TP2 LIMIT reduce-only
|
||||||
if (tp2USD > 0) {
|
if (tp2USD > 0) {
|
||||||
const baseAmount = usdToBase(tp2USD, options.tp2Price)
|
const baseAmount = usdToBase(tp2USD)
|
||||||
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
||||||
const orderParams: any = {
|
const orderParams: any = {
|
||||||
orderType: OrderType.LIMIT,
|
orderType: OrderType.LIMIT,
|
||||||
@@ -293,7 +296,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
|
|||||||
// 3. Single TRIGGER_MARKET (default, guaranteed execution)
|
// 3. Single TRIGGER_MARKET (default, guaranteed execution)
|
||||||
|
|
||||||
const slUSD = options.positionSizeUSD
|
const slUSD = options.positionSizeUSD
|
||||||
const slBaseAmount = usdToBase(slUSD, options.stopLossPrice)
|
const slBaseAmount = usdToBase(slUSD)
|
||||||
|
|
||||||
if (slBaseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
if (slBaseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
|
||||||
const useDualStops = options.useDualStops ?? false
|
const useDualStops = options.useDualStops ?? false
|
||||||
|
|||||||
Reference in New Issue
Block a user