Skip to content

Commit

Permalink
Close isolated position (#377)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredvu authored May 24, 2024
1 parent ab4a7ce commit 78ecf70
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("ktlint:standard:property-naming")

package exchange.dydx.abacus.calculator

import abs
Expand Down Expand Up @@ -54,12 +56,13 @@ internal class TradeInputCalculator(
): Map<String, Any> {
val account = parser.asNativeMap(state["account"])
val subaccount = if (subaccountNumber != null) {
parser.asNativeMap(
parser.value(
account,
"subaccounts.$subaccountNumber",
),
)
parser.asMap(parser.value(account, "groupedSubaccounts.$subaccountNumber"))
?: parser.asNativeMap(
parser.value(
account,
"subaccounts.$subaccountNumber",
),
)
} else {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ fun TradingStateMachine.closePosition(
val input = this.input?.mutable() ?: mutableMapOf()
input["current"] = "closePosition"
val trade =
parser.asMap(input["closePosition"])?.mutable() ?: inititiateClosePosition(
parser.asMap(input["closePosition"])?.mutable() ?: initiateClosePosition(
null,
subaccountNumber,
)

var sizeChanged = false
when (typeText) {
ClosePositionInputField.market.rawValue -> {
val position = if (data != null) getPosition(data) else null
val position = if (data != null) getPosition(data, subaccountNumber) else null
if (position != null) {
if (data != null) {
if (parser.asString(trade["marketId"]) != data) {
Expand Down Expand Up @@ -100,14 +100,21 @@ fun TradingStateMachine.closePosition(

fun TradingStateMachine.getPosition(
marketId: String,
subaccountNumber: Int = 0
subaccountNumber: Int,
): Map<String, Any>? {
val groupedSubaccounts = parser.asMap(parser.value(wallet, "account.groupedSubaccounts"))
val path = if (groupedSubaccounts != null) {
"account.groupedSubaccounts.$subaccountNumber.openPositions.$marketId"
} else {
"account.subaccounts.$subaccountNumber.openPositions.$marketId"
}
val position = parser.asMap(
parser.value(
wallet,
"account.subaccounts.$subaccountNumber.openPositions.$marketId",
path,
),
)

return if (position != null && (
parser.asDouble(parser.value(position, "size.current"))
?: Numeric.double.ZERO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ internal fun TradingStateMachine.initiateTrade(
return parser.asMap(modified["trade"])?.mutable() ?: trade
}

internal fun TradingStateMachine.inititiateClosePosition(
internal fun TradingStateMachine.initiateClosePosition(
marketId: String?,
subaccountNumber: Int,
): MutableMap<String, Any> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ open class TradingStateMachine(
for (subaccountNumber in subaccountNumbers) {
val subaccountText = "$subaccountNumber"
val subaccount =
parser.asNativeMap(parser.value(this.account, "subaccounts.$subaccountNumber"))
parser.asNativeMap(parser.value(this.account, "groupedSubaccounts.$subaccountNumber")) ?: parser.asNativeMap(parser.value(this.account, "subaccounts.$subaccountNumber"))

if (changes.changes.contains(Changes.historicalPnl)) {
val now = ServerTime.now()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,10 @@ internal class SubaccountSupervisor(
val goodTilTimeInSeconds = null
val goodTilBlock = currentHeight?.plus(SHORT_TERM_ORDER_DURATION)
val marketInfo = marketInfo(marketId)
val subaccountNumberForPosition = helper.parser.asInt(helper.parser.value(stateMachine.data, "wallet.account.groupedSubaccounts.$subaccountNumber.openPositions.$marketId.childSubaccountNumber")) ?: subaccountNumber

return HumanReadablePlaceOrderPayload(
subaccountNumber,
subaccountNumberForPosition,
marketId,
clientId,
"MARKET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import exchange.dydx.abacus.protocols.TransactionCallback
import exchange.dydx.abacus.state.manager.HumanReadablePlaceOrderPayload
import exchange.dydx.abacus.state.manager.HumanReadableTriggerOrdersPayload
import exchange.dydx.abacus.state.manager.setAddresses
import exchange.dydx.abacus.state.model.ClosePositionInputField
import exchange.dydx.abacus.state.model.TradeInputField
import exchange.dydx.abacus.state.model.TriggerOrdersInputField
import exchange.dydx.abacus.state.v2.manager.AsyncAbacusStateManagerV2
Expand Down Expand Up @@ -344,19 +345,26 @@ class V4TransactionTests : NetworkTests() {
assertTransactionQueueEmpty()
}

private fun setStateMachineForIsolatedMarginTests(stateManager: AsyncAbacusStateManagerV2) {
private fun setStateMachineForIsolatedMarginTests(stateManager: AsyncAbacusStateManagerV2, withPositions: Boolean = false) {
stateManager.readyToConnect = true
testWebSocket?.simulateConnected(true)
testWebSocket?.simulateReceived(mock.connectionMock.connectedMessage)
testWebSocket?.simulateReceived(mock.marketsChannel.v4_subscribed_r1)

stateManager.setAddresses(null, "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5")
testWebSocket?.simulateReceived(mock.v4ParentSubaccountsMock.subscribed)
testWebSocket?.simulateReceived(mock.v4ParentSubaccountsMock.channel_batch_data)

if (withPositions) {
testWebSocket?.simulateReceived(mock.v4ParentSubaccountsMock.subscribed_with_positions)
} else {
testWebSocket?.simulateReceived(mock.v4ParentSubaccountsMock.subscribed)
}
stateManager.market = "BTC-USD"
}

private fun prepareIsolatedMarginClosePosition() {
stateManager.closePosition("APE-USD", ClosePositionInputField.market)
stateManager.closePosition("1", ClosePositionInputField.percent)
}

private fun prepareIsolatedMarginTrade(isShortTerm: Boolean) {
stateManager.trade("2000", TradeInputField.limitPrice)
stateManager.trade("0.01", TradeInputField.size)
Expand All @@ -371,6 +379,15 @@ class V4TransactionTests : NetworkTests() {
}
}

@Test
fun testIsolatedMarginClosePosition() {
setStateMachineForIsolatedMarginTests(stateManager, withPositions = true)
prepareIsolatedMarginClosePosition()
val closePositionPayload = subaccountSupervisor?.closePositionPayload(0)
assertNotNull(closePositionPayload, "Close position payload should not be null")
assertEquals(128, closePositionPayload.subaccountNumber)
}

@Test
fun testIsolatedMarginPlaceOrderTransactions() {
setStateMachineForIsolatedMarginTests(stateManager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,25 @@ internal class MarketsChannelMock {
"stepBaseQuantums": 1000000,
"subticksPerTick": 1000000
},
"LDO-USD":{
"clobPairId":"26",
"ticker":"LDO-USD",
"status":"ACTIVE",
"oraclePrice":"1.619032989",
"priceChange24H":"-0.049527727",
"volume24H":"1181661.046",
"trades24H":2992,
"nextFundingRate":"0",
"initialMarginFraction":"0.2",
"maintenanceMarginFraction":"0.1",
"openInterest":"83181",
"atomicResolution":-6,
"quantumConversionExponent":-9,
"tickSize":"0.001",
"stepSize":"1",
"stepBaseQuantums":1000000,
"subticksPerTick":1000000
},
"LDO-USD":{
"clobPairId":"26",
"ticker":"LDO-USD",
"status":"ACTIVE",
"oraclePrice":"1.619032989",
"priceChange24H":"-0.049527727",
"volume24H":"1181661.046",
"trades24H":2992,
"nextFundingRate":"0",
"initialMarginFraction":"0.2",
"maintenanceMarginFraction":"0.1",
"openInterest":"83181",
"atomicResolution":-6,
"quantumConversionExponent":-9,
"tickSize":"0.001",
"stepSize":"1",
"stepBaseQuantums":1000000,
"subticksPerTick":1000000
},
"BTC-USD": {
"clobPairId":"0",
"ticker":"BTC-USD",
Expand Down Expand Up @@ -2739,6 +2739,25 @@ internal class MarketsChannelMock {
"channel":"v4_markets",
"contents":{
"markets":{
"APE-USD": {
"clobPairId": "22",
"ticker": "APE-USD",
"status": "ACTIVE",
"oraclePrice": "1.260519794",
"priceChange24H": "-0.013680206",
"volume24H": "519465.935",
"trades24H": 1358,
"nextFundingRate": "-0.00000426818181818182",
"initialMarginFraction": "0.2",
"maintenanceMarginFraction": "0.1",
"openInterest": "142671",
"atomicResolution": -6,
"quantumConversionExponent": -9,
"tickSize": "0.001",
"stepSize": "1",
"stepBaseQuantums": 1000000,
"subticksPerTick": 1000000
},
"BTC-USD":{
"clobPairId":"0",
"ticker":"BTC-USD",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,156 @@ internal class V4ParentSubaccountsMock {
}
""".trimIndent()

internal val subscribed_with_positions = """
{
"type": "subscribed",
"connection_id": "3adc59d6-09cb-432e-b987-ced0da32bec9",
"message_id": 2,
"channel": "v4_parent_subaccounts",
"id": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5/0",
"contents": {
"subaccount": {
"address": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5",
"parentSubaccountNumber": 0,
"equity": "1004.026896843",
"freeCollateral": "963.0237707731",
"childSubaccounts": [
{
"address": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5",
"subaccountNumber": 0,
"equity": "862.675984",
"freeCollateral": "862.675984",
"openPerpetualPositions": {},
"assetPositions": {
"USDC": {
"size": "862.675984",
"symbol": "USDC",
"side": "LONG",
"assetId": "0",
"subaccountNumber": 0
}
},
"marginEnabled": true,
"updatedAtHeight": "12906107"
},
{
"address": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5",
"subaccountNumber": 128,
"equity": "59.810279856",
"freeCollateral": "57.7685502848",
"openPerpetualPositions": {
"APE-USD": {
"market": "APE-USD",
"status": "OPEN",
"side": "LONG",
"size": "8",
"maxSize": "8",
"entryPrice": "1.292",
"exitPrice": null,
"realizedPnl": "0",
"unrealizedPnl": "-0.127352144",
"createdAt": "2024-05-22T20:18:18.080Z",
"createdAtHeight": "12905490",
"closedAt": null,
"sumOpen": "8",
"sumClose": "0",
"netFunding": "0",
"subaccountNumber": 128
}
},
"assetPositions": {
"USDC": {
"size": "49.601632",
"symbol": "USDC",
"side": "LONG",
"assetId": "0",
"subaccountNumber": 128
}
},
"marginEnabled": true,
"updatedAtHeight": "12905505"
},
{
"address": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5",
"subaccountNumber": 256,
"equity": "41.902552695",
"freeCollateral": "21.9040510255",
"openPerpetualPositions": {
"XLM-USD": {
"market": "XLM-USD",
"status": "OPEN",
"side": "LONG",
"size": "1810",
"maxSize": "1810",
"entryPrice": "0.11046795580110497238",
"exitPrice": null,
"realizedPnl": "0",
"unrealizedPnl": "0.0380166949999999922",
"createdAt": "2024-05-22T20:30:13.972Z",
"createdAtHeight": "12906057",
"closedAt": null,
"sumOpen": "1810",
"sumClose": "0",
"netFunding": "0",
"subaccountNumber": 256
}
},
"assetPositions": {
"USDC": {
"size": "158.082464",
"symbol": "USDC",
"side": "SHORT",
"assetId": "0",
"subaccountNumber": 256
}
},
"marginEnabled": true,
"updatedAtHeight": "12906057"
},
{
"address": "dydx155va0m7wz5n8zcqscn9afswwt04n4usj46wvp5",
"subaccountNumber": 384,
"equity": "39.638080292",
"freeCollateral": "20.6751854628",
"openPerpetualPositions": {
"ARB-USD": {
"market": "ARB-USD",
"status": "OPEN",
"side": "LONG",
"size": "166",
"maxSize": "166",
"entryPrice": "1.14298795180722891566",
"exitPrice": null,
"realizedPnl": "0",
"unrealizedPnl": "-0.10705170799999999956",
"createdAt": "2024-05-22T20:31:23.666Z",
"createdAtHeight": "12906110",
"closedAt": null,
"sumOpen": "166",
"sumClose": "0",
"netFunding": "0",
"subaccountNumber": 384
}
},
"assetPositions": {
"USDC": {
"size": "149.990868",
"symbol": "USDC",
"side": "SHORT",
"assetId": "0",
"subaccountNumber": 384
}
},
"marginEnabled": true,
"updatedAtHeight": "12906110"
}
]
},
"orders": []
}
}
""".trimIndent()

internal val channel_batch_data = """
{
"type": "channel_batch_data",
Expand Down

0 comments on commit 78ecf70

Please sign in to comment.