diff --git a/build.gradle.kts b/build.gradle.kts index 6a94e593a..53546a38f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,7 @@ allprojects { } group = "exchange.dydx.abacus" -version = "1.6.40" +version = "1.6.41" repositories { google() diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/manager/V4StateManagerAdaptor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/manager/V4StateManagerAdaptor.kt index 835a843b5..4007e8014 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/manager/V4StateManagerAdaptor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/manager/V4StateManagerAdaptor.kt @@ -1162,37 +1162,53 @@ class V4StateManagerAdaptor( override fun commitPlaceOrder(callback: TransactionCallback): HumanReadablePlaceOrderPayload { val payload = placeOrderPayload() - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload(payload) + val midMarketPrice = stateMachine.state?.marketOrderbook(payload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload(payload, midMarketPrice) return submitPlaceOrder(callback, payload, analyticsPayload) } override fun commitClosePosition(callback: TransactionCallback): HumanReadablePlaceOrderPayload { val payload = closePositionPayload() - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload(payload, true) + val midMarketPrice = stateMachine.state?.marketOrderbook(payload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload(payload, midMarketPrice, true) return submitPlaceOrder(callback, payload, analyticsPayload) } override fun cancelOrder(orderId: String, callback: TransactionCallback) { val payload = cancelOrderPayload(orderId) - val analyticsPayload = analyticsUtils.formatCancelOrderPayload(payload) + val subaccount = stateMachine.state?.subaccount(subaccountNumber) + val existingOrder = subaccount?.orders?.firstOrNull { it.id == orderId } + val analyticsPayload = analyticsUtils.cancelOrderAnalyticsPayload( + payload, + existingOrder, + ) + submitCancelOrder(orderId, callback, payload, analyticsPayload) } override fun commitTriggerOrders(callback: TransactionCallback): HumanReadableTriggerOrdersPayload { val payloads = triggerOrdersPayload() - payloads.cancelOrderPayloads.forEach { - val analyticsPayload = analyticsUtils.formatCancelOrderPayload(it, true) - submitCancelOrder(it.orderId, callback, it, analyticsPayload, true) + payloads.cancelOrderPayloads.forEach { payload -> + val subaccount = stateMachine.state?.subaccount(subaccountNumber) + val existingOrder = subaccount?.orders?.firstOrNull { it.id == payload.orderId } + val analyticsPayload = analyticsUtils.cancelOrderAnalyticsPayload( + payload, + existingOrder, + true, + ) + submitCancelOrder(payload.orderId, callback, payload, analyticsPayload, true) } - payloads.placeOrderPayloads.forEach { - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload( - it, + payloads.placeOrderPayloads.forEach { payload -> + val midMarketPrice = stateMachine.state?.marketOrderbook(payload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload( + payload, + midMarketPrice, isClosePosition = false, fromSlTpDialog = true, ) - submitPlaceOrder(callback, it, analyticsPayload, true) + submitPlaceOrder(callback, payload, analyticsPayload, true) } if (payloads.cancelOrderPayloads.isEmpty() && payloads.placeOrderPayloads.isEmpty()) { diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/SubaccountSupervisor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/SubaccountSupervisor.kt index d9ae30d8e..668e6a2e0 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/SubaccountSupervisor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/v2/supervisor/SubaccountSupervisor.kt @@ -737,7 +737,8 @@ internal class SubaccountSupervisor( callback: TransactionCallback ): HumanReadablePlaceOrderPayload { val orderPayload = placeOrderPayload(currentHeight) - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload(orderPayload, false) + val midMarketPrice = stateMachine.state?.marketOrderbook(orderPayload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload(orderPayload, midMarketPrice, false) val isIsolatedMarginOrder = helper.parser.asInt(orderPayload.subaccountNumber) != subaccountNumber val transferPayload = @@ -751,14 +752,17 @@ internal class SubaccountSupervisor( callback: TransactionCallback ): HumanReadablePlaceOrderPayload { val payload = closePositionPayload(currentHeight) - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload(payload, true) + val midMarketPrice = stateMachine.state?.marketOrderbook(payload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload(payload, midMarketPrice, true) return submitPlaceOrder(callback, payload, analyticsPayload) } internal fun cancelOrder(orderId: String, callback: TransactionCallback): HumanReadableCancelOrderPayload { val payload = cancelOrderPayload(orderId) - val analyticsPayload = analyticsUtils.formatCancelOrderPayload(payload) + val subaccount = stateMachine.state?.subaccount(subaccountNumber) + val existingOrder = subaccount?.orders?.firstOrNull { it.id == orderId } + val analyticsPayload = analyticsUtils.cancelOrderAnalyticsPayload(payload, existingOrder) return submitCancelOrder(orderId, callback, payload, analyticsPayload) } @@ -770,13 +774,17 @@ internal class SubaccountSupervisor( val payloads = triggerOrdersPayload(currentHeight) payloads.cancelOrderPayloads.forEach { payload -> - val analyticsPayload = analyticsUtils.formatCancelOrderPayload(payload, true) + val subaccount = stateMachine.state?.subaccount(subaccountNumber) + val existingOrder = subaccount?.orders?.firstOrNull { it.id == payload.orderId } + val analyticsPayload = analyticsUtils.cancelOrderAnalyticsPayload(payload, existingOrder, true) submitCancelOrder(payload.orderId, callback, payload, analyticsPayload, true) } payloads.placeOrderPayloads.forEach { payload -> - val analyticsPayload = analyticsUtils.formatPlaceOrderPayload( + val midMarketPrice = stateMachine.state?.marketOrderbook(payload.marketId)?.midPrice + val analyticsPayload = analyticsUtils.placeOrderAnalyticsPayload( payload, + midMarketPrice, isClosePosition = false, fromSlTpDialog = true, ) diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/utils/AnalyticsUtils.kt b/src/commonMain/kotlin/exchange.dydx.abacus/utils/AnalyticsUtils.kt index 433f5c797..0e7227a1a 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/utils/AnalyticsUtils.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/utils/AnalyticsUtils.kt @@ -3,14 +3,37 @@ package exchange.dydx.abacus.utils import exchange.dydx.abacus.output.SubaccountOrder import exchange.dydx.abacus.state.manager.HumanReadableCancelOrderPayload import exchange.dydx.abacus.state.manager.HumanReadablePlaceOrderPayload +import kollections.toIMap class AnalyticsUtils { + /** + * Format Place Order Payload and add additional details for `TradePlaceOrder` Analytic Events + * @param payload HumanReadablePlaceOrderPayload + * @param midMarketPrice Double? + * @param isClosePosition Boolean? + * @param fromSlTpDialog Boolean? + */ + fun placeOrderAnalyticsPayload( + payload: HumanReadablePlaceOrderPayload, + midMarketPrice: Double?, + isClosePosition: Boolean? = false, + fromSlTpDialog: Boolean? = false, + ): IMap? { + return ParsingHelper.merge( + formatPlaceOrderPayload(payload, isClosePosition, fromSlTpDialog), + iMapOf( + "inferredTimeInForce" to calculateOrderTimeInForce(payload), + "midMarketPrice" to midMarketPrice, + ) as IMap?, + )?.toIMap() + } + /** * Format Place Order Payload for `TradePlaceOrder` Analytic Event * @param payload HumanReadablePlaceOrderPayload * @param isClosePosition Boolean */ - fun formatPlaceOrderPayload( + private fun formatPlaceOrderPayload( payload: HumanReadablePlaceOrderPayload, isClosePosition: Boolean? = false, fromSlTpDialog: Boolean? = false, @@ -37,11 +60,54 @@ class AnalyticsUtils { } /** - * Format Cancel Order Payload for `TradeCancelOrder` Analytic Event + * Infer time in force from order params for analytics, mirroring v4-clients + * @param payload HumanReadablePlaceOrderPayload + */ + private fun calculateOrderTimeInForce( + payload: HumanReadablePlaceOrderPayload + ): String? { + return when (payload.type) { + "MARKET" -> payload.timeInForce ?: "FOK" + "LIMIT" -> { + when (payload.timeInForce) { + "GTT" -> if (payload.postOnly == true) "POST_ONLY" else "GTT" + else -> payload.timeInForce + } + } + + "STOP_LIMIT", "TAKE_PROFIT" -> { + when (payload.execution) { + "DEFAULT" -> "GTT" + else -> payload.execution + } + } + + "STOP_MARKET", "TAKE_PROFIT_MARKET" -> payload.execution + else -> payload.timeInForce ?: payload.execution + } + } + + /** + * Format Cancel Order Payload and add order details for `TradeCancelOrder` Analytic Events * @param payload HumanReadableCancelOrderPayload + * @param existingOrder SubaccountOrder? * @param fromSlTpDialog Boolean */ - fun formatCancelOrderPayload(payload: HumanReadableCancelOrderPayload, fromSlTpDialog: Boolean? = false,): IMap? { + fun cancelOrderAnalyticsPayload( + payload: HumanReadableCancelOrderPayload, + existingOrder: SubaccountOrder?, + fromSlTpDialog: Boolean? = false, + ): IMap? { + return ParsingHelper.merge( + formatCancelOrderPayload(payload, fromSlTpDialog), + if (existingOrder != null) formatOrder(existingOrder) else mapOf(), + )?.toIMap() + } + + private fun formatCancelOrderPayload( + payload: HumanReadableCancelOrderPayload, + fromSlTpDialog: Boolean? = false, + ): IMap? { return iMapOf( "fromSlTpDialog" to fromSlTpDialog, "subaccountNumber" to payload.subaccountNumber, diff --git a/v4_abacus.podspec b/v4_abacus.podspec index 9d763aa39..275771f2d 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.6.40' + spec.version = '1.6.41' spec.homepage = 'https://github.com/dydxprotocol/v4-abacus' spec.source = { :http=> ''} spec.authors = ''