Merge pull request #85 from blockworks-foundation/stable-price-tv-line

add stable price line to tv chart
This commit is contained in:
tylersssss 2023-02-15 11:19:09 -05:00 committed by GitHub
commit d713692dc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 170 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import {
IChartingLibraryWidget,
ResolutionString,
IOrderLineAdapter,
EntityId,
} from '@public/charting_library'
import mangoStore from '@store/mangoStore'
import { useViewport } from 'hooks/useViewport'
@ -13,6 +14,7 @@ import {
CHART_DATA_FEED,
DEFAULT_MARKET_NAME,
SHOW_ORDER_LINES_KEY,
SHOW_STABLE_PRICE_KEY,
} from 'utils/constants'
import { breakpoints } from 'utils/theme'
import { COLORS } from 'styles/colors'
@ -34,6 +36,7 @@ import { formatNumericValue, getDecimalCount } from 'utils/numbers'
import { BN } from '@project-serum/anchor'
import SpotDatafeed from 'apis/birdeye/datafeed'
import PerpDatafeed from 'apis/mngo/datafeed'
import useStablePrice from 'hooks/useStablePrice'
export interface ChartContainerProps {
container: ChartingLibraryWidgetOptions['container']
@ -72,6 +75,14 @@ const TradingViewChart = () => {
const [showOrderLines, toggleShowOrderLines] = useState(
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 isMobile = width ? width < breakpoints.sm : false
@ -233,17 +244,115 @@ const TradingViewChart = () => {
tvWidgetRef.current.onChartReady(function () {
createOLButton()
createStablePriceButton()
if (showOrderLines) {
const openOrders = mangoStore.getState().mangoAccount.openOrders
deleteLines()
drawLinesForMarket(openOrders)
}
if (showStablePrice && stablePrice) {
const set = mangoStore.getState().set
set((s) => {
s.tradingView.stablePriceLine = drawStablePriceLine(stablePrice)
})
}
setChartReady(true)
})
//eslint-disable-next-line
}
}, [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 button = tvWidgetRef?.current?.createButton()
if (!button) {

48
hooks/useStablePrice.ts Normal file
View File

@ -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

View File

@ -7,6 +7,7 @@
"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}}",
"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-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",

View File

@ -7,6 +7,7 @@
"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}}",
"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-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",

View File

@ -7,6 +7,7 @@
"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}}",
"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-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",

View File

@ -10,5 +10,6 @@
"slippage-accept": "若您接受潜在的下滑请使用交易表格进行。",
"slippage-warning": "您的订单价格({{updatedOrderPrice}})多余5%{{aboveBelow}}市场价格({{selectedMarketPrice}})表是您也许遭受可观的下滑。",
"toggle-order-line": "切换订单线可见性",
"toggle-stable-price": "Toggle stable price line",
"toggle-trade-executions": "Toggle trade execution visibility"
}

View File

@ -10,5 +10,6 @@
"slippage-accept": "若您接受潛在的下滑請使用交易表格進行。",
"slippage-warning": "您的訂單價格({{updatedOrderPrice}})多餘5%{{aboveBelow}}市場價格({{selectedMarketPrice}})表是您也許遭受可觀的下滑。",
"toggle-order-line": "切換訂單線可見性",
"toggle-stable-price": "Toggle stable price line",
"toggle-trade-executions": "Toggle trade execution visibility"
}

View File

@ -49,7 +49,10 @@ import spotBalancesUpdater from './spotBalancesUpdater'
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
import perpPositionsUpdater from './perpPositionsUpdater'
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')
@ -333,6 +336,7 @@ export type MangoStore = {
}
tradeForm: TradeForm
tradingView: {
stablePriceLine: Map<string, EntityId> | undefined
orderLines: Map<string | BN, IOrderLineAdapter>
}
wallet: {
@ -480,6 +484,7 @@ const mangoStore = create<MangoStore>()(
},
tradeForm: DEFAULT_TRADE_FORM,
tradingView: {
stablePriceLine: new Map(),
orderLines: new Map(),
},
wallet: {

View File

@ -45,6 +45,8 @@ export const RPC_PROVIDER_KEY = 'rpcProviderKey-0.4'
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'
// Unused