Merge pull request #85 from blockworks-foundation/stable-price-tv-line
add stable price line to tv chart
This commit is contained in:
commit
d713692dc6
|
@ -6,6 +6,7 @@ import {
|
||||||
IChartingLibraryWidget,
|
IChartingLibraryWidget,
|
||||||
ResolutionString,
|
ResolutionString,
|
||||||
IOrderLineAdapter,
|
IOrderLineAdapter,
|
||||||
|
EntityId,
|
||||||
} from '@public/charting_library'
|
} from '@public/charting_library'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
|
@ -13,6 +14,7 @@ import {
|
||||||
CHART_DATA_FEED,
|
CHART_DATA_FEED,
|
||||||
DEFAULT_MARKET_NAME,
|
DEFAULT_MARKET_NAME,
|
||||||
SHOW_ORDER_LINES_KEY,
|
SHOW_ORDER_LINES_KEY,
|
||||||
|
SHOW_STABLE_PRICE_KEY,
|
||||||
} from 'utils/constants'
|
} from 'utils/constants'
|
||||||
import { breakpoints } from 'utils/theme'
|
import { breakpoints } from 'utils/theme'
|
||||||
import { COLORS } from 'styles/colors'
|
import { COLORS } from 'styles/colors'
|
||||||
|
@ -34,6 +36,7 @@ import { formatNumericValue, getDecimalCount } from 'utils/numbers'
|
||||||
import { BN } from '@project-serum/anchor'
|
import { BN } from '@project-serum/anchor'
|
||||||
import SpotDatafeed from 'apis/birdeye/datafeed'
|
import SpotDatafeed from 'apis/birdeye/datafeed'
|
||||||
import PerpDatafeed from 'apis/mngo/datafeed'
|
import PerpDatafeed from 'apis/mngo/datafeed'
|
||||||
|
import useStablePrice from 'hooks/useStablePrice'
|
||||||
|
|
||||||
export interface ChartContainerProps {
|
export interface ChartContainerProps {
|
||||||
container: ChartingLibraryWidgetOptions['container']
|
container: ChartingLibraryWidgetOptions['container']
|
||||||
|
@ -72,6 +75,14 @@ const TradingViewChart = () => {
|
||||||
const [showOrderLines, toggleShowOrderLines] = useState(
|
const [showOrderLines, toggleShowOrderLines] = useState(
|
||||||
showOrderLinesLocalStorage
|
showOrderLinesLocalStorage
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [showStablePriceLocalStorage, toggleShowStablePriceLocalStorage] =
|
||||||
|
useLocalStorageState(SHOW_STABLE_PRICE_KEY, false)
|
||||||
|
const [showStablePrice, toggleShowStablePrice] = useState(
|
||||||
|
showStablePriceLocalStorage
|
||||||
|
)
|
||||||
|
const stablePrice = useStablePrice()
|
||||||
|
const stablePriceLine = mangoStore((s) => s.tradingView.stablePriceLine)
|
||||||
const selectedMarketName = mangoStore((s) => s.selectedMarket.current?.name)
|
const selectedMarketName = mangoStore((s) => s.selectedMarket.current?.name)
|
||||||
const isMobile = width ? width < breakpoints.sm : false
|
const isMobile = width ? width < breakpoints.sm : false
|
||||||
|
|
||||||
|
@ -233,17 +244,115 @@ const TradingViewChart = () => {
|
||||||
|
|
||||||
tvWidgetRef.current.onChartReady(function () {
|
tvWidgetRef.current.onChartReady(function () {
|
||||||
createOLButton()
|
createOLButton()
|
||||||
|
createStablePriceButton()
|
||||||
if (showOrderLines) {
|
if (showOrderLines) {
|
||||||
const openOrders = mangoStore.getState().mangoAccount.openOrders
|
const openOrders = mangoStore.getState().mangoAccount.openOrders
|
||||||
deleteLines()
|
deleteLines()
|
||||||
drawLinesForMarket(openOrders)
|
drawLinesForMarket(openOrders)
|
||||||
}
|
}
|
||||||
|
if (showStablePrice && stablePrice) {
|
||||||
|
const set = mangoStore.getState().set
|
||||||
|
set((s) => {
|
||||||
|
s.tradingView.stablePriceLine = drawStablePriceLine(stablePrice)
|
||||||
|
})
|
||||||
|
}
|
||||||
setChartReady(true)
|
setChartReady(true)
|
||||||
})
|
})
|
||||||
//eslint-disable-next-line
|
//eslint-disable-next-line
|
||||||
}
|
}
|
||||||
}, [theme, isMobile, defaultProps, spotOrPerp])
|
}, [theme, isMobile, defaultProps, spotOrPerp])
|
||||||
|
|
||||||
|
const createStablePriceButton = () => {
|
||||||
|
const button = tvWidgetRef?.current?.createButton()
|
||||||
|
if (!button) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
button.textContent = 'SP'
|
||||||
|
if (showStablePriceLocalStorage) {
|
||||||
|
button.style.color = COLORS.ACTIVE[theme]
|
||||||
|
} else {
|
||||||
|
button.style.color = COLORS.FGD4[theme]
|
||||||
|
}
|
||||||
|
button.setAttribute('title', t('tv-chart:toggle-stable-price'))
|
||||||
|
button.addEventListener('click', toggleStablePrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStablePrice(this: HTMLElement) {
|
||||||
|
toggleShowStablePrice((prevState: boolean) => !prevState)
|
||||||
|
if (this.style.color === hexToRgb(COLORS.ACTIVE[theme])) {
|
||||||
|
this.style.color = COLORS.FGD4[theme]
|
||||||
|
} else {
|
||||||
|
this.style.color = COLORS.ACTIVE[theme]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showStablePrice !== showStablePriceLocalStorage) {
|
||||||
|
toggleShowStablePriceLocalStorage(showStablePrice)
|
||||||
|
}
|
||||||
|
}, [showStablePrice])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tvWidgetRef.current && chartReady) {
|
||||||
|
if (stablePriceLine) {
|
||||||
|
removeStablePrice(stablePriceLine)
|
||||||
|
}
|
||||||
|
if (showStablePrice && stablePrice) {
|
||||||
|
const set = mangoStore.getState().set
|
||||||
|
set((s) => {
|
||||||
|
s.tradingView.stablePriceLine = drawStablePriceLine(stablePrice)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [stablePrice, showStablePrice, chartReady, tvWidgetRef])
|
||||||
|
|
||||||
|
function drawStablePriceLine(price: number) {
|
||||||
|
if (!tvWidgetRef?.current?.chart()) return
|
||||||
|
const newStablePrice: Map<string, EntityId> = new Map()
|
||||||
|
const now = Date.now() / 1000
|
||||||
|
try {
|
||||||
|
const id = tvWidgetRef.current.chart().createShape(
|
||||||
|
{ time: now, price: price },
|
||||||
|
{
|
||||||
|
shape: 'horizontal_line',
|
||||||
|
overrides: {
|
||||||
|
linecolor: COLORS.FGD4[theme],
|
||||||
|
linestyle: 1,
|
||||||
|
linewidth: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
try {
|
||||||
|
newStablePrice.set(`${now}${price}`, id)
|
||||||
|
} catch (error) {
|
||||||
|
console.log('failed to set stable price line')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('failed to create stable price line')
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
console.log('failed to create stable price line')
|
||||||
|
}
|
||||||
|
return newStablePrice
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeStablePrice = (stablePrice: Map<string, EntityId>) => {
|
||||||
|
if (!tvWidgetRef?.current?.chart()) return
|
||||||
|
const set = mangoStore.getState().set
|
||||||
|
for (const val of stablePrice.values()) {
|
||||||
|
try {
|
||||||
|
tvWidgetRef.current.chart().removeEntity(val)
|
||||||
|
} catch (error) {
|
||||||
|
console.log('stable price could not be removed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set((s) => {
|
||||||
|
s.tradingView.stablePriceLine = new Map()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const createOLButton = () => {
|
const createOLButton = () => {
|
||||||
const button = tvWidgetRef?.current?.createButton()
|
const button = tvWidgetRef?.current?.createButton()
|
||||||
if (!button) {
|
if (!button) {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { I80F48, PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||||
|
import mangoStore from '@store/mangoStore'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import useMangoGroup from './useMangoGroup'
|
||||||
|
import useSelectedMarket from './useSelectedMarket'
|
||||||
|
|
||||||
|
const useStablePrice = () => {
|
||||||
|
const { selectedMarket } = useSelectedMarket()
|
||||||
|
const perpMarkets = mangoStore((s) => s.perpMarkets)
|
||||||
|
const { group } = useMangoGroup()
|
||||||
|
|
||||||
|
const banks = useMemo(() => {
|
||||||
|
if (!group) return []
|
||||||
|
return Array.from(group.banksMapByMint)
|
||||||
|
.map(([mintAddress, banks]) => banks)
|
||||||
|
.map((b) => b[0])
|
||||||
|
}, [group])
|
||||||
|
|
||||||
|
const stablePrice = useMemo(() => {
|
||||||
|
if (!group || !selectedMarket || !banks.length) return 0
|
||||||
|
let stablePrice
|
||||||
|
if (selectedMarket instanceof PerpMarket) {
|
||||||
|
stablePrice = selectedMarket.stablePriceModel.stablePrice || 0
|
||||||
|
} else {
|
||||||
|
const baseBank = banks.find(
|
||||||
|
(b) => b.tokenIndex === selectedMarket.baseTokenIndex
|
||||||
|
)
|
||||||
|
const quoteBank = banks.find(
|
||||||
|
(b) => b.tokenIndex === selectedMarket.quoteTokenIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
const baseStablePrice = group.toUiPrice(
|
||||||
|
I80F48.fromNumber(baseBank!.stablePriceModel.stablePrice),
|
||||||
|
baseBank!.mintDecimals
|
||||||
|
)
|
||||||
|
const quoteStablePrice = group.toUiPrice(
|
||||||
|
I80F48.fromNumber(quoteBank!.stablePriceModel.stablePrice),
|
||||||
|
quoteBank!.mintDecimals
|
||||||
|
)
|
||||||
|
stablePrice = baseStablePrice / quoteStablePrice
|
||||||
|
}
|
||||||
|
return stablePrice
|
||||||
|
}, [banks, group, perpMarkets, selectedMarket])
|
||||||
|
|
||||||
|
return stablePrice
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useStablePrice
|
|
@ -7,6 +7,7 @@
|
||||||
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
||||||
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
||||||
"outside-range": "Order Price Outside Range",
|
"outside-range": "Order Price Outside Range",
|
||||||
|
"toggle-stable-price": "Toggle stable price line",
|
||||||
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
||||||
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
||||||
"toggle-order-line": "Toggle order line visibility",
|
"toggle-order-line": "Toggle order line visibility",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
||||||
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
||||||
"outside-range": "Order Price Outside Range",
|
"outside-range": "Order Price Outside Range",
|
||||||
|
"toggle-stable-price": "Toggle stable price line",
|
||||||
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
||||||
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
||||||
"toggle-order-line": "Toggle order line visibility",
|
"toggle-order-line": "Toggle order line visibility",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
"modify-order-details": "Edit your {{marketName}} order from {{orderSide}} {{orderSize}} at {{currentOrderPrice}} to {{orderSide}} {{orderSize}} at {{updatedOrderPrice}}",
|
||||||
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
"order-details": " ({{orderType}} {{orderSide}}) if price is {{triggerCondition}} {{triggerPrice}}",
|
||||||
"outside-range": "Order Price Outside Range",
|
"outside-range": "Order Price Outside Range",
|
||||||
|
"toggle-stable-price": "Toggle stable price line",
|
||||||
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
"slippage-accept": "Use the trade order form if you wish to accept the potential slippage.",
|
||||||
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
"slippage-warning": "{{updatedOrderPrice}} is greater than 5% {{aboveBelow}} the current market price of {{selectedMarketPrice}}. Executing this trade could incur significant slippage.",
|
||||||
"toggle-order-line": "Toggle order line visibility",
|
"toggle-order-line": "Toggle order line visibility",
|
||||||
|
|
|
@ -10,5 +10,6 @@
|
||||||
"slippage-accept": "若您接受潜在的下滑请使用交易表格进行。",
|
"slippage-accept": "若您接受潜在的下滑请使用交易表格进行。",
|
||||||
"slippage-warning": "您的订单价格({{updatedOrderPrice}})多余5%{{aboveBelow}}市场价格({{selectedMarketPrice}})表是您也许遭受可观的下滑。",
|
"slippage-warning": "您的订单价格({{updatedOrderPrice}})多余5%{{aboveBelow}}市场价格({{selectedMarketPrice}})表是您也许遭受可观的下滑。",
|
||||||
"toggle-order-line": "切换订单线可见性",
|
"toggle-order-line": "切换订单线可见性",
|
||||||
|
"toggle-stable-price": "Toggle stable price line",
|
||||||
"toggle-trade-executions": "Toggle trade execution visibility"
|
"toggle-trade-executions": "Toggle trade execution visibility"
|
||||||
}
|
}
|
|
@ -10,5 +10,6 @@
|
||||||
"slippage-accept": "若您接受潛在的下滑請使用交易表格進行。",
|
"slippage-accept": "若您接受潛在的下滑請使用交易表格進行。",
|
||||||
"slippage-warning": "您的訂單價格({{updatedOrderPrice}})多餘5%{{aboveBelow}}市場價格({{selectedMarketPrice}})表是您也許遭受可觀的下滑。",
|
"slippage-warning": "您的訂單價格({{updatedOrderPrice}})多餘5%{{aboveBelow}}市場價格({{selectedMarketPrice}})表是您也許遭受可觀的下滑。",
|
||||||
"toggle-order-line": "切換訂單線可見性",
|
"toggle-order-line": "切換訂單線可見性",
|
||||||
|
"toggle-stable-price": "Toggle stable price line",
|
||||||
"toggle-trade-executions": "Toggle trade execution visibility"
|
"toggle-trade-executions": "Toggle trade execution visibility"
|
||||||
}
|
}
|
|
@ -49,7 +49,10 @@ import spotBalancesUpdater from './spotBalancesUpdater'
|
||||||
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
|
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
|
||||||
import perpPositionsUpdater from './perpPositionsUpdater'
|
import perpPositionsUpdater from './perpPositionsUpdater'
|
||||||
import { DEFAULT_PRIORITY_FEE } from '@components/settings/RpcSettings'
|
import { DEFAULT_PRIORITY_FEE } from '@components/settings/RpcSettings'
|
||||||
import { IOrderLineAdapter } from '@public/charting_library/charting_library'
|
import {
|
||||||
|
EntityId,
|
||||||
|
IOrderLineAdapter,
|
||||||
|
} from '@public/charting_library/charting_library'
|
||||||
|
|
||||||
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
|
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
|
||||||
|
|
||||||
|
@ -333,6 +336,7 @@ export type MangoStore = {
|
||||||
}
|
}
|
||||||
tradeForm: TradeForm
|
tradeForm: TradeForm
|
||||||
tradingView: {
|
tradingView: {
|
||||||
|
stablePriceLine: Map<string, EntityId> | undefined
|
||||||
orderLines: Map<string | BN, IOrderLineAdapter>
|
orderLines: Map<string | BN, IOrderLineAdapter>
|
||||||
}
|
}
|
||||||
wallet: {
|
wallet: {
|
||||||
|
@ -480,6 +484,7 @@ const mangoStore = create<MangoStore>()(
|
||||||
},
|
},
|
||||||
tradeForm: DEFAULT_TRADE_FORM,
|
tradeForm: DEFAULT_TRADE_FORM,
|
||||||
tradingView: {
|
tradingView: {
|
||||||
|
stablePriceLine: new Map(),
|
||||||
orderLines: new Map(),
|
orderLines: new Map(),
|
||||||
},
|
},
|
||||||
wallet: {
|
wallet: {
|
||||||
|
|
|
@ -45,6 +45,8 @@ export const RPC_PROVIDER_KEY = 'rpcProviderKey-0.4'
|
||||||
|
|
||||||
export const PRIORITY_FEE_KEY = 'priorityFeeKey-0.1'
|
export const PRIORITY_FEE_KEY = 'priorityFeeKey-0.1'
|
||||||
|
|
||||||
|
export const SHOW_STABLE_PRICE_KEY = 'showStablePriceKey-0.1'
|
||||||
|
|
||||||
export const SHOW_ORDER_LINES_KEY = 'showOrderLines-0.1'
|
export const SHOW_ORDER_LINES_KEY = 'showOrderLines-0.1'
|
||||||
|
|
||||||
// Unused
|
// Unused
|
||||||
|
|
Loading…
Reference in New Issue