Skip to content

Commit

Permalink
Move test-only functions off TradingStateMachine (#670)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruixhuang authored Sep 24, 2024
1 parent 599ce82 commit a0a4321
Show file tree
Hide file tree
Showing 22 changed files with 346 additions and 298 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ import exchange.dydx.abacus.protocols.ParserProtocol
import exchange.dydx.abacus.protocols.TrackingProtocol
import exchange.dydx.abacus.protocols.asTypedStringMap
import exchange.dydx.abacus.responses.ParsingError
import exchange.dydx.abacus.responses.ParsingErrorType
import exchange.dydx.abacus.responses.ParsingException
import exchange.dydx.abacus.responses.SocketInfo
import exchange.dydx.abacus.responses.StateResponse
import exchange.dydx.abacus.state.app.adaptors.AbUrl
import exchange.dydx.abacus.state.app.helper.Formatter
import exchange.dydx.abacus.state.changes.Changes
import exchange.dydx.abacus.state.changes.StateChanges
Expand All @@ -77,7 +74,6 @@ 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 All @@ -86,9 +82,6 @@ import kollections.iMutableMapOf
import kollections.toIList
import kollections.toIMap
import kollections.toIMutableMap
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlin.math.max
import kotlin.math.min
import kotlin.time.Duration.Companion.days
Expand Down Expand Up @@ -283,297 +276,6 @@ open class TradingStateMachine(
return StateResponse(state, null)
}

fun socket(
url: AbUrl,
jsonString: String,
subaccountNumber: Int,
height: BlockAndTime?,
): StateResponse {
val errors = iMutableListOf<ParsingError>()
val json =
try {
Json.parseToJsonElement(jsonString).jsonObject.toMap()
} catch (e: SerializationException) {
errors.add(
ParsingError(
ParsingErrorType.ParsingError,
"$jsonString is not a valid JSON object",
e.stackTraceToString(),
),
)
null
}
if (json == null || errors.isNotEmpty()) {
return StateResponse(state, null, errors)
}
return socket(url, json, subaccountNumber, height)
}

@Throws(Exception::class)
private fun socket(
url: AbUrl,
payload: Map<String, Any>,
subaccountNumber: Int,
height: BlockAndTime?,
): StateResponse {
var changes: StateChanges? = null
val type = parser.asString(payload["type"])
val channel = parser.asString(payload["channel"])
val id = parser.asString(payload["id"])
val childSubaccountNumber = parser.asInt(payload["subaccountNumber"])
val info = SocketInfo(type, channel, id, childSubaccountNumber)
try {
when (type) {
"subscribed" -> {
val content = parser.asNativeMap(payload["contents"])
?: throw ParsingException(
ParsingErrorType.MissingContent,
payload.toString(),
)
when (channel) {
"v3_markets", "v4_markets" -> {
changes = receivedMarkets(content, subaccountNumber)
}

"v4_subaccounts", "v4_parent_subaccounts" -> {
changes = receivedSubaccountSubscribed(content, height)
}

"v3_orderbook", "v4_orderbook" -> {
val market = parser.asString(payload["id"])
changes = receivedOrderbook(market, content, subaccountNumber)
}

"v3_trades", "v4_trades" -> {
val market = parser.asString(payload["id"])
changes = receivedTrades(market, content)
}

"v4_candles" -> {
val channel = parser.asString(payload["id"])
val (market, resolution) = splitCandlesChannel(channel)
changes = receivedCandles(market, resolution, content)
}

else -> {
throw ParsingException(
ParsingErrorType.UnknownChannel,
"$channel subscribed is not known",
)
}
}
}

"unsubscribed" -> {}

"channel_data" -> {
val content = parser.asNativeMap(payload["contents"])
?: throw ParsingException(
ParsingErrorType.MissingContent,
payload.toString(),
)
when (channel) {
"v3_markets", "v4_markets" -> {
changes = receivedMarketsChanges(content, subaccountNumber)
}

"v4_subaccounts", "v4_parent_subaccounts" -> {
changes = receivedSubaccountsChanges(content, info, height)
}

"v3_orderbook", "v4_orderbook" -> {
throw ParsingException(
ParsingErrorType.UnhandledEndpoint,
"channel_data for $channel is not implemented",
)
// change = receivedOrderbookChanges(market, it)
}

"v3_trades", "v4_trades" -> {
val market = parser.asString(payload["id"])
changes = receivedTradesChanges(market, content)
}

"v4_candles" -> {
val channel = parser.asString(payload["id"])
val (market, resolution) = splitCandlesChannel(channel)
changes = receivedCandlesChanges(market, resolution, content)
}

else -> {
throw ParsingException(
ParsingErrorType.UnknownChannel,
"$channel channel data is not known",
)
}
}
}

"channel_batch_data" -> {
val content = parser.asList(payload["contents"])
?: throw ParsingException(
ParsingErrorType.MissingContent,
payload.toString(),
)
when (channel) {
"v3_markets", "v4_markets" -> {
changes = receivedBatchedMarketsChanges(content, subaccountNumber)
}

"v3_trades", "v4_trades" -> {
val market = parser.asString(payload["id"])
changes = receivedBatchedTradesChanges(market, content)
}

"v4_candles" -> {
val channel = parser.asString(payload["id"])
val (market, resolution) = splitCandlesChannel(channel)
changes = receivedBatchedCandlesChanges(market, resolution, content)
}

"v3_orderbook", "v4_orderbook" -> {
val market = parser.asString(payload["id"])
changes = receivedBatchOrderbookChanges(
market,
content,
subaccountNumber,
)
}

"v4_subaccounts", "v4_parent_subaccounts" -> {
changes = receivedBatchSubaccountsChanges(content, info, height)
}

else -> {
throw ParsingException(
ParsingErrorType.UnknownChannel,
"$channel channel batch data is not known",
)
}
}
}

"connected" -> {}

"error" -> {
throw ParsingException(ParsingErrorType.BackendError, payload.toString())
}

else -> {
throw ParsingException(
ParsingErrorType.Unhandled,
"Type [ $type # $channel ] is not handled",
)
}
}
var realChanges = changes
changes?.let {
realChanges = updateStateChanges(it)
}
return StateResponse(state, realChanges, null, info)
} catch (e: ParsingException) {
return StateResponse(state, null, iListOf(e.toParsingError()), info)
}
}

private fun splitCandlesChannel(channel: String?): Pair<String, String> {
if (channel == null) {
throw ParsingException(
ParsingErrorType.UnknownChannel,
"$channel is not known",
)
}
val marketAndResolution = channel.split("/")
if (marketAndResolution.size != 2) {
throw ParsingException(
ParsingErrorType.UnknownChannel,
"$channel is not known",
)
}
val market = marketAndResolution[0]
val resolution = marketAndResolution[1]
return Pair(market, resolution)
}

/**
* function specifically for testing spoofed rest response processing
*/
fun rest(
url: AbUrl,
payload: String,
subaccountNumber: Int,
height: Int?,
deploymentUri: String? = null,
period: String? = null,
): StateResponse {
/*
For backward compatibility only
*/
var changes: StateChanges? = null
var error: ParsingError? = null
when (url.path) {
"/v3/historical-pnl", "/v4/historical-pnl" -> {
val subaccountNumber =
parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value)
?: 0
changes = historicalPnl(payload, subaccountNumber)
}

"/v3/candles" -> {
changes = candles(payload)
}

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

"/v4/fills" -> {
val subaccountNumber =
parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value)
?: 0
changes = fills(payload, subaccountNumber)
}

"/v4/transfers" -> {
val subaccountNumber =
parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value)
?: 0
changes = transfers(payload, subaccountNumber)
}

"/configs/markets.json" -> {
if (deploymentUri != null) {
changes = configurations(
payload = payload,
subaccountNumber = subaccountNumber,
deploymentUri = deploymentUri,
)
}
}

else -> {
if (url.path.contains("/v3/historical-funding/") || url.path.contains("/v4/historicalFunding/")) {
changes = historicalFundings(payload)
} else if (url.path.contains("/v3/candles/") || url.path.contains("/v4/candles/")) {
changes = candles(payload)
} else if (url.path.contains("/v4/addresses/")) {
changes = account(payload)
} else {
error = ParsingError(
ParsingErrorType.UnhandledEndpoint,
"${url.path} parsing has not be implemented, or is an invalid endpoint",
)
}
}
}
if (changes != null) {
updateStateChanges(changes)
}

val errors = if (error != null) iListOf(error) else null
return StateResponse(state, changes, errors)
}

internal fun resetWallet(accountAddress: String?): StateResponse {
val wallet = if (accountAddress != null) iMapOf("walletAddress" to accountAddress) else null
this.wallet = wallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import exchange.dydx.abacus.state.app.adaptors.AbUrl
import exchange.dydx.abacus.state.model.AdjustIsolatedMarginInputField
import exchange.dydx.abacus.state.model.adjustIsolatedMargin
import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers
import exchange.dydx.abacus.tests.extensions.rest
import exchange.dydx.abacus.tests.extensions.socket
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import exchange.dydx.abacus.state.model.closePosition
import exchange.dydx.abacus.state.model.trade
import exchange.dydx.abacus.state.model.tradeInMarket
import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers
import exchange.dydx.abacus.tests.extensions.socket
import kotlin.test.BeforeTest
import kotlin.test.DefaultAsserter.assertTrue
import kotlin.test.Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import exchange.dydx.abacus.responses.StateResponse
import exchange.dydx.abacus.state.app.adaptors.AbUrl
import exchange.dydx.abacus.state.model.TriggerOrdersInputField
import exchange.dydx.abacus.state.model.triggerOrders
import exchange.dydx.abacus.tests.extensions.rest
import exchange.dydx.abacus.utils.Rounder
import kotlin.math.abs
import kotlin.test.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package exchange.dydx.abacus.payload.v3

import exchange.dydx.abacus.tests.extensions.socket
import exchange.dydx.abacus.utils.Logger
import kotlin.test.Test
import kotlin.test.assertNotNull
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package exchange.dydx.abacus.payload.v3

import exchange.dydx.abacus.state.model.setOrderbookGrouping
import exchange.dydx.abacus.tests.extensions.socket
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package exchange.dydx.abacus.payload.v4
import exchange.dydx.abacus.calculator.CalculationPeriod
import exchange.dydx.abacus.tests.extensions.loadv4SubaccountsWithPositions
import exchange.dydx.abacus.tests.extensions.log
import exchange.dydx.abacus.tests.extensions.socket
import exchange.dydx.abacus.utils.SHORT_TERM_ORDER_DURATION
import exchange.dydx.abacus.utils.ServerTime
import kotlin.test.Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import exchange.dydx.abacus.tests.extensions.loadv4SubaccountWithOrdersAndFillsC
import exchange.dydx.abacus.tests.extensions.loadv4SubaccountsWithPositions
import exchange.dydx.abacus.tests.extensions.log
import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers
import exchange.dydx.abacus.tests.extensions.rest
import exchange.dydx.abacus.tests.extensions.socket
import exchange.dydx.abacus.utils.JsonEncoder
import exchange.dydx.abacus.utils.Parser
import exchange.dydx.abacus.utils.ServerTime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package exchange.dydx.abacus.payload.v4
import exchange.dydx.abacus.calculator.CalculationPeriod
import exchange.dydx.abacus.responses.StateResponse
import exchange.dydx.abacus.state.app.adaptors.AbUrl
import exchange.dydx.abacus.tests.extensions.rest
import exchange.dydx.abacus.tests.extensions.socket
import kotlin.test.Test
import kotlin.test.assertEquals

Expand Down
Loading

0 comments on commit a0a4321

Please sign in to comment.