Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static typing: Historical funding and fixing issues with isolated market orders #739

Merged
merged 5 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ allprojects {
}

group = "exchange.dydx.abacus"
version = "1.13.17"
version = "1.13.18"

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal class ReceiptCalculator {
listOf(
ReceiptLine.Equity,
ReceiptLine.MarginUsage,
ReceiptLine.Fee,
ReceiptLine.TransferFee,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For mobile only. FE web doesn't use this.

)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,12 @@ internal class SubaccountTransformerV2(
hasTransfer: Boolean = false,
) {
val deltaMarketId = delta?.marketId
val positions = subaccount.openPositions
val positions = subaccount.openPositions ?: mapOf()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing issue when create a new subaccount for isolated margin.


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,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ internal class TradeInputCalculatorV2(
subaccountNumber = subaccountNumber,
trade = trade,
market = markets[trade.marketId],
CalculationPeriod.post,
period = CalculationPeriod.post,
)

return trade
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For mobile only. FE web doesn't use this.


companion object {
operator fun invoke(rawValue: String) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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<IndexerHistoricalFundingResponseObject>?,
): 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<Any>?,
payload: List<Any>?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -183,6 +186,29 @@ internal class MarketsSummaryProcessor(
return existing
}

fun processHistoricalFundings(
existing: InternalMarketSummaryState,
payload: IndexerHistoricalFundingResponse?,
): InternalMarketSummaryState {
val marketPaylaods = mutableMapOf<String, List<IndexerHistoricalFundingResponseObject>>()
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<String, Any>?,
content: Map<String, Any>
Expand Down Expand Up @@ -347,7 +373,7 @@ internal class MarketsSummaryProcessor(
return modify(existing, markets)
}

internal fun receivedHistoricalFundings(
internal fun receivedHistoricalFundingsDeprecated(
existing: Map<String, Any>?,
payload: Map<String, Any>
): Map<String, Any>? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -201,7 +202,10 @@ internal data class InternalMarketState(
var groupedOrderbook: MarketOrderbook? = null,

// candles: resolution -> candles
var candles: MutableMap<String, List<MarketCandle>>? = null
var candles: MutableMap<String, List<MarketCandle>>? = null,

// historical fundings
var historicalFundings: List<MarketHistoricalFunding>? = null,
)

internal data class InternalOrderbook(
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IndexerHistoricalFundingResponse>(payload)
val marketIds: MutableList<String> = 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
}
}
}

Expand All @@ -23,7 +46,7 @@ private fun TradingStateMachine.receivedHistoricalFundings(payload: Map<String,
return if (marketId != null) {
val size = parser.asList(payload["historicalFunding"])?.size ?: 0
if (size > 0) {
marketsSummary = marketsProcessor.receivedHistoricalFundings(marketsSummary, payload)
marketsSummary = marketsProcessor.receivedHistoricalFundingsDeprecated(marketsSummary, payload)
StateChanges(iListOf(Changes.historicalFundings), iListOf(marketId))
} else {
StateChanges(iListOf())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Map<String, Any>>
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<Map<String, Any>>
val existing = historicalFundings?.get(marketId)
val historicalFunding =
MarketHistoricalFunding.create(existing, parser, data)
modified.typedSafeSet(marketId, historicalFunding)
}
}
historicalFundings = modified
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any>().mutable()
modified.safeSet("transfer.fee", gas)
update(StateChanges(iListOf(Changes.input)), oldState)
if (stateMachine.staticTyping) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding processing for transfer gas

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<String, Any>().mutable()
modified.safeSet("transfer.fee", gas)
update(StateChanges(iListOf(Changes.input)), oldState)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion v4_abacus.podspec
Original file line number Diff line number Diff line change
@@ -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 = ''
Expand Down