Skip to content

Commit

Permalink
MOB-495: Add entry and liquidation lines. (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
prashanDYDX authored May 13, 2024
1 parent 8334424 commit f9ceda5
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry
import com.github.mikephil.charting.data.CandleEntry
import com.github.mikephil.charting.data.Entry
import exchange.dydx.abacus.output.input.OrderSide
import exchange.dydx.abacus.output.input.OrderType
import exchange.dydx.abacus.protocols.LocalizerProtocol
import exchange.dydx.platformui.components.buttons.PlatformPillItem
import exchange.dydx.platformui.components.charts.config.CombinedChartConfig
Expand All @@ -53,10 +51,6 @@ import exchange.dydx.platformui.designSystem.theme.ThemeFont
import exchange.dydx.platformui.designSystem.theme.ThemeShapes
import exchange.dydx.platformui.designSystem.theme.color
import exchange.dydx.platformui.designSystem.theme.dydxDefault
import exchange.dydx.platformui.designSystem.theme.negativeColor
import exchange.dydx.platformui.designSystem.theme.positiveColor
import exchange.dydx.platformui.designSystem.theme.textOnNegativeColor
import exchange.dydx.platformui.designSystem.theme.textOnPositiveColor
import exchange.dydx.platformui.designSystem.theme.themeColor
import exchange.dydx.platformui.designSystem.theme.themeFont
import exchange.dydx.trading.common.component.DydxComponent
Expand Down Expand Up @@ -95,7 +89,7 @@ object DydxMarketPricesView : DydxComponent {
val candles: CandleChartDataSet?,
val volumes: BarDataSet?,
val prices: LineChartDataSet?,
val orderLines: List<OrderData>,
val orderLines: List<OrderLineData>,
val typeOptions: SelectionOptions,
val resolutionOptions: SelectionOptions,
val highlight: PriceHighlight? = null,
Expand Down Expand Up @@ -130,7 +124,7 @@ object DydxMarketPricesView : DydxComponent {
"funding",
),
listOf(
OrderData(1.0, OrderSide.buy, 1.0, "$1.0", OrderType.limit),
OrderLineData(1.0, ThemeColor.SemanticColor.color_green.color.toArgb(), ThemeColor.SemanticColor.color_white.color.toArgb(), 1.0, "$1.0", "Limit"),
),
typeOptions = SelectionOptions(
titles = listOf("Candles", "Lines"),
Expand Down Expand Up @@ -205,7 +199,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.datetimeText ?: "",
text = highlight.datetimeText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand All @@ -223,7 +217,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.openText ?: "",
text = highlight.openText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand All @@ -238,7 +232,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.highText ?: "",
text = highlight.highText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand All @@ -252,7 +246,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.lowText ?: "",
text = highlight.lowText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand All @@ -266,7 +260,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.closeText ?: "",
text = highlight.closeText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand All @@ -280,7 +274,7 @@ object DydxMarketPricesView : DydxComponent {
.themeColor(ThemeColor.SemanticColor.text_tertiary),
)
Text(
text = highlight.volumeText ?: "",
text = highlight.volumeText,
style = TextStyle.dydxDefault
.themeFont(fontSize = ThemeFont.FontSize.tiny)
.themeColor(ThemeColor.SemanticColor.text_secondary),
Expand Down Expand Up @@ -408,16 +402,16 @@ internal class CombinedChartWithOrderLines(
(parent as ViewGroup).clipChildren = true
}

var orderLines: List<OrderData> = emptyList()
var orderLines: List<OrderLineData> = emptyList()
set(value) {
if (field == value) return
field = value
axisLeft.removeAllLimitLines()
value.forEach { (price, side) ->
value.forEach { (price, lineColor, textColor) ->
LimitLine(price.toFloat())
.apply {
lineColor = side.orderLineColor
textColor = side.orderLineTextColor
this.lineColor = lineColor
this.textColor = textColor
enableDashedLine(20f, 10f, 0f)
}
.also { axisLeft.addLimitLine(it) }
Expand All @@ -439,38 +433,38 @@ internal class CombinedChartWithOrderLines(
}
}

private fun Canvas.drawOrderPriceTag(yPos: Float, orderLine: OrderData): Float {
private fun Canvas.drawOrderPriceTag(yPos: Float, orderLine: OrderLineData): Float {
return drawTextView(
xPos = 0f,
yPos = yPos,
text = orderLine.formattedPrice,
backgroundColor = orderLine.side.orderLineColor,
textColor = orderLine.side.orderLineTextColor,
backgroundColor = orderLine.lineColor,
textColor = orderLine.textColor,
)
}

private fun Canvas.drawOrderLabelAndSize(xPos: Float, yPos: Float, orderLine: OrderData) {
private fun Canvas.drawOrderLabelAndSize(xPos: Float, yPos: Float, orderLine: OrderLineData) {
val orderLabelRight = drawOrderLabel(xPos, yPos, orderLine)
drawOrderSize(orderLabelRight, yPos, orderLine)
}

private fun Canvas.drawOrderLabel(xPos: Float, yPos: Float, orderLine: OrderData): Float {
private fun Canvas.drawOrderLabel(xPos: Float, yPos: Float, orderLine: OrderLineData): Float {
return drawTextView(
xPos = xPos,
yPos = yPos,
text = localizer.localize(orderLine.orderType.labelKey),
text = localizer.localize(orderLine.labelKey),
backgroundColor = ThemeColor.SemanticColor.layer_1.color.toArgb(),
textColor = ThemeColor.SemanticColor.text_tertiary.color.toArgb(),
)
}

private fun Canvas.drawOrderSize(xPos: Float, yPos: Float, orderLine: OrderData) {
private fun Canvas.drawOrderSize(xPos: Float, yPos: Float, orderLine: OrderLineData) {
drawTextView(
xPos = xPos,
yPos = yPos,
text = "${orderLine.size}",
backgroundColor = orderLine.side.orderLineColor,
textColor = orderLine.side.orderLineTextColor,
backgroundColor = orderLine.lineColor,
textColor = orderLine.textColor,
horizontalPadding = 12f,
)
}
Expand Down Expand Up @@ -513,25 +507,3 @@ internal class CombinedChartWithOrderLines(
return right
}
}

private val OrderSide.orderLineColor: Int
get() = when (this) {
OrderSide.buy -> ThemeColor.SemanticColor.positiveColor.color.toArgb()
OrderSide.sell -> ThemeColor.SemanticColor.negativeColor.color.toArgb()
}

private val OrderSide.orderLineTextColor: Int
get() = when (this) {
OrderSide.buy -> ThemeColor.SemanticColor.textOnPositiveColor.color.toArgb()
OrderSide.sell -> ThemeColor.SemanticColor.textOnNegativeColor.color.toArgb()
}

private val OrderType.labelKey: String
get() = when (this) {
OrderType.takeProfitMarket -> "APP.TRADE.TAKE_PROFIT_MARKET"
OrderType.takeProfitLimit -> "APP.TRADE.TAKE_PROFIT_LIMIT"
OrderType.limit -> "APP.TRADE.LIMIT_ORDER"
OrderType.stopLimit -> "APP.TRADE.STOP_LIMIT"
OrderType.stopMarket -> "APP.TRADE.STOP_MARKET"
else -> error("$this is not supported by orderlines")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import exchange.dydx.platformui.designSystem.theme.ThemeColor.SemanticColor
import exchange.dydx.platformui.designSystem.theme.color
import exchange.dydx.platformui.designSystem.theme.negativeColor
import exchange.dydx.platformui.designSystem.theme.positiveColor
import exchange.dydx.platformui.designSystem.theme.textOnNegativeColor
import exchange.dydx.platformui.designSystem.theme.textOnPositiveColor
import exchange.dydx.trading.common.DydxViewModel
import exchange.dydx.trading.common.di.CoroutineScopes
import exchange.dydx.trading.common.formatter.DydxFormatter
Expand All @@ -40,11 +42,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import timber.log.Timber
import java.time.Instant
Expand Down Expand Up @@ -108,39 +108,80 @@ class DydxMarketPricesViewModel @Inject constructor(
abacusStateManager.marketId
.filterNotNull()
.flatMapLatest { abacusStateManager.state.selectedSubaccountOrdersOfMarket(it) },
abacusStateManager.marketId
.filterNotNull()
.flatMapLatest { abacusStateManager.state.selectedSubaccountPositionOfMarket(it) },
selectedPrice,
typeIndex,
resolutionIndex,
) { market, allPrices, ordersForMarket, selectedPrice, typeIndex, resolutionIndex ->
) { market, allPrices, ordersForMarket, marketPosition, selectedPrice, typeIndex, resolutionIndex ->

val positionSize = marketPosition?.size?.current
val entryPrice = marketPosition?.entryPrice?.current
val liquidationPrice = marketPosition?.liquidationPrice?.current

market.configs?.let { configs ->
val candlesPeriod = candlesPeriods[resolutionIndex]
val prices = allPrices.candles?.get(candlesPeriod)
val orderData = ordersForMarket?.let { orders ->
val orderLineData = ordersForMarket?.let { orders ->
orders
.filter {
it.status in setOf(OrderStatus.open, OrderStatus.untriggered, OrderStatus.partiallyFilled) &&
it.type in setOf(OrderType.limit, OrderType.stopLimit, OrderType.stopMarket, OrderType.takeProfitLimit, OrderType.takeProfitMarket)
}
.map {
OrderData(
price = it.price,
side = it.side,
val price = it.triggerPrice ?: it.price
OrderLineData(
price = price,
lineColor = it.side.orderLineColor,
textColor = it.side.orderLineTextColor,
size = it.remainingSize ?: it.size,
formattedPrice = formatter.dollar(it.price, configs.tickSizeDecimals)
formattedPrice = formatter.dollar(price, configs.tickSizeDecimals)
?: run {
Timber.tag("DydxMarketPricesViewModel")
.e("Failed to format orderline price.")
""
},
orderType = it.type,
labelKey = it.type.labelKey,
)
}
}.orEmpty()
}.orEmpty() + listOfNotNull(
entryPrice?.let {
OrderLineData(
price = it,
lineColor = SemanticColor.color_purple.color.toArgb(),
textColor = SemanticColor.color_white.color.toArgb(),
size = positionSize ?: 0.0,
formattedPrice = formatter.dollar(it, configs.tickSizeDecimals)
?: run {
Timber.tag("DydxMarketPricesViewModel")
.e("Failed to format orderline price.")
""
},
labelKey = "APP.TRADE.ENTRY_PRICE_SHORT",
)
},
liquidationPrice?.let {
OrderLineData(
price = it,
lineColor = SemanticColor.color_yellow.color.toArgb(),
textColor = SemanticColor.color_white.color.toArgb(),
size = positionSize ?: 0.0,
formattedPrice = formatter.dollar(it, configs.tickSizeDecimals)
?: run {
Timber.tag("DydxMarketPricesViewModel")
.e("Failed to format orderline price.")
""
},
labelKey = "APP.TRADE.LIQUIDATION",
)
},
)

createViewState(
prices = prices,
market = market,
orderData = orderData,
orderLineData = orderLineData,
candlesPeriod = candlesPeriod,
selectedPrice = selectedPrice,
typeIndex = typeIndex,
Expand All @@ -158,7 +199,7 @@ class DydxMarketPricesViewModel @Inject constructor(
private fun createViewState(
prices: List<MarketCandle>?,
market: PerpetualMarket?,
orderData: List<OrderData>,
orderLineData: List<OrderLineData>,
candlesPeriod: String,
selectedPrice: MarketCandle?,
typeIndex: Int,
Expand Down Expand Up @@ -232,52 +273,14 @@ class DydxMarketPricesViewModel @Inject constructor(
}
}

val orderData = listOf(
OrderData(
price = 3100.0,
side = OrderSide.sell,
formattedPrice = "$3100.00",
orderType = OrderType.takeProfitLimit,
size = 10.0,
),
OrderData(
price = 3300.0,
side = OrderSide.sell,
formattedPrice = "$3300.00",
orderType = OrderType.takeProfitMarket,
size = 10.0,
),
OrderData(
price = 3500.0,
side = OrderSide.sell,
formattedPrice = "$3500.00",
orderType = OrderType.limit,
size = 10.0,
),
OrderData(
price = 2900.0,
side = OrderSide.buy,
formattedPrice = "$2900.00",
orderType = OrderType.stopMarket,
size = 10.0,
),
OrderData(
price = 2700.0,
side = OrderSide.buy,
formattedPrice = "$2700.00",
orderType = OrderType.stopLimit,
size = 10.0,
),
)

return DydxMarketPricesView.ViewState(
localizer = localizer,
config = config(market, candlesPeriod),
market = market?.id,
candles = CandleDataSet(candles, "candles"),
volumes = BarDataSet(volumes, "volumes"),
prices = LineChartDataSet(lines, "lines"),
orderLines = orderData,
orderLines = orderLineData,
typeOptions = SelectionOptions(
typeTitles,
typeIndex,
Expand Down Expand Up @@ -424,10 +427,33 @@ class DydxMarketPricesViewModel @Inject constructor(
}
}

data class OrderData(
data class OrderLineData(
val price: Double,
val side: OrderSide,
val lineColor: Int,
val textColor: Int,
val size: Double,
val formattedPrice: String,
val orderType: OrderType,
val labelKey: String,
)

private val OrderSide.orderLineColor: Int
get() = when (this) {
OrderSide.buy -> SemanticColor.positiveColor.color.toArgb()
OrderSide.sell -> SemanticColor.negativeColor.color.toArgb()
}

private val OrderSide.orderLineTextColor: Int
get() = when (this) {
OrderSide.buy -> SemanticColor.textOnPositiveColor.color.toArgb()
OrderSide.sell -> SemanticColor.textOnNegativeColor.color.toArgb()
}

private val OrderType.labelKey: String
get() = when (this) {
OrderType.takeProfitMarket -> "APP.TRADE.TAKE_PROFIT_MARKET"
OrderType.takeProfitLimit -> "APP.TRADE.TAKE_PROFIT_LIMIT"
OrderType.limit -> "APP.TRADE.LIMIT_ORDER"
OrderType.stopLimit -> "APP.TRADE.STOP_LIMIT"
OrderType.stopMarket -> "APP.TRADE.STOP_MARKET"
else -> error("$this is not supported by orderlines")
}

0 comments on commit f9ceda5

Please sign in to comment.