From 3e8c66b4a80dcb3a56e9708f4602b49a9ecc32ae Mon Sep 17 00:00:00 2001 From: Tucsky Date: Sun, 29 Sep 2024 18:38:36 +0200 Subject: [PATCH] feat: show weighted average in trade feed #402 --- CHANGELOG.md | 7 +- package-lock.json | 2 +- package.json | 2 +- .../framework/editor/references/README.md | 29 +++ src/components/prices/Prices.vue | 2 +- src/components/trades/Trades.vue | 1 + src/components/trades/TradesLite.vue | 4 +- src/components/trades/TradesSettings.vue | 165 +++++++++--------- src/components/trades/tradesFeed.ts | 10 +- src/services/aggregatorService.ts | 3 +- src/store/panesSettings/trades.ts | 4 +- src/types/types.d.ts | 4 +- src/utils/helpers.ts | 2 +- src/worker/aggregator.ts | 25 +-- 14 files changed, 154 insertions(+), 106 deletions(-) create mode 100644 src/components/framework/editor/references/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e89c7cbc..985da5c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,14 @@ All notable changes to this project will be documented here. -## [3.6.1] - 2024-08-14 +## [3.6.2] - 2024-09-29 - Fix Dockerfile and startup instructions +- Show weighted average price in trade feed (instead of last price) + +## [3.6.1] - 2024-09-27 + +- Fix Binance Futures wicks of death ## [3.6.0-hotfix.0] - 2024-08-22 diff --git a/package-lock.json b/package-lock.json index 4572b41d..7ac3cd18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "aggr", - "version": "3.6.1", + "version": "3.6.2", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index bdcfca6d..5d41dbb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aggr", - "version": "3.6.1", + "version": "3.6.2", "private": true, "type": "module", "scripts": { diff --git a/src/components/framework/editor/references/README.md b/src/components/framework/editor/references/README.md new file mode 100644 index 00000000..8344d6b5 --- /dev/null +++ b/src/components/framework/editor/references/README.md @@ -0,0 +1,29 @@ +```ts +area( + value: number | { + value: number, + time: number + }, + [options] +) +``` + +Renders an area series for the current bar using the specified value and optional styling parameters. This function adapts the `addAreaSeries` method from Lightweight Charts for individual bar rendering. + +## Parameters + +- `value`: The value for the current bar, either a number or an object with `value` and `time` properties. +- `options` (optional): A set of styling options for the area series. Options include: + - `lineColor`: The color of the line in the area series (e.g., `lineColor=yellow`). + - `topColor`: The color at the top of the area series (e.g., `topColor='#2962FF'`). + - `bottomColor`: The color at the bottom of the area series (e.g., `bottomColor='rgba(41, 98, 255, 0.28)'`). + +## Returns + +- This function does not return a value. It renders the area series on the chart for the current bar. + +## Summary + +The `area` function is designed for real-time, bar-by-bar rendering of area series in charts. It accepts a value for the current bar and a series of optional styling parameters, allowing for customization of the series appearance. The parameters follow a key=value format, making it flexible and intuitive to specify various options. This function is ideal for applications requiring dynamic and visually distinct data visualization, particularly in financial or data-intensive contexts. + +*Note: All options are optional. The function can be used with just the `value` parameter (e.g., `area(1)`), in which case default styles will be applied. The styling parameters must be valid CSS color values for the function to render the series correctly.* diff --git a/src/components/prices/Prices.vue b/src/components/prices/Prices.vue index ab971b6b..9f837ca2 100644 --- a/src/components/prices/Prices.vue +++ b/src/components/prices/Prices.vue @@ -672,7 +672,7 @@ export default class Prices extends Mixins(PaneMixin) { &__exchange { padding: 0; background-repeat: no-repeat; - background-size: 1em; + background-size: 1em 1em; width: 1rem; align-self: stretch; flex-shrink: 0; diff --git a/src/components/trades/Trades.vue b/src/components/trades/Trades.vue index db965d80..656b1c66 100644 --- a/src/components/trades/Trades.vue +++ b/src/components/trades/Trades.vue @@ -241,6 +241,7 @@ export default class Trades extends Mixins(PaneMixin) { exchange, pair, price, + avgPrice: price, amount, size, side diff --git a/src/components/trades/TradesLite.vue b/src/components/trades/TradesLite.vue index f75ad5b4..253d37a8 100644 --- a/src/components/trades/TradesLite.vue +++ b/src/components/trades/TradesLite.vue @@ -183,6 +183,7 @@ export default class TradesLite extends Mixins(PaneMixin) { private addedVolumeBySide: { buy: number; sell: number } private offset: number private maxCount: number + private showAvgPrice: boolean private limit: number private batchSize = 1 @@ -344,7 +345,7 @@ export default class TradesLite extends Mixins(PaneMixin) { pair: trades[i].pair, amount: trades[i].amount, count: trades[i].count, - price: trades[i].price, + price: this.showAvgPrice ? trades[i].avgPrice : trades[i].price, side: trades[i].side, time: null } @@ -723,6 +724,7 @@ export default class TradesLite extends Mixins(PaneMixin) { this.maxHistory = pane.maxRows this.showHistograms = pane.showHistograms this.showPairs = pane.showPairs + this.showAvgPrice = pane.showAvgPrice this.renderTrades = !pane.showHistograms || this.height > window.innerHeight / 24 this.showPrices = pane.showPrices diff --git a/src/components/trades/TradesSettings.vue b/src/components/trades/TradesSettings.vue index 1a949c32..f3d82cc3 100644 --- a/src/components/trades/TradesSettings.vue +++ b/src/components/trades/TradesSettings.vue @@ -222,7 +222,7 @@ - +
-
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
- -
-
-
-
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+
${priceSlippage}${formatMarketPrice( - trade.price, + ? `
${priceSlippage}${formatMarketPrice( + this.showAvgPrice ? trade.avgPrice : trade.price, marketKey - )}
` + )}
` : '' }
${' '} - ${formatAmount(trade.size * trade.price)} + ${formatAmount(trade.size * trade.avgPrice)} ${' '} @@ -522,6 +523,7 @@ export default class TradesFeed { this.showLogos = store.state[this.paneId].showLogos this.showPairs = store.state[this.paneId].showPairs this.showPrices = store.state[this.paneId].showPrices + this.showAvgPrice = store.state[this.paneId].showAvgPrice this.showTimeAgo = store.state[this.paneId].showTimeAgo if (this.showTimeAgo && !this.timeUpdateInterval) { diff --git a/src/services/aggregatorService.ts b/src/services/aggregatorService.ts index ce3494da..cfa7d171 100644 --- a/src/services/aggregatorService.ts +++ b/src/services/aggregatorService.ts @@ -82,6 +82,7 @@ class AggregatorService extends EventEmitter { marketDecimals[market] = countDecimals( price < 0.000001 ? price + 1 : price ) + if (!this.normalizeDecimalsQueue) { this.normalizeDecimalsQueue = { markets: [] @@ -264,4 +265,4 @@ class AggregatorService extends EventEmitter { } } -export default new AggregatorService() \ No newline at end of file +export default new AggregatorService() diff --git a/src/store/panesSettings/trades.ts b/src/store/panesSettings/trades.ts index 29f0eeb4..ed3af4d8 100644 --- a/src/store/panesSettings/trades.ts +++ b/src/store/panesSettings/trades.ts @@ -40,6 +40,7 @@ export interface TradesPaneState { monochromeLogos: boolean multipliers: { [identifier: string]: number } thresholdsMultipler: number + showAvgPrice: boolean } const getters = { @@ -75,7 +76,8 @@ const state = { showTimeAgo: true, showPrices: true, showHistograms: true, - thresholdsMultipler: 1 + thresholdsMultipler: 1, + showAvgPrice: true } as TradesPaneState const actions = { diff --git a/src/types/types.d.ts b/src/types/types.d.ts index 22bdb32a..330a4226 100644 --- a/src/types/types.d.ts +++ b/src/types/types.d.ts @@ -22,6 +22,7 @@ export interface AggregatorPayload { export interface AggregatedTrade extends Trade { originalPrice: number + value: number } export interface AggregatorSettings { @@ -45,9 +46,10 @@ export interface Trade { price: number size: number side: 'buy' | 'sell' + originalPrice?: number + avgPrice?: number amount?: number count?: number - originalPrice?: number liquidation?: boolean slippage?: number } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index a4dedadd..d92f1138 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -347,7 +347,7 @@ export function copyTextToClipboard(text) { function formatAmount(amount, decimals?: number) { const negative = amount < 0 - amount = Math.abs(amount) + amount = Math.ceil(Math.abs(amount)) if (amount >= 1000000000) { amount = diff --git a/src/worker/aggregator.ts b/src/worker/aggregator.ts index 949b7922..c3429d0c 100644 --- a/src/worker/aggregator.ts +++ b/src/worker/aggregator.ts @@ -209,6 +209,7 @@ class Aggregator { ) { aggTrade.size += trade.size aggTrade.price = trade.price + aggTrade.value += trade.price * trade.size aggTrade.count += trade.count || 1 continue } else { @@ -217,6 +218,7 @@ class Aggregator { } trade.originalPrice = this.tickers[marketKey].price || trade.price + trade.value = trade.price * trade.size trade.count = trade.count || 1 this.aggregationTimeouts[marketKey] = now + this.baseAggregationTimeout @@ -276,7 +278,7 @@ class Aggregator { } } - processTrade(trade: Trade): Trade { + processTrade(trade: AggregatedTrade | Trade): Trade { const marketKey = trade.exchange + ':' + trade.pair if (settings.calculateSlippage) { @@ -285,24 +287,29 @@ class Aggregator { Math.round( (trade.price - trade.originalPrice + Number.EPSILON) * 10 ) / 10 - if (Math.abs(trade.slippage) / trade.price < 0.00025) { - trade.slippage = null + if (Math.abs(trade.slippage) / trade.price < 0.000025) { + trade.slippage = 0 } } else if (settings.calculateSlippage === 'bps') { - if (trade.side === 'buy') { - trade.slippage = Math.floor( + if (trade.price > trade.originalPrice) { + trade.slippage = Math.round( ((trade.price - trade.originalPrice) / trade.originalPrice) * 10000 ) } else { - trade.slippage = Math.floor( + trade.slippage = Math.round( ((trade.originalPrice - trade.price) / trade.price) * 10000 ) } } } + trade.avgPrice = + trade.count > 1 + ? (trade as AggregatedTrade).value / trade.size + : trade.price + trade.amount = - (settings.preferQuoteCurrencySize ? trade.price : 1) * trade.size + (settings.preferQuoteCurrencySize ? trade.avgPrice : 1) * trade.size this.tickers[marketKey].updated = true this.tickers[marketKey].volume += trade.amount @@ -314,10 +321,6 @@ class Aggregator { this.emitInitialPrice(marketKey, trade.price) } - if (settings.aggregationLength > 0) { - trade.price = Math.max(trade.price, trade.originalPrice) - } - if (this.connections[marketKey].bucket) { this.connections[marketKey].bucket['c' + trade.side] += trade.count this.connections[marketKey].bucket['v' + trade.side] += trade.amount