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

MOB-495: Add entry and liquidation lines. #99

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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",
prashanDYDX marked this conversation as resolved.
Show resolved Hide resolved
)
},
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")
}
Loading