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

Create Input for Adjusting Isolated Position's Margin #326

Merged
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cb128db
draft new Input
jaredvu Apr 3, 2024
a78ece2
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 3, 2024
4a4c397
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 4, 2024
4d3c22a
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 23, 2024
bc3c4f2
adjustIsolatedMarginInput: state, tests, calculators
jaredvu Apr 26, 2024
7dd42c9
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 26, 2024
84201d3
update documentation and nit
jaredvu Apr 26, 2024
37dbcdf
update summary
jaredvu Apr 30, 2024
d961cc9
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 30, 2024
d1f2736
spotlessApply
jaredvu Apr 30, 2024
2296a58
remove newline
jaredvu Apr 30, 2024
04f755c
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 30, 2024
3c1c059
bump version
jaredvu Apr 30, 2024
dc966b4
linter get what linter wants
jaredvu Apr 30, 2024
157d951
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu Apr 30, 2024
59ec66f
detekt rule
jaredvu Apr 30, 2024
a7efaba
fix validation
jaredvu Apr 30, 2024
eeb7829
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu May 6, 2024
b4a693c
force enum usage instead of converting to string
jaredvu May 6, 2024
527b5eb
spotlessApply
jaredvu May 6, 2024
b25c10b
string -> enum
jaredvu May 7, 2024
875771a
kotlin enum feedback
jaredvu May 7, 2024
45171ec
built-in kotlin enum for input field
jaredvu May 8, 2024
360b43d
bump verison, update test
jaredvu May 8, 2024
09442d9
Merge branch 'main' into jared/tra-183-create-input-for-adjusting-pos…
jaredvu May 8, 2024
92b79b6
Update accessors due to built-in enum change
jaredvu May 8, 2024
a0a969f
update docs
jaredvu May 8, 2024
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 @@ -49,7 +49,7 @@ allprojects {
}

group = "exchange.dydx.abacus"
version = "1.7.3"
version = "1.7.4"

repositories {
google()
Expand Down
3 changes: 3 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ naming:
# /exchange -> /dydx -> /abacus
# didn't seem worth the potential thrash in PRs to fix (feel free to fix if you feel differently)
active: false
MatchingDeclarationName:
# Affects a lot of the TradingStateMachine+_.kt files
active: false

complexity:
CognitiveComplexMethod:
Expand Down
27 changes: 26 additions & 1 deletion docs/API/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,29 @@ Take profit order trigger price's percentage difference from the position's aver

### takeProfitUsdcDiff

Take profit order trigger price's usdc difference from the position's average entry price
Take profit order trigger price's usdc difference from the position's average entry price

# AdjustIsolatedMargin

fun adjustIsolatedMargin(data: String?, type: AdjustIsolatedMarginInputField?): AppStateResponse

The input state is in `response.state.input.adjustIsolatedMargin` as a [AdjustIsolatedMarginInput](../Input/AdjustIsolatedMarginInput.md).

### data

Data input in string format

## AdjustIsolatedMarginInputField

### type

ADD - Add margin to the child's isolated margin account from the parent's cross margin account
REMOVE - Remove margin from the child's isolated margin account to the parent's cross margin account

### amount

Amount of USDC to remove or add

### childSubaccountNumber

Subaccount number for the child whose margin is to be adjusted
21 changes: 21 additions & 0 deletions docs/Input/AdjustIsolatedMarginInput.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# AdjustIsolatedMarginInput

data class TriggerOrdersInput(
 val type: String?, // "ADD" or "REMOVE"
 val amount: Double?,
 val childSubaccountNumber: Int?,
)

## type

ADD - Add margin to the child's isolated margin account from the parent's cross margin account
REMOVE - Remove margin from the child's isolated margin account to the parent's cross margin account

## amount

Amount of USDC to remove or add

## childSubaccountNumber

Subaccount number for the child whose margin is to be adjusted

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package exchange.dydx.abacus.calculator

import exchange.dydx.abacus.output.input.IsolatedMarginAdjustmentType
Copy link
Contributor

Choose a reason for hiding this comment

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

can use static imports for Add and Remove specifically to make the code easier to read below.

import exchange.dydx.abacus.output.input.IsolatedMarginAdjustmentType.Add
import exchange.dydx.abacus.output.input.IsolatedMarginAdjustmentType.Remove

Copy link
Contributor Author

Choose a reason for hiding this comment

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

qq: wdym by easier to read? The import would be easier to read?

Copy link
Contributor

Choose a reason for hiding this comment

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

like at usage sites you now get:

when(type) {
  Add -> ...
  Remove -> ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow that's pretty cool! I don't have a preference on either though. With the static import I think we would lose some readability unless you double check the enum, and what happens if there are multiple enums with the same .name

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah up to you, i don't feel strongly.

re multiple enums: you have to explicitly say what type you're casting the value to, so that ends up not being an issue. ex:

val type = "Add"
when(IsolatedMarginAdjustmentType.valueOf(type)) {
  Add -> ...
  Remove -> ...
}

import exchange.dydx.abacus.protocols.ParserProtocol
import exchange.dydx.abacus.utils.Numeric
import exchange.dydx.abacus.utils.mutable
import exchange.dydx.abacus.utils.safeSet

@Suppress("UNCHECKED_CAST")
internal class AdjustIsolatedMarginInputCalculator(val parser: ParserProtocol) {
private val subaccountTransformer = SubaccountTransformer()

internal fun calculate(
state: Map<String, Any>,
parentSubaccountNumber: Int?,
): Map<String, Any> {
val wallet = parser.asNativeMap(state["wallet"])
val isolatedMarginAdjustment = parser.asNativeMap(state["adjustIsolatedMargin"])
val childSubaccountNumber = parser.asInt(isolatedMarginAdjustment?.get("childSubaccountNumber"))
val type = parser.asString(isolatedMarginAdjustment?.get("type"))?.let {
IsolatedMarginAdjustmentType.invoke(it)
} ?: IsolatedMarginAdjustmentType.Add

return if (wallet != null && isolatedMarginAdjustment != null && type != null) {
val modified = state.mutable()
val parentTransferDelta = getModifiedTransferDelta(isolatedMarginAdjustment, true)
val childTransferDelta = getModifiedTransferDelta(isolatedMarginAdjustment, false)

val walletPostParentSubaccountTransfer =
subaccountTransformer.applyIsolatedMarginAdjustmentToWallet(
wallet,
subaccountNumber = parentSubaccountNumber,
parentTransferDelta,
parser,
"postOrder",
)

val walletPostChildSubaccountTransfer =
subaccountTransformer.applyIsolatedMarginAdjustmentToWallet(
wallet = walletPostParentSubaccountTransfer,
subaccountNumber = childSubaccountNumber,
childTransferDelta,
parser,
"postOrder",
)

val modifiedParentSubaccount = parser.asNativeMap(parser.value(walletPostChildSubaccountTransfer, "accounts.subaccounts.$parentSubaccountNumber"))
val modifiedChildSubaccount = parser.asNativeMap(parser.value(walletPostChildSubaccountTransfer, "accounts.subaccounts.$childSubaccountNumber"))
val modifiedIsolatedMarginAdjustment = finalize(isolatedMarginAdjustment, modifiedParentSubaccount, modifiedChildSubaccount, type)

modified["adjustIsolatedMargin"] = modifiedIsolatedMarginAdjustment
modified["wallet"] = walletPostChildSubaccountTransfer
modified
} else {
state
}
}

private fun getModifiedTransferDelta(
isolatedMarginAdjustment: Map<String, Any>,
isParentSubaccount: Boolean,
): Map<String, Double> {
val type = parser.asString(isolatedMarginAdjustment["type"])?.let {
IsolatedMarginAdjustmentType.invoke(it)
} ?: IsolatedMarginAdjustmentType.Add
val amount = parser.asDouble(isolatedMarginAdjustment["amount"])

when (type) {
IsolatedMarginAdjustmentType.Add -> {
val multiplier =
if (isParentSubaccount) Numeric.double.NEGATIVE else Numeric.double.POSITIVE
val usdcSize = (amount ?: Numeric.double.ZERO) * multiplier

return mapOf(
"usdcSize" to usdcSize,
)
}

IsolatedMarginAdjustmentType.Remove -> {
val multiplier =
if (isParentSubaccount) Numeric.double.POSITIVE else Numeric.double.NEGATIVE
val usdcSize = (amount ?: Numeric.double.ZERO) * multiplier

return mapOf(
"usdcSize" to usdcSize,
)
}
}
}

private fun summaryForType(
parentSubaccount: Map<String, Any>?,
childSubaccount: Map<String, Any>?,
type: IsolatedMarginAdjustmentType,
): Map<String, Any> {
val summary = mutableMapOf<String, Any>()
val crossCollateral = parser.asDouble(parser.value(parentSubaccount, "freeCollateral.postOrder"))
val crossMarginUsage = parser.asDouble(parser.value(parentSubaccount, "marginUsage.postOrder"))
val openPositions = parser.asNativeMap(childSubaccount?.get("openPositions"))
val marketId = openPositions?.keys?.firstOrNull()
val positionMargin = parser.asDouble(parser.value(childSubaccount, "freeCollateral.postOrder"))
val positionLeverage = parser.asDouble(parser.value(childSubaccount, "openPositions.$marketId.leverage.postOrder"))
val liquidationPrice = parser.asDouble(parser.value(childSubaccount, "openPositions.$marketId.liquidationPrice.postOrder"))

when (type) {
IsolatedMarginAdjustmentType.Add -> {
summary.safeSet("crossFreeCollateral", crossCollateral)
summary.safeSet("crossMarginUsage", crossMarginUsage)
summary.safeSet("positionMargin", positionMargin)
summary.safeSet("positionLeverage", positionLeverage)
summary.safeSet("liquidationPrice", liquidationPrice)
}

IsolatedMarginAdjustmentType.Remove -> {
summary.safeSet("crossFreeCollateral", crossCollateral)
summary.safeSet("crossMarginUsage", crossMarginUsage)
summary.safeSet("positionMargin", positionMargin)
summary.safeSet("positionLeverage", positionLeverage)
summary.safeSet("liquidationPrice", liquidationPrice)
}
}

return summary
}

private fun finalize(
isolatedMarginAdjustment: Map<String, Any>,
parentSubaccount: Map<String, Any>?,
childSubaccount: Map<String, Any>?,
type: IsolatedMarginAdjustmentType,
): Map<String, Any> {
val modified = isolatedMarginAdjustment.mutable()
modified.safeSet("summary", summaryForType(parentSubaccount, childSubaccount, type))
return modified
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,29 @@ internal class SubaccountTransformer {
}
}

internal fun applyIsolatedMarginAdjustmentToWallet(
wallet: Map<String, Any>,
subaccountNumber: Int?,
delta: Map<String, Double>?,
parser: ParserProtocol,
period: String
): Map<String, Any> {
return if (delta != null) {
jaredvu marked this conversation as resolved.
Show resolved Hide resolved
val key = "account.subaccounts.$subaccountNumber"
val subaccount = parser.asNativeMap(parser.value(wallet, key))
if (subaccount != null) {
val modifiedSubaccount = applyDeltaToSubaccount(subaccount, delta, parser, period)
val modifiedWallet = wallet.mutable()
modifiedWallet.safeSet(key, modifiedSubaccount)
modifiedWallet
} else {
wallet
}
} else {
wallet
}
}

internal fun applyTradeToSubaccount(
subaccount: Map<String, Any>?,
trade: Map<String, Any>,
Expand Down
Loading
Loading