From e4bcbb8e1d3c3219d40346f795574999326d48a2 Mon Sep 17 00:00:00 2001 From: Jared Vu Date: Fri, 15 Nov 2024 10:59:14 -0800 Subject: [PATCH] chore(default-target-leverage): update default target leverage to 2x (#748) Co-authored-by: Rui <102453770+ruixhuang@users.noreply.github.com> --- .../exchange.dydx.abacus/calculator/MarginCalculator.kt | 5 +++-- .../calculator/MarginModeCalculator.kt | 6 ++++-- .../V2/TradeInput/TradeInputMarginModeCalculator.kt | 6 ++++-- .../V2/TradeInput/TradeInputMarketOrderCalculator.kt | 9 +++++---- .../processor/input/ClosePositionInputProcessor.kt | 4 +++- .../processor/input/TradeInputProcessor.kt | 8 +++++--- .../model/TradingStateMachine+ClosePositionInput.kt | 5 ++++- .../state/model/TradingStateMachine+TradeInput.kt | 6 ++++-- .../kotlin/exchange.dydx.abacus/utils/Constants.kt | 1 + .../payload/IsolatedMarginModeTests.kt | 4 ++-- .../exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt | 4 ++++ 11 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt index 783c4eded..e1b84b766 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt @@ -14,6 +14,7 @@ import exchange.dydx.abacus.state.internalstate.InternalMarketState import exchange.dydx.abacus.state.internalstate.InternalPerpetualPosition import exchange.dydx.abacus.state.internalstate.InternalSubaccountState import exchange.dydx.abacus.state.internalstate.InternalTradeInputState +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.IList import exchange.dydx.abacus.utils.Logger import exchange.dydx.abacus.utils.MAX_LEVERAGE_BUFFER_PERCENT @@ -702,7 +703,7 @@ internal object MarginCalculator { val oraclePrice = market?.perpetualMarket?.oraclePrice ?: return null val price = trade.summary?.price ?: return null val maxMarketLeverage = market.perpetualMarket?.configs?.maxMarketLeverage ?: return null - val targetLeverage = trade.targetLeverage ?: maxMarketLeverage + val targetLeverage = trade.targetLeverage ?: min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) val positionSizeDifference = getPositionSizeDifference(subaccount, trade) ?: return null return calculateIsolatedMarginTransferAmountFromValues( @@ -732,7 +733,7 @@ internal object MarginCalculator { val effectiveImf = parser.asDouble(parser.value(market, "configs.effectiveInitialMarginFraction")) ?: Numeric.double.ZERO val maxMarketLeverage = getMaxMarketLeverageDeprecated(effectiveImf = effectiveImf, imf = initialMarginFraction) - val targetLeverage = parser.asDouble(trade["targetLeverage"]) ?: maxMarketLeverage + val targetLeverage = parser.asDouble(trade["targetLeverage"]) ?: min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) val positionSizeDifference = getPositionSizeDifferenceDeprecated(parser, subaccount, trade) ?: return null return calculateIsolatedMarginTransferAmountFromValues( diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginModeCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginModeCalculator.kt index 43921c579..2fc3144a8 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginModeCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginModeCalculator.kt @@ -1,8 +1,10 @@ package exchange.dydx.abacus.calculator +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric import exchange.dydx.abacus.utils.Parser import exchange.dydx.abacus.utils.mutable +import kotlin.math.min object MarginModeCalculator { @@ -49,7 +51,7 @@ object MarginModeCalculator { subaccountNumber, ) val existingPositionLeverage = parser.asDouble(parser.value(existingPosition, "leverage.current")) - modified["targetLeverage"] = existingPositionLeverage ?: maxMarketLeverage + modified["targetLeverage"] = existingPositionLeverage ?: min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } } else { val marketMarginMode = MarginCalculator.findMarketMarginModeDeprecated( @@ -60,7 +62,7 @@ object MarginModeCalculator { "ISOLATED" -> { modified["marginMode"] = marketMarginMode if (parser.asDouble(tradeInput["targetLeverage"]) == null) { - modified["targetLeverage"] = maxMarketLeverage + modified["targetLeverage"] = min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarginModeCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarginModeCalculator.kt index 9e5c48758..7a8e3cf98 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarginModeCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarginModeCalculator.kt @@ -6,7 +6,9 @@ import exchange.dydx.abacus.output.input.MarginMode import exchange.dydx.abacus.state.internalstate.InternalAccountState import exchange.dydx.abacus.state.internalstate.InternalMarketState import exchange.dydx.abacus.state.internalstate.InternalTradeInputState +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric +import kotlin.math.min internal class TradeInputMarginModeCalculator { fun updateTradeInputMarginMode( @@ -33,7 +35,7 @@ internal class TradeInputMarginModeCalculator { subaccountNumber = subaccountNumber, ) val existingPositionLeverage = existingPosition?.calculated?.get(CalculationPeriod.current)?.leverage - tradeInput.targetLeverage = if (existingPositionLeverage != null && existingPositionLeverage > Numeric.double.ZERO) existingPositionLeverage else maxMarketLeverage + tradeInput.targetLeverage = if (existingPositionLeverage != null && existingPositionLeverage > Numeric.double.ZERO) existingPositionLeverage else min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } } else { val marketMarginMode = MarginCalculator.findMarketMarginMode( @@ -42,7 +44,7 @@ internal class TradeInputMarginModeCalculator { when (marketMarginMode) { MarginMode.Isolated -> { tradeInput.marginMode = marketMarginMode - tradeInput.targetLeverage = tradeInput.targetLeverage ?: maxMarketLeverage + tradeInput.targetLeverage = tradeInput.targetLeverage ?: min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } MarginMode.Cross -> { diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarketOrderCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarketOrderCalculator.kt index 60a96f81b..380be1dbd 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarketOrderCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputMarketOrderCalculator.kt @@ -675,14 +675,15 @@ internal class TradeInputMarketOrderCalculator() { tradeLeverage: Double, isTradeSameSide: Boolean, ): Double { - if (freeCollateral <= Numeric.double.ZERO || tradeLeverage <= Numeric.double.ZERO) { + val tradeLeverageAbs = tradeLeverage.abs() + if (freeCollateral <= Numeric.double.ZERO || tradeLeverageAbs == Numeric.double.ZERO) { return Numeric.double.ZERO } return if (isTradeSameSide) { - (usdcSize / tradeLeverage) / freeCollateral + (usdcSize / tradeLeverageAbs) / freeCollateral } else { - val existingBalance = positionSize.abs() / tradeLeverage - (usdcSize / tradeLeverage - existingBalance) / (freeCollateral + existingBalance) + val existingBalance = positionSize.abs() / tradeLeverageAbs + (usdcSize / tradeLeverageAbs - existingBalance) / (freeCollateral + existingBalance) } } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/ClosePositionInputProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/ClosePositionInputProcessor.kt index 38695b085..8a17aab69 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/ClosePositionInputProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/ClosePositionInputProcessor.kt @@ -25,8 +25,10 @@ import exchange.dydx.abacus.state.internalstate.InternalWalletState import exchange.dydx.abacus.state.internalstate.safeCreate import exchange.dydx.abacus.state.manager.StatsigConfig import exchange.dydx.abacus.state.model.ClosePositionInputField +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric import kollections.iListOf +import kotlin.math.min internal interface ClosePositionInputProcessorProtocol { fun closePosition( @@ -107,7 +109,7 @@ internal class ClosePositionInputProcessor( val maxMarketLeverage = market?.perpetualMarket?.configs?.maxMarketLeverage ?: Numeric.double.ONE val currentPositionLeverage = position.calculated[CalculationPeriod.current]?.leverage?.abs() - trade.targetLeverage = if (currentPositionLeverage != null && currentPositionLeverage > Numeric.double.ZERO) currentPositionLeverage else maxMarketLeverage + trade.targetLeverage = if (currentPositionLeverage != null && currentPositionLeverage > Numeric.double.ZERO) currentPositionLeverage else min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) // default full close trade.sizePercent = 1.0 diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/TradeInputProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/TradeInputProcessor.kt index a8ca3589f..bf93a0427 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/TradeInputProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/input/TradeInputProcessor.kt @@ -28,8 +28,10 @@ import exchange.dydx.abacus.state.internalstate.InternalTradeInputState import exchange.dydx.abacus.state.internalstate.InternalWalletState import exchange.dydx.abacus.state.internalstate.safeCreate import exchange.dydx.abacus.state.model.TradeInputField +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric import kollections.iListOf +import kotlin.math.min internal interface TradeInputProcessorProtocol { fun tradeInMarket( @@ -262,7 +264,7 @@ internal class TradeInputProcessor( val market = marketSummaryState.markets[trade.marketId] val maxMarketLeverage = market?.perpetualMarket?.configs?.maxMarketLeverage ?: Numeric.double.ONE - trade.targetLeverage = maxMarketLeverage + trade.targetLeverage = min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } val changedSubaccountNumbers = @@ -335,7 +337,7 @@ internal class TradeInputProcessor( } else if (existingOrder != null) { trade.marginMode = if (existingOrder.subaccountNumber == subaccountNumber) MarginMode.Cross else MarginMode.Isolated - trade.targetLeverage = maxMarketLeverage + trade.targetLeverage = min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } else { val marketType = marketState?.perpetualMarket?.configs?.perpetualMarketType trade.marginMode = when (marketType) { @@ -343,7 +345,7 @@ internal class TradeInputProcessor( PerpetualMarketType.ISOLATED -> MarginMode.Isolated else -> null } - trade.targetLeverage = maxMarketLeverage + trade.targetLeverage = min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) } } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+ClosePositionInput.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+ClosePositionInput.kt index 1b7cd31c6..ceb66c45a 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+ClosePositionInput.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+ClosePositionInput.kt @@ -10,6 +10,7 @@ import exchange.dydx.abacus.responses.cannotModify import exchange.dydx.abacus.state.changes.Changes import exchange.dydx.abacus.state.changes.StateChanges import exchange.dydx.abacus.state.manager.StatsigConfig +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric import exchange.dydx.abacus.utils.mutable import exchange.dydx.abacus.utils.mutableMapOf @@ -17,6 +18,7 @@ import exchange.dydx.abacus.utils.safeSet import kollections.JsExport import kollections.iListOf import kotlinx.serialization.Serializable +import kotlin.math.min @JsExport @Serializable @@ -114,8 +116,9 @@ fun TradingStateMachine.closePosition( } else { Numeric.double.ONE } + val currentPositionLeverage = parser.asDouble(parser.value(position, "leverage.current"))?.abs() - trade["targetLeverage"] = if (currentPositionLeverage != null && currentPositionLeverage > 0) currentPositionLeverage else maxMarketLeverage + trade["targetLeverage"] = if (currentPositionLeverage != null && currentPositionLeverage > 0) currentPositionLeverage else min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage) // default full close trade.safeSet("size.percent", 1.0) diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+TradeInput.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+TradeInput.kt index cabb93a86..208f61073 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+TradeInput.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+TradeInput.kt @@ -11,6 +11,7 @@ import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.responses.cannotModify import exchange.dydx.abacus.state.changes.Changes import exchange.dydx.abacus.state.changes.StateChanges +import exchange.dydx.abacus.utils.DEFAULT_TARGET_LEVERAGE import exchange.dydx.abacus.utils.Numeric import exchange.dydx.abacus.utils.mutable import exchange.dydx.abacus.utils.mutableMapOf @@ -18,6 +19,7 @@ import exchange.dydx.abacus.utils.safeSet import kollections.JsExport import kollections.iListOf import kotlinx.serialization.Serializable +import kotlin.math.min @JsExport @Serializable @@ -170,11 +172,11 @@ internal fun TradingStateMachine.tradeInMarket( } else if (existingOrder != null) { val orderMarginMode = if ((parser.asInt(parser.value(existingOrder, "subaccountNumber")) ?: subaccountNumber) == subaccountNumber) MarginMode.Cross.rawValue else MarginMode.Isolated.rawValue it.safeSet("marginMode", orderMarginMode) - it.safeSet("targetLeverage", maxMarketLeverage) + it.safeSet("targetLeverage", min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage)) } else { val marketType = parser.asString(parser.value(marketsSummary, "markets.$marketId.configs.perpetualMarketType")) it.safeSet("marginMode", MarginMode.invoke(marketType)?.rawValue) - it.safeSet("targetLeverage", maxMarketLeverage) + it.safeSet("targetLeverage", min(DEFAULT_TARGET_LEVERAGE, maxMarketLeverage)) } } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/utils/Constants.kt b/src/commonMain/kotlin/exchange.dydx.abacus/utils/Constants.kt index 05f1f791c..04cbcdb78 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/utils/Constants.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/utils/Constants.kt @@ -17,6 +17,7 @@ internal const val MAX_FREE_COLLATERAL_BUFFER_PERCENT = 0.95 // Isolated Margin Constants internal const val MAX_LEVERAGE_BUFFER_PERCENT = 0.98 internal const val MARGIN_COLLATERALIZATION_CHECK_BUFFER = 0.01; +internal const val DEFAULT_TARGET_LEVERAGE = 2.0; // Order flags internal const val SHORT_TERM_ORDER_FLAGS = 0 diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt index b04eebe1e..114cf6d04 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt @@ -212,7 +212,7 @@ class IsolatedMarginModeTests : V4BaseTests(true) { assertEquals("NEAR-USD", trade.marketId) assertEquals(MarginMode.Cross, trade.marginMode) assertEquals(true, trade.options.needsMarginMode) - assertEquals(10.0, trade.targetLeverage) + assertEquals(2.0, trade.targetLeverage) } else { test( { @@ -225,7 +225,7 @@ class IsolatedMarginModeTests : V4BaseTests(true) { "trade": { "marketId": "NEAR-USD", "marginMode": "CROSS", - "targetLeverage": 10.0, + "targetLeverage": 5.0, "options": { "needsMarginMode": true } diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt index b00f51e3a..124d61542 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt @@ -718,6 +718,10 @@ open class V4TradeInputTests : V4BaseTests() { perp.trade("380", TradeInputField.usdcSize, 0) }, null) + test({ + perp.trade("20", TradeInputField.targetLeverage, 0) + }, null) + if (perp.staticTyping) { perp.trade("1500", TradeInputField.limitPrice, 0)