diff --git a/build.gradle.kts b/build.gradle.kts index 8be4d821a..9675f2b29 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ allprojects { } group = "exchange.dydx.abacus" -version = "1.13.13" +version = "1.13.14" repositories { google() diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketProcessor.kt index 91f86f2cf..5da487db2 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketProcessor.kt @@ -1,5 +1,6 @@ package exchange.dydx.abacus.processor.markets +import com.ionspin.kotlin.bignum.decimal.toBigDecimal import exchange.dydx.abacus.output.MarketConfigs import exchange.dydx.abacus.output.MarketConfigsV4 import exchange.dydx.abacus.output.MarketPerpetual @@ -26,6 +27,7 @@ import kollections.toIList import numberOfDecimals import kotlin.math.max import kotlin.math.min +import kotlin.math.pow import kotlin.time.Duration.Companion.seconds internal interface MarketProcessorProtocol : BaseProcessorProtocol { @@ -262,8 +264,28 @@ internal class MarketProcessor( private fun createConfigs( payload: IndexerCompositeMarketObject, ): MarketConfigs { - val stepSize = parser.asDouble(payload.stepSize) - val tickSize = parser.asDouble(payload.tickSize) + var stepSize = parser.asDouble(payload.stepSize) + var tickSize = parser.asDouble(payload.tickSize) + + val atomicResolution = parser.asInt(payload.atomicResolution) + val stepBaseQuantums = parser.asInt(payload.stepBaseQuantums) + val quantumConversionExponent = parser.asInt(payload.quantumConversionExponent) + val subticksPerTick = parser.asInt(payload.subticksPerTick) + if (stepSize == null) { + if (atomicResolution != null && stepBaseQuantums != null) { + stepSize = getStepSize(stepBaseQuantums, atomicResolution) + } else { + stepSize = fallbackStepSize + } + } + if (tickSize == null) { + if (subticksPerTick != null && quantumConversionExponent != null && atomicResolution != null) { + tickSize = getTickSize(subticksPerTick, quantumConversionExponent, atomicResolution) + } else { + tickSize = fallbackTickSize + } + } + return MarketConfigs( clobPairId = payload.clobPairId, largeSize = null, @@ -493,15 +515,68 @@ internal class MarketProcessor( parser.asDouble(payload["basePositionSize"]), ) // v4 } + + // calculate stepSize and tickSize if not provided + val stepSize = parser.asDouble(configs["stepSize"]) + val tickSize = parser.asDouble(configs["tickSize"]) + val atomicResolution = parser.asInt(parser.value(configsV4, "atomicResolution")) + val stepBaseQuantums = parser.asInt(parser.value(configsV4, "stepBaseQuantums")) + val quantumConversionExponent = parser.asInt(parser.value(configsV4, "quantumConversionExponent")) + if (stepSize == null) { + if (atomicResolution != null && stepBaseQuantums != null) { + val stepSize = getStepSize(stepBaseQuantums, atomicResolution) + configs.safeSet("stepSize", stepSize) + } else { + configs.safeSet("stepSize", fallbackStepSize) + } + } + if (tickSize == null) { + val subticksPerTick = parser.asInt(parser.value(configsV4, "subticksPerTick")) + if (subticksPerTick != null && quantumConversionExponent != null && atomicResolution != null) { + val tickSize = getTickSize(subticksPerTick, quantumConversionExponent, atomicResolution) + configs.safeSet("tickSize", tickSize) + } else { + configs.safeSet("tickSize", fallbackTickSize) + } + } return configs } + private fun getStepSize( + stepBaseQuantums: Int, + atomicResolution: Int, + ): Double { + val stepSize = stepBaseQuantums.toBigDecimal().times(10.0.toBigDecimal().pow(atomicResolution)) + return parser.asDouble(stepSize) ?: fallbackStepSize + } + + private fun getTickSize( + subticksPerTick: Int, + quantumConversionExponent: Int, + atomicResolution: Int + ): Double { + val quoteCurrencyAtomicResolution = -6 + val tickSize = subticksPerTick.toBigDecimal().times( + 10.0.toBigDecimal().pow(quantumConversionExponent), + ).times( + 10.0.toBigDecimal().pow(quoteCurrencyAtomicResolution), + ).divide( + 10.0.toBigDecimal().pow(atomicResolution), + ) + return parser.asDouble(tickSize) ?: fallbackTickSize + } + private fun perpetual( existing: Map?, payload: Map, oraclePrice: Double?, ): Map { val perpetual = transform(existing, payload, perpetualKeyMap) + val openInterest = parser.asDouble(perpetual["openInterest"]) + if (openInterest == null) { + perpetual.safeSet("openInterest", 0.0) + perpetual.safeSet("openInterestUSDC", 0.0) + } oraclePrice?.let { parser.asDouble(perpetual["openInterest"])?.let { perpetual["openInterestUSDC"] = it * oraclePrice diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsProcessor.kt index 09a0d409d..21645c465 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsProcessor.kt @@ -189,11 +189,20 @@ internal class MarketsProcessor( for ((market, data) in narrowedPayload) { val marketPayload = parser.asNativeMap(data) if (marketPayload != null) { - val receivedMarket = marketProcessorDeprecated!!.receivedDeltaDeprecated( - parser.asNativeMap(existing?.get(market)), - marketPayload, - ) - markets[market] = receivedMarket + if (marketPayload["clobPairId"] != null) { + // new permissionless market + val receivedMarket = marketProcessorDeprecated!!.received( + parser.asNativeMap(existing?.get(market)), + marketPayload, + ) + markets[market] = receivedMarket + } else { + val receivedMarket = marketProcessorDeprecated!!.receivedDeltaDeprecated( + parser.asNativeMap(existing?.get(market)), + marketPayload, + ) + markets[market] = receivedMarket + } } } return markets diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt index 710d8417e..b3af2204b 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt @@ -421,6 +421,12 @@ class V4MarketsTests : V4BaseTests() { assertEquals(ethMarket?.perpetualMarket?.perpetual?.volume24H, 626131271.611287094) assertEquals(ethMarket?.perpetualMarket?.perpetual?.trades24H, 1214631.0) assertEquals(ethMarket?.perpetualMarket?.perpetual?.nextFundingRate, 0.0) + + val mlnMarket = markets["MLN-USD"] + assertEquals(mlnMarket?.perpetualMarket?.oraclePrice, null) + assertEquals(mlnMarket?.perpetualMarket?.perpetual?.openInterest, null) + assertEquals(mlnMarket?.perpetualMarket?.configs?.stepSize, 0.1) + assertEquals(mlnMarket?.perpetualMarket?.configs?.tickSize, 0.01) } else { test( { @@ -475,7 +481,26 @@ class V4MarketsTests : V4BaseTests() { "nextFundingRate":0.0, "trades24H":1214631 } + }, + "MLN-USD":{ + "market":"MLN-USD", + "status":{ + "canTrade":true, + "canReduce":true + }, + "configs":{ + "stepSize":0.1, + "tickSize":0.01, + "initialMarginFraction":0.05, + "clobPairId":111 + }, + "perpetual":{ + "openInterest":0.0, + "openInterestLowerCap": 500000.0, + "openInterestUpperCap": 1000000.0 + } } + } } } diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/tests/payloads/MarketsChannelMock.kt b/src/commonTest/kotlin/exchange.dydx.abacus/tests/payloads/MarketsChannelMock.kt index be02b9ae2..c81ddee0d 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/tests/payloads/MarketsChannelMock.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/tests/payloads/MarketsChannelMock.kt @@ -2791,7 +2791,22 @@ internal class MarketsChannelMock { "volume24H":"626131271.611287094", "trades24H":1214631, "openInterest":"67603.376057" - } + }, + "MLN-USD": { + "id": "111", + "clobPairId": "111", + "ticker": "MLN-USD", + "marketId": 111, + "status": "ACTIVE", + "quantumConversionExponent": -9, + "atomicResolution": -7, + "subticksPerTick": 1000000, + "stepBaseQuantums": 1000000, + "initialMarginFraction": "0.05", + "maintenanceMarginFraction": "0.03", + "openInterestLowerCap": "500000", + "openInterestUpperCap": "1000000" + } } } ] diff --git a/v4_abacus.podspec b/v4_abacus.podspec index 531a89995..4ae24dd3b 100644 --- a/v4_abacus.podspec +++ b/v4_abacus.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'v4_abacus' - spec.version = '1.13.13' + spec.version = '1.13.14' spec.homepage = 'https://github.com/dydxprotocol/v4-abacus' spec.source = { :http=> ''} spec.authors = ''