diff --git a/build.gradle.kts b/build.gradle.kts index 1842c11f9..5afb9dc23 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ allprojects { } group = "exchange.dydx.abacus" -version = "1.13.17" +version = "1.13.18" repositories { google() diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt index 941d40bd8..783c4eded 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/MarginCalculator.kt @@ -758,12 +758,12 @@ internal object MarginCalculator { val positionSizeDifference = getPositionSizeDifference(subaccount, trade) ?: return null return calculateIsolatedMarginTransferAmountFromValues( - targetLeverage, - side, - oraclePrice, - price, - maxMarketLeverage, - positionSizeDifference, + targetLeverage = targetLeverage, + side = side, + oraclePrice = oraclePrice, + price = price, + maxMarketLeverage = maxMarketLeverage, + positionSizeDifference = positionSizeDifference, ) } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/ReceiptCalculator.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/ReceiptCalculator.kt index 3a9e60596..b6375c01c 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/ReceiptCalculator.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/ReceiptCalculator.kt @@ -68,7 +68,7 @@ internal class ReceiptCalculator { listOf( ReceiptLine.Equity, ReceiptLine.MarginUsage, - ReceiptLine.Fee, + ReceiptLine.TransferFee, ) } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/AccountTransformerV2.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/AccountTransformerV2.kt index c21380c14..e77f5c38d 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/AccountTransformerV2.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/AccountTransformerV2.kt @@ -50,7 +50,10 @@ internal class AccountTransformerV2( period = period, ) } else if (childSubaccountNumber != null) { - val childSubaccount = account.subaccounts[childSubaccountNumber] + val childSubaccount = account.subaccounts[childSubaccountNumber] ?: InternalSubaccountState( + subaccountNumber = childSubaccountNumber, + ) + account.subaccounts[childSubaccountNumber] = childSubaccount var transferAmountAppliedToParent = 0.0 var transferAmountAppliedToChild = 0.0 diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/SubaccountTransformerV2.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/SubaccountTransformerV2.kt index 2fdd2f526..6fcea4f4d 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/SubaccountTransformerV2.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/SubaccountTransformerV2.kt @@ -193,11 +193,12 @@ internal class SubaccountTransformerV2( hasTransfer: Boolean = false, ) { val deltaMarketId = delta?.marketId - val positions = subaccount.openPositions + val positions = subaccount.openPositions ?: mapOf() - val marketPosition = positions?.get(deltaMarketId) + val marketPosition = positions[deltaMarketId] val modifiedDelta = if (delta != null) { - val positionSize = marketPosition?.calculated?.get(CalculationPeriod.current)?.size ?: Numeric.double.ZERO + val positionSize = marketPosition?.calculated?.get(CalculationPeriod.current)?.size + ?: Numeric.double.ZERO transformDelta( delta = delta, positionSize = positionSize, @@ -207,19 +208,18 @@ internal class SubaccountTransformerV2( null } - if (positions != null) { - subaccount.openPositions = applyDeltaToPositions( - positions = positions, - delta = modifiedDelta, - period = period, - ) - } + subaccount.openPositions = applyDeltaToPositions( + positions = positions, + delta = modifiedDelta, + period = period, + ) val calculatedAtPeriod = subaccount.calculated[period] ?: InternalSubaccountCalculated() val usdcSize = modifiedDelta?.usdcSize ?: Numeric.double.ZERO if (delta != null && usdcSize != Numeric.double.ZERO) { val fee = modifiedDelta?.fee ?: Numeric.double.ZERO - val quoteBalance = subaccount.calculated[CalculationPeriod.current]?.quoteBalance ?: Numeric.double.ZERO + val quoteBalance = subaccount.calculated[CalculationPeriod.current]?.quoteBalance + ?: Numeric.double.ZERO calculatedAtPeriod.quoteBalance = quoteBalance + usdcSize + fee } else { calculatedAtPeriod.quoteBalance = null diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputCalculatorV2.kt b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputCalculatorV2.kt index 6c5b24fb1..f3f1b1454 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputCalculatorV2.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/calculator/V2/TradeInput/TradeInputCalculatorV2.kt @@ -107,7 +107,7 @@ internal class TradeInputCalculatorV2( subaccountNumber = subaccountNumber, trade = trade, market = markets[trade.marketId], - CalculationPeriod.post, + period = CalculationPeriod.post, ) return trade diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/output/input/ReceiptLine.kt b/src/commonMain/kotlin/exchange.dydx.abacus/output/input/ReceiptLine.kt index 6a78aadf6..d8f5b031e 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/output/input/ReceiptLine.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/output/input/ReceiptLine.kt @@ -27,7 +27,8 @@ enum class ReceiptLine(val rawValue: String) { CrossMarginUsage("CROSS_MARGIN_USAGE"), PositionMargin("POSITION_MARGIN"), PositionLeverage("POSITION_LEVERAGE"), - LiquidationPrice("LIQUIDATION_PRICE"); + LiquidationPrice("LIQUIDATION_PRICE"), + TransferFee("TRANSFER_FEE"); companion object { operator fun invoke(rawValue: String) = diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/configs/RewardsParamsProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/configs/RewardsParamsProcessor.kt index 64842de48..e4822254c 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/configs/RewardsParamsProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/configs/RewardsParamsProcessor.kt @@ -46,6 +46,7 @@ internal class RewardsParamsProcessor( denom = payload.params?.denom, denomExponent = payload.params?.denomExponent, marketId = payload.params?.marketId, + feeMultiplierPpm = payload.params?.feeMultiplierPpm, ) } else { null diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/HistoricalFundingsProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/HistoricalFundingsProcessor.kt index 10b678128..0712088d2 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/HistoricalFundingsProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/HistoricalFundingsProcessor.kt @@ -1,7 +1,11 @@ package exchange.dydx.abacus.processor.markets +import exchange.dydx.abacus.output.MarketHistoricalFunding import exchange.dydx.abacus.processor.base.BaseProcessor import exchange.dydx.abacus.protocols.ParserProtocol +import exchange.dydx.abacus.state.internalstate.InternalMarketState +import indexer.codegen.IndexerHistoricalFundingResponseObject +import kotlinx.datetime.Instant @Suppress("UNCHECKED_CAST") internal class HistoricalFundingsProcessor(parser: ParserProtocol) : BaseProcessor(parser) { @@ -14,6 +18,40 @@ internal class HistoricalFundingsProcessor(parser: ParserProtocol) : BaseProcess return if (payload != null) receivedList(existing, payload) else null } + fun process( + existing: InternalMarketState, + payload: List?, + ): InternalMarketState { + val history = payload?.mapNotNull { + val rate = parser.asDouble(it.rate) + val price = parser.asDouble(it.price) + val effectiveAt = parser.asDatetime(it.effectiveAt)?.toEpochMilliseconds()?.toDouble() + if (rate != null && price != null && effectiveAt != null) { + MarketHistoricalFunding( + rate = rate, + price = price, + effectiveAtMilliseconds = effectiveAt, + ) + } else { + null + } + }?.reversed() + + existing.historicalFundings = merge( + parser = parser, + existing = existing.historicalFundings, + incoming = history, + timeField = { item -> + item?.effectiveAtMilliseconds?.toLong()?.let { + Instant.fromEpochMilliseconds(it) + } + }, + ascending = true, + ) + + return existing + } + private fun receivedList( existing: List?, payload: List?, diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsSummaryProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsSummaryProcessor.kt index 51a2268f0..419a6c03e 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsSummaryProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/markets/MarketsSummaryProcessor.kt @@ -9,6 +9,8 @@ import exchange.dydx.abacus.utils.mutable import exchange.dydx.abacus.utils.safeSet import indexer.codegen.IndexerCandleResponse import indexer.codegen.IndexerCandleResponseObject +import indexer.codegen.IndexerHistoricalFundingResponse +import indexer.codegen.IndexerHistoricalFundingResponseObject import indexer.codegen.IndexerOrderbookResponseObject import indexer.codegen.IndexerSparklineTimePeriod import indexer.codegen.IndexerTradeResponse @@ -26,6 +28,7 @@ internal class MarketsSummaryProcessor( private val orderbookProcessor = OrderbookProcessor(parser) private val candlesProcessor = CandlesProcessor(parser) private val tradesProcessor = TradesProcessorV2(TradeProcessorV2(parser, localizer)) + private val historicalFundingsProcessor = HistoricalFundingsProcessor(parser) internal var groupingMultiplier: Int get() = if (staticTyping) orderbookProcessor.groupingMultiplier else marketsProcessor.groupingMultiplier @@ -183,6 +186,29 @@ internal class MarketsSummaryProcessor( return existing } + fun processHistoricalFundings( + existing: InternalMarketSummaryState, + payload: IndexerHistoricalFundingResponse?, + ): InternalMarketSummaryState { + val marketPaylaods = mutableMapOf>() + for (funding in payload?.historicalFunding?.toList() ?: emptyList()) { + val marketId = funding.ticker + if (marketId != null) { + val list = marketPaylaods[marketId] ?: emptyList() + marketPaylaods[marketId] = list + listOf(funding) + } + } + + for ((marketId, funding) in marketPaylaods) { + val marketState = existing.markets[marketId] ?: InternalMarketState() + historicalFundingsProcessor.process( + existing = marketState, + payload = funding, + ) + } + return existing + } + internal fun subscribedDeprecated( existing: Map?, content: Map @@ -347,7 +373,7 @@ internal class MarketsSummaryProcessor( return modify(existing, markets) } - internal fun receivedHistoricalFundings( + internal fun receivedHistoricalFundingsDeprecated( existing: Map?, payload: Map ): Map? { diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/internalstate/InternalState.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/internalstate/InternalState.kt index eb9100df0..836140ad6 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/internalstate/InternalState.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/internalstate/InternalState.kt @@ -10,6 +10,7 @@ import exchange.dydx.abacus.output.FeeTier import exchange.dydx.abacus.output.LaunchIncentivePoint import exchange.dydx.abacus.output.LaunchIncentiveSeason import exchange.dydx.abacus.output.MarketCandle +import exchange.dydx.abacus.output.MarketHistoricalFunding import exchange.dydx.abacus.output.MarketOrderbook import exchange.dydx.abacus.output.MarketTrade import exchange.dydx.abacus.output.PerpetualMarket @@ -201,7 +202,10 @@ internal data class InternalMarketState( var groupedOrderbook: MarketOrderbook? = null, // candles: resolution -> candles - var candles: MutableMap>? = null + var candles: MutableMap>? = null, + + // historical fundings + var historicalFundings: List? = null, ) internal data class InternalOrderbook( diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+HistoricalFunding.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+HistoricalFunding.kt index 45b2bca2f..f0a22aeb0 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+HistoricalFunding.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine+HistoricalFunding.kt @@ -1,15 +1,38 @@ package exchange.dydx.abacus.state.model +import exchange.dydx.abacus.protocols.asTypedObject import exchange.dydx.abacus.state.changes.Changes import exchange.dydx.abacus.state.changes.StateChanges +import indexer.codegen.IndexerHistoricalFundingResponse import kollections.iListOf +import kollections.toIList internal fun TradingStateMachine.historicalFundings(payload: String): StateChanges { - val json = parser.decodeJsonObject(payload) - return if (json != null) { - receivedHistoricalFundings(json) + if (staticTyping) { + val response = parser.asTypedObject(payload) + val marketIds: MutableList = mutableListOf() + for (item in response?.historicalFunding?.toList() ?: emptyList()) { + val marketId = item.ticker + if (marketId != null && marketIds.contains(marketId).not()) { + marketIds.add(marketId) + } + } + marketsProcessor.processHistoricalFundings( + existing = internalState.marketsSummary, + payload = response, + ) + return if (marketIds.isNotEmpty()) { + StateChanges(iListOf(Changes.historicalFundings), marketIds.toIList()) + } else { + StateChanges(iListOf()) + } } else { - StateChanges.noChange + val json = parser.decodeJsonObject(payload) + return if (json != null) { + receivedHistoricalFundings(json) + } else { + StateChanges.noChange + } } } @@ -23,7 +46,7 @@ private fun TradingStateMachine.receivedHistoricalFundings(payload: Map 0) { - marketsSummary = marketsProcessor.receivedHistoricalFundings(marketsSummary, payload) + marketsSummary = marketsProcessor.receivedHistoricalFundingsDeprecated(marketsSummary, payload) StateChanges(iListOf(Changes.historicalFundings), iListOf(marketId)) } else { StateChanges(iListOf()) diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt index a797ccc28..cb7435d48 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt @@ -1045,15 +1045,21 @@ open class TradingStateMachine( if (markets != null) { val modified = historicalFundings?.toIMutableMap() ?: mutableMapOf() for (marketId in markets) { - val data = parser.asList( - parser.value( - data, - "markets.markets.$marketId.historicalFunding", - ), - ) as? IList> - val existing = historicalFundings?.get(marketId) - val historicalFunding = MarketHistoricalFunding.create(existing, parser, data) - modified.typedSafeSet(marketId, historicalFunding) + if (staticTyping) { + val historicalFundings = internalState.marketsSummary.markets[marketId]?.historicalFundings + modified.typedSafeSet(marketId, historicalFundings?.toIList()) + } else { + val data = parser.asList( + parser.value( + data, + "markets.markets.$marketId.historicalFunding", + ), + ) as? IList> + val existing = historicalFundings?.get(marketId) + val historicalFunding = + MarketHistoricalFunding.create(existing, parser, data) + modified.typedSafeSet(marketId, historicalFunding) + } } historicalFundings = modified } else { diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/OnboardingSupervisor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/OnboardingSupervisor.kt index fa4d43183..8ef6bfc37 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/OnboardingSupervisor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/OnboardingSupervisor.kt @@ -865,13 +865,22 @@ internal class OnboardingSupervisor( } private fun receiveTransferGas(gas: BigDecimal?) { - val input = stateMachine.input - val oldFee = helper.parser.asDecimal(helper.parser.value(input, "transfer.fee")) - if (oldFee != gas) { - val oldState = stateMachine.state - val modified = input?.mutable() ?: iMapOf().mutable() - modified.safeSet("transfer.fee", gas) - update(StateChanges(iListOf(Changes.input)), oldState) + if (stateMachine.staticTyping) { + val gas = helper.parser.asDouble(gas) + val oldFee = stateMachine.internalState.input.transfer.fee + if (oldFee != gas) { + stateMachine.internalState.input.transfer.fee = gas + update(StateChanges(iListOf(Changes.input)), stateMachine.state) + } + } else { + val input = stateMachine.input + val oldFee = helper.parser.asDecimal(helper.parser.value(input, "transfer.fee")) + if (oldFee != gas) { + val oldState = stateMachine.state + val modified = input?.mutable() ?: iMapOf().mutable() + modified.safeSet("transfer.fee", gas) + update(StateChanges(iListOf(Changes.input)), oldState) + } } } diff --git a/v4_abacus.podspec b/v4_abacus.podspec index c6b3f0a04..bac082fdb 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.17' + spec.version = '1.13.18' spec.homepage = 'https://github.com/dydxprotocol/v4-abacus' spec.source = { :http=> ''} spec.authors = ''