-
Notifications
You must be signed in to change notification settings - Fork 15
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
v1.6.48: Add liquidation warning for stop market trigger price #315
Changes from 4 commits
7488a34
1cb8274
88ab02e
04becfd
10ed401
c8144a6
4ed3984
0eb9852
5bb8264
5a21d1c
be09d61
40fdc3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ internal class TriggerOrdersInputValidator( | |
|
||
val marketId = parser.asString(transaction["marketId"]) ?: return null | ||
val market = parser.asNativeMap(markets?.get(marketId)) | ||
val position = parser.asNativeMap(parser.value(subaccount, "openPositions.$marketId")) ?: return null | ||
val tickSize = parser.asString(parser.value(market, "configs.tickSize")) ?: "0.01" | ||
val oraclePrice = parser.asDouble( | ||
parser.value( | ||
|
@@ -87,6 +88,7 @@ internal class TriggerOrdersInputValidator( | |
market, | ||
oraclePrice, | ||
tickSize, | ||
validateStopLossTriggerToLiquidationPrice(stopLossOrder, position, tickSize), | ||
) | ||
} else { | ||
null | ||
|
@@ -130,6 +132,7 @@ internal class TriggerOrdersInputValidator( | |
market: Map<String, Any>?, | ||
oraclePrice: Double, | ||
tickSize: String, | ||
liquidationPriceErrors: List<Any>? = null, | ||
): MutableList<Any>? { | ||
val triggerErrors = mutableListOf<Any>() | ||
|
||
|
@@ -166,9 +169,84 @@ internal class TriggerOrdersInputValidator( | |
*/ | ||
triggerErrors.addAll(it) | ||
} | ||
|
||
liquidationPriceErrors?.let { | ||
/* | ||
SELL_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE | ||
BUY_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE | ||
*/ | ||
triggerErrors.addAll(it) | ||
} | ||
|
||
return if (triggerErrors.size > 0) triggerErrors else null | ||
} | ||
|
||
private fun validateStopLossTriggerToLiquidationPrice( | ||
triggerOrder: Map<String, Any>, | ||
position: Map<String, Any>, | ||
tickSize: String, | ||
): List<Any>? { | ||
val liquidationPrice = parser.asDouble(parser.value(position, "liquidationPrice.current")) | ||
val triggerPrice = parser.asDouble(parser.value(triggerOrder, "price.triggerPrice")) | ||
|
||
if (liquidationPrice == null || triggerPrice == null) { | ||
return null | ||
} | ||
|
||
val type = parser.asString(triggerOrder["type"]) | ||
val side = parser.asString(triggerOrder["side"]) | ||
|
||
return when (requiredTriggerToLiquidationPrice(type, side)) { | ||
RelativeToPrice.ABOVE -> { | ||
if (triggerPrice <= liquidationPrice) { | ||
listOf( | ||
error( | ||
"ERROR", | ||
"SELL_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
listOf(TriggerOrdersInputField.stopLossPrice.rawValue), | ||
"APP.TRADE.MODIFY_TRIGGER_PRICE", | ||
"ERRORS.TRADE_BOX_TITLE.SELL_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
"ERRORS.TRADE_BOX.SELL_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
mapOf( | ||
"TRIGGER_PRICE_LIMIT" to mapOf( | ||
"value" to liquidationPrice, | ||
"format" to "price", | ||
"tickSize" to tickSize, | ||
), | ||
), | ||
), | ||
) | ||
} else { | ||
null | ||
} | ||
} | ||
RelativeToPrice.BELOW -> { | ||
if (triggerPrice >= liquidationPrice) { | ||
listOf( | ||
error( | ||
"ERROR", | ||
"BUY_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
listOf(TriggerOrdersInputField.stopLossPrice.rawValue), | ||
"APP.TRADE.MODIFY_TRIGGER_PRICE", | ||
"ERRORS.TRADE_BOX_TITLE.BUY_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
"ERRORS.TRADE_BOX.BUY_TRIGGER_TOO_CLOSE_TO_LIQUIDATION_PRICE", | ||
mapOf( | ||
"TRIGGER_PRICE_LIMIT" to mapOf( | ||
"value" to liquidationPrice, | ||
"format" to "price", | ||
"tickSize" to tickSize, | ||
), | ||
), | ||
), | ||
) | ||
} else { | ||
null | ||
} | ||
} | ||
else -> null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this else case needed...? (as in, does the compiler complain if you remove it) and if it is, i would again consider whether this is a valid state to be in and if we should just error out instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when using |
||
} | ||
} | ||
|
||
private fun validateOrderCount( | ||
triggerOrders: Map<String, Any>, | ||
subaccount: Map<String, Any>?, | ||
|
@@ -543,6 +621,19 @@ internal class TriggerOrdersInputValidator( | |
return null | ||
} | ||
|
||
private fun requiredTriggerToLiquidationPrice(type: String?, side: String?): RelativeToPrice? { | ||
return when (type) { | ||
"STOP_MARKET" -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we make these case enums instead of strings, we can avoid having an I would also consider whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah interesting; yeah the current pattern I've seen in the repo is to return early (return null) if we get an unexpected null type (but then detekt yelled at me because we had too many return statements in a function oops). Happy to pivot to using |
||
when (side) { | ||
"BUY" -> RelativeToPrice.BELOW | ||
"SELL" -> RelativeToPrice.ABOVE | ||
else -> null | ||
} | ||
|
||
else -> null | ||
} | ||
} | ||
|
||
private fun requiredTriggerToIndexPrice(type: String, side: String): RelativeToPrice? { | ||
return when (type) { | ||
"STOP_LIMIT", "STOP_MARKET" -> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might we be able to instead pass
position
as an argument and then move the liquidationPriceErrors calculations into this function body? Seems like that is the existing pattern for this function's bodyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm so liquidationPriceErrors only applies to stop loss orders (not take profit) - which is why I have it passed in from the caller. I could pass in a🤔 oh actually I do the check for type anyways in the stopLossLiquidation function; so ignore me, yes candoisStopLoss?
as a param here but not sure if that's much better