Skip to content

Commit

Permalink
TRCL-3682 Add isNew to PerpetualMarket (#633)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruixhuang authored Sep 9, 2024
1 parent 401dbcd commit 854a38b
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ data class MarketPerpetual(
val openInterestLowerCap: Double? = null,
val openInterestUpperCap: Double? = null,
val line: IList<Double>?,
val isNew: Boolean = false,
) {
companion object {
internal fun create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import exchange.dydx.abacus.utils.parseException
import exchange.dydx.abacus.utils.safeSet
import indexer.codegen.IndexerPerpetualMarketStatus
import indexer.codegen.IndexerPerpetualMarketType
import indexer.codegen.IndexerSparklineTimePeriod
import indexer.models.IndexerCompositeMarketObject
import indexer.models.IndexerWsMarketOraclePriceObject
import kollections.toIList
Expand All @@ -30,7 +31,7 @@ import kotlin.time.Duration.Companion.seconds
internal interface MarketProcessorProtocol : BaseProcessorProtocol {
fun process(marketId: String, payload: IndexerCompositeMarketObject): PerpetualMarket?
fun processOraclePrice(marketId: String, payload: IndexerWsMarketOraclePriceObject): PerpetualMarket?
fun processSparklines(marketId: String, payload: List<String>): PerpetualMarket?
fun processSparklines(marketId: String, payload: List<String>, period: IndexerSparklineTimePeriod): PerpetualMarket?
fun clearCachedOraclePrice(marketId: String)
}

Expand Down Expand Up @@ -153,6 +154,7 @@ internal class MarketProcessor(
private var cachedIndexerMarketResponses: MutableMap<String, IndexerCompositeMarketObject> = mutableMapOf()
private var cachedIndexerOraclePrices: MutableMap<String, IndexerWsMarketOraclePriceObject> = mutableMapOf()
private var cachedIndexerSparklines: MutableMap<String, List<Double>> = mutableMapOf()
private var cachedIsNew: MutableMap<String, Boolean> = mutableMapOf()

override fun process(
marketId: String,
Expand All @@ -178,9 +180,19 @@ internal class MarketProcessor(
override fun processSparklines(
marketId: String,
payload: List<String>,
period: IndexerSparklineTimePeriod,
): PerpetualMarket? {
cachedIndexerSparklines[marketId] = payload.mapNotNull { parser.asDouble(it) }.reversed()
return createPerpetualMarket(marketId)
when (period) {
IndexerSparklineTimePeriod.ONEDAY -> {
cachedIndexerSparklines[marketId] = payload.mapNotNull { parser.asDouble(it) }.reversed()
return createPerpetualMarket(marketId)
}
IndexerSparklineTimePeriod.SEVENDAYS -> {
val sevenDaySparklineEntries = 42
cachedIsNew[marketId] = payload.size < sevenDaySparklineEntries
return createPerpetualMarket(marketId)
}
}
}

override fun clearCachedOraclePrice(
Expand Down Expand Up @@ -211,12 +223,17 @@ internal class MarketProcessor(
marketCaps = null,
priceChange24H = parser.asDouble(payload.priceChange24H),
priceChange24HPercent = calculatePriceChange24HPercent(
parser.asDouble(payload.priceChange24H),
oraclePrice,
priceChange24H = parser.asDouble(payload.priceChange24H),
oraclePrice = oraclePrice,
),
status = status,
configs = createConfigs(payload),
perpetual = createMarketPerpetual(payload, oraclePrice, cachedIndexerSparklines[marketId]),
perpetual = createMarketPerpetual(
payload = payload,
oraclePrice = oraclePrice,
line = cachedIndexerSparklines[marketId],
isNew = cachedIsNew[marketId] ?: false,
),
)
return newValue
} catch (e: IndexerResponseParsingException) {
Expand Down Expand Up @@ -311,6 +328,7 @@ internal class MarketProcessor(
payload: IndexerCompositeMarketObject,
oraclePrice: Double?,
line: List<Double>?,
isNew: Boolean,
): MarketPerpetual? {
val nextFundingRate = parser.asDouble(payload.nextFundingRate)
val openInterest = parser.asDouble(payload.openInterest)
Expand All @@ -326,6 +344,7 @@ internal class MarketProcessor(
openInterestLowerCap = parser.asDouble(payload.openInterestLowerCap),
openInterestUpperCap = parser.asDouble(payload.openInterestUpperCap),
line = line?.toIList(),
isNew = isNew,
)
} else {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import exchange.dydx.abacus.state.internalstate.InternalMarketSummaryState
import exchange.dydx.abacus.utils.Logger
import exchange.dydx.abacus.utils.mutable
import exchange.dydx.abacus.utils.safeSet
import indexer.codegen.IndexerSparklineTimePeriod
import indexer.models.IndexerCompositeMarketObject
import indexer.models.IndexerWsMarketUpdateResponse

Expand All @@ -29,7 +30,8 @@ internal interface MarketsProcessorProtocol : BaseProcessorProtocol {

fun processSparklines(
existing: InternalMarketSummaryState,
content: Map<String, List<String>>?
content: Map<String, List<String>>?,
period: IndexerSparklineTimePeriod,
): InternalMarketSummaryState
}

Expand Down Expand Up @@ -110,13 +112,15 @@ internal class MarketsProcessor(

override fun processSparklines(
existing: InternalMarketSummaryState,
content: Map<String, List<String>>?
content: Map<String, List<String>>?,
period: IndexerSparklineTimePeriod,
): InternalMarketSummaryState {
for ((marketId, sparklines) in content ?: mapOf()) {
val marketState = existing.markets[marketId] ?: InternalMarketState()
val receivedMarket = marketProcessor.processSparklines(
marketId = marketId,
payload = sparklines,
period = period,
)
if (receivedMarket != marketState.perpetualMarket) {
marketState.perpetualMarket = receivedMarket
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import exchange.dydx.abacus.utils.safeSet
import indexer.codegen.IndexerCandleResponse
import indexer.codegen.IndexerCandleResponseObject
import indexer.codegen.IndexerOrderbookResponseObject
import indexer.codegen.IndexerSparklineTimePeriod
import indexer.codegen.IndexerTradeResponse
import indexer.models.IndexerCompositeMarketObject
import indexer.models.IndexerWsMarketUpdateResponse
Expand Down Expand Up @@ -60,8 +61,9 @@ internal class MarketsSummaryProcessor(
fun processSparklines(
existing: InternalMarketSummaryState,
content: Map<String, List<String>>?,
period: IndexerSparklineTimePeriod,
): InternalMarketSummaryState {
marketsProcessor.processSparklines(existing, content)
marketsProcessor.processSparklines(existing, content, period)
return existing
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@ package exchange.dydx.abacus.state.model
import exchange.dydx.abacus.protocols.asTypedStringMapOfList
import exchange.dydx.abacus.state.changes.Changes
import exchange.dydx.abacus.state.changes.StateChanges
import indexer.codegen.IndexerSparklineTimePeriod
import kollections.iListOf

internal fun TradingStateMachine.sparklines(payload: String): StateChanges? {
internal fun TradingStateMachine.sparklines(
payload: String,
period: IndexerSparklineTimePeriod
): StateChanges? {
val json = parser.decodeJsonObject(payload) as? Map<String, List<String>>
if (staticTyping) {
val sparklines = parser.asTypedStringMapOfList<String>(json)
return if (sparklines != null) {
marketsProcessor.processSparklines(internalState.marketsSummary, sparklines)
StateChanges(iListOf(Changes.sparklines, Changes.markets), null)
marketsProcessor.processSparklines(internalState.marketsSummary, sparklines, period)
when (period) {
IndexerSparklineTimePeriod.ONEDAY -> {
StateChanges(iListOf(Changes.sparklines, Changes.markets), null)
}
IndexerSparklineTimePeriod.SEVENDAYS -> {
StateChanges(iListOf(Changes.markets), null)
}
}
} else {
StateChanges.noChange
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import exchange.dydx.abacus.utils.mutableMapOf
import exchange.dydx.abacus.utils.safeSet
import exchange.dydx.abacus.utils.typedSafeSet
import exchange.dydx.abacus.validator.InputValidator
import indexer.codegen.IndexerSparklineTimePeriod
import indexer.models.configs.ConfigsMarketAsset
import kollections.JsExport
import kollections.iListOf
Expand Down Expand Up @@ -536,7 +537,7 @@ open class TradingStateMachine(
}

"/v4/sparklines" -> {
changes = sparklines(payload)
changes = sparklines(payload, IndexerSparklineTimePeriod.ONEDAY)
}

"/v4/fills" -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ internal class StateManagerAdaptorV2(
)

private val markets = MarketsSupervisor(
stateMachine,
networkHelper,
analyticsUtils,
appConfigs.marketConfigs,
stateMachine = stateMachine,
helper = networkHelper,
analyticsUtils = analyticsUtils,
configs = appConfigs.marketConfigs,
)

private val triggerOrderToastGenerator = TriggerOrderToastGenerator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data class MarketsConfigs(
val subscribeToOrderbook: Boolean,
val subscribeToTrades: Boolean,
val subscribeToCandles: Boolean,
val retrieveSevenDaySparkline: Boolean,
) {
companion object {
val forApp = MarketsConfigs(
Expand All @@ -53,6 +54,7 @@ data class MarketsConfigs(
subscribeToOrderbook = true,
subscribeToTrades = true,
subscribeToCandles = true,
retrieveSevenDaySparkline = true,
)
val forWeb = MarketsConfigs(
retrieveSparklines = true,
Expand All @@ -62,6 +64,7 @@ data class MarketsConfigs(
subscribeToOrderbook = true,
subscribeToTrades = true,
subscribeToCandles = false,
retrieveSevenDaySparkline = false,
)
val forProgrammaticTraders = MarketsConfigs(
retrieveSparklines = false,
Expand All @@ -71,6 +74,7 @@ data class MarketsConfigs(
subscribeToOrderbook = true,
subscribeToTrades = false,
subscribeToCandles = false,
retrieveSevenDaySparkline = false,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import exchange.dydx.abacus.utils.CoroutineTimer
import exchange.dydx.abacus.utils.IList
import exchange.dydx.abacus.utils.IMap
import exchange.dydx.abacus.utils.iMapOf
import indexer.codegen.IndexerSparklineTimePeriod

internal class MarketsSupervisor(
stateMachine: TradingStateMachine,
Expand All @@ -27,7 +28,7 @@ internal class MarketsSupervisor(
internal val markets = mutableMapOf<String, MarketSupervisor>()

private var sparklinesTimer: LocalTimerProtocol? = null
private val sparklinesPollingDuration = 60.0
private val sparklinesPollingDuration = 60.0 * 60.0 // 1 hour

internal var candlesResolution: String = "1DAY"
internal set(value) {
Expand Down Expand Up @@ -148,21 +149,29 @@ internal class MarketsSupervisor(
private fun getSparklines() {
val url = helper.configs.publicApiUrl("sparklines")
if (url != null) {
helper.get(url, sparklinesParams(), null) { _, response, httpCode, _ ->
// Get 1 day sparkline for market display
val period = IndexerSparklineTimePeriod.ONEDAY
helper.get(url, iMapOf("timePeriod" to period.value), null) { _, response, httpCode, _ ->
if (helper.success(httpCode) && response != null) {
parseSparklinesResponse(response)
parseSparklinesResponse(response, period)
}
}

if (configs.retrieveSevenDaySparkline) {
// Get 7 day sparkline to determine if market is new
val period = IndexerSparklineTimePeriod.SEVENDAYS
helper.get(url, iMapOf("timePeriod" to period.value), null) { _, response, httpCode, _ ->
if (helper.success(httpCode) && response != null) {
parseSparklinesResponse(response, period)
}
}
}
}
}

private fun parseSparklinesResponse(response: String) {
private fun parseSparklinesResponse(response: String, period: IndexerSparklineTimePeriod) {
val oldState = stateMachine.state
update(stateMachine.sparklines(response), oldState)
}

private fun sparklinesParams(): IMap<String, String> {
return iMapOf("timePeriod" to "ONE_DAY")
update(stateMachine.sparklines(response, period), oldState)
}

internal fun receiveMarketsChannelSocketData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ class V4ForegroundCycleTests : NetworkTests() {
val localizer = BaseTests.testLocalizer(ioImplementations)
val uiImplementations = BaseTests.testUIImplementations(localizer)
stateManager = AsyncAbacusStateManagerV2(
"https://api.examples.com",
"DEV",
if (forIsolatedMargins) {
deploymentUri = "https://api.examples.com",
deployment = "DEV",
appConfigs = if (forIsolatedMargins) {
AppConfigsV2.forAppWithIsolatedMargins
} else {
AppConfigsV2.forApp
},
ioImplementations,
uiImplementations,
TestState(),
null,
ioImplementations = ioImplementations,
uiImplementations = uiImplementations,
stateNotification = TestState(),
dataNotification = null,
)
stateManager.environmentId = "dydxprotocol-staging"
return stateManager
Expand Down Expand Up @@ -205,6 +205,7 @@ class V4ForegroundCycleTests : NetworkTests() {
"https://api.examples.com/configs/exchanges.json",
"https://api.dydx.exchange/v4/geo",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=ONE_DAY",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=SEVEN_DAYS",
"https://indexer.v4staging.dydx.exchange/v4/candles/perpetualMarkets/ETH-USD?resolution=1DAY",
"https://indexer.v4staging.dydx.exchange/v4/historicalFunding/ETH-USD"
]
Expand Down Expand Up @@ -264,7 +265,6 @@ class V4ForegroundCycleTests : NetworkTests() {
// """.trimIndent(),
"""
[
"https://api.examples.com/configs/documentation.json",
"https://indexer.v4staging.dydx.exchange/v4/time",
"https://indexer.v4staging.dydx.exchange/v4/height",
Expand All @@ -275,6 +275,7 @@ class V4ForegroundCycleTests : NetworkTests() {
"https://api.examples.com/configs/exchanges.json",
"https://api.dydx.exchange/v4/geo",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=ONE_DAY",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=SEVEN_DAYS",
"https://indexer.v4staging.dydx.exchange/v4/candles/perpetualMarkets/ETH-USD?resolution=1DAY",
"https://indexer.v4staging.dydx.exchange/v4/historicalFunding/ETH-USD",
"https://indexer.v4staging.dydx.exchange/v4/candles/perpetualMarkets/BTC-USD?resolution=1DAY",
Expand Down Expand Up @@ -330,6 +331,7 @@ class V4ForegroundCycleTests : NetworkTests() {
"https://api.examples.com/configs/exchanges.json",
"https://api.dydx.exchange/v4/geo",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=ONE_DAY",
"https://indexer.v4staging.dydx.exchange/v4/sparklines?timePeriod=SEVEN_DAYS",
"https://indexer.v4staging.dydx.exchange/v4/candles/perpetualMarkets/ETH-USD?resolution=1DAY",
"https://indexer.v4staging.dydx.exchange/v4/historicalFunding/ETH-USD"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ internal typealias VerificationFunction = (response: StateResponse) -> Unit
open class BaseTests(
private val maxSubaccountNumber: Int,
private val useParentSubaccount: Boolean,
private val staticTyping: Boolean = false, // turn on static typing for testing
private val staticTyping: Boolean = true, // turn on static typing for testing
) {
open val doAsserts = true
internal val deploymentUri = "https://api.examples.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import exchange.dydx.abacus.output.PerpetualMarketType
import exchange.dydx.abacus.utils.Parser
import indexer.codegen.IndexerPerpetualMarketStatus
import indexer.codegen.IndexerPerpetualMarketType
import indexer.codegen.IndexerSparklineTimePeriod
import indexer.models.IndexerCompositeMarketObject
import indexer.models.IndexerWsMarketOraclePriceObject
import kollections.toIList
Expand Down Expand Up @@ -149,7 +150,25 @@ class MarketProcessorTests {
@Test
fun testProcessSparklines() {
processor.process("BTC-USD", marketPayloadMock)
val output = processor.processSparklines("BTC-USD", listOf("1", "2", "3"))
val output = processor.processSparklines(
marketId = "BTC-USD",
payload = listOf("1", "2", "3"),
period = IndexerSparklineTimePeriod.ONEDAY,
)
assertEquals(output?.perpetual?.line, listOf(3.0, 2.0, 1.0).toIList())

val output2 = processor.processSparklines(
marketId = "BTC-USD",
payload = listOf("1", "2", "3"),
period = IndexerSparklineTimePeriod.SEVENDAYS,
)
assertEquals(output2?.perpetual?.isNew, true)

val output3 = processor.processSparklines(
marketId = "BTC-USD",
payload = MutableList(42) { "1" }, // 42 elements
period = IndexerSparklineTimePeriod.SEVENDAYS,
)
assertEquals(output3?.perpetual?.isNew, false)
}
}
Loading

0 comments on commit 854a38b

Please sign in to comment.