Merge pull request #82 from blockworks-foundation/tv-orders

add order lines to trading view chart
This commit is contained in:
tylersssss 2023-02-13 10:50:54 -05:00 committed by GitHub
commit 7b3578a9b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 520 additions and 53 deletions

View File

@ -1,16 +1,37 @@
import { useEffect, useRef, useMemo, useState } from 'react'
import { useEffect, useRef, useMemo, useState, useCallback } from 'react'
import { useTheme } from 'next-themes'
import {
widget,
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
IOrderLineAdapter,
} from '@public/charting_library'
import mangoStore from '@store/mangoStore'
import { useViewport } from 'hooks/useViewport'
import { CHART_DATA_FEED, DEFAULT_MARKET_NAME } from 'utils/constants'
import {
CHART_DATA_FEED,
DEFAULT_MARKET_NAME,
SHOW_ORDER_LINES_KEY,
} from 'utils/constants'
import { breakpoints } from 'utils/theme'
import { COLORS } from 'styles/colors'
import { useTranslation } from 'next-i18next'
import { notify } from 'utils/notifications'
import {
PerpMarket,
PerpOrder,
PerpOrderType,
Serum3Market,
Serum3OrderType,
Serum3SelfTradeBehavior,
Serum3Side,
} from '@blockworks-foundation/mango-v4'
import { Order } from '@project-serum/serum/lib/market'
import { PublicKey } from '@solana/web3.js'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { formatNumericValue, getDecimalCount } from 'utils/numbers'
import { BN } from '@project-serum/anchor'
import SpotDatafeed from 'apis/birdeye/datafeed'
import PerpDatafeed from 'apis/mngo/datafeed'
@ -30,12 +51,27 @@ export interface ChartContainerProps {
theme: string
}
function hexToRgb(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(
result[3],
16
)})`
: null
}
const TradingViewChart = () => {
const { t } = useTranslation(['tv-chart', 'trade'])
const { theme } = useTheme()
const { width } = useViewport()
const [chartReady, setChartReady] = useState(false)
const [spotOrPerp, setSpotOrPerp] = useState('spot')
const [showOrderLinesLocalStorage, toggleShowOrderLinesLocalStorage] =
useLocalStorageState(SHOW_ORDER_LINES_KEY, true)
const [showOrderLines, toggleShowOrderLines] = useState(
showOrderLinesLocalStorage
)
const selectedMarketName = mangoStore((s) => s.selectedMarket.current?.name)
const isMobile = width ? width < breakpoints.sm : false
@ -183,26 +219,7 @@ const TradingViewChart = () => {
: 'Dark',
custom_css_url: '/styles/tradingview.css',
loading_screen: {
backgroundColor:
theme === 'Dark'
? COLORS.BKG1.Dark
: theme === 'Light'
? COLORS.BKG1.Light
: theme === 'Mango Classic'
? COLORS.BKG1['Mango Classic']
: theme === 'Medium'
? COLORS.BKG1.Medium
: theme === 'Avocado'
? COLORS.BKG1.Avocado
: theme === 'Blueberry'
? COLORS.BKG1.Blueberry
: theme === 'Banana'
? COLORS.BKG1.Banana
: theme === 'Lychee'
? COLORS.BKG1.Lychee
: theme === 'Olive'
? COLORS.BKG1.Olive
: COLORS.BKG1['High Contrast'],
backgroundColor: COLORS.BKG1[theme],
},
overrides: {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
@ -215,12 +232,437 @@ const TradingViewChart = () => {
tvWidgetRef.current = tvWidget
tvWidgetRef.current.onChartReady(function () {
createOLButton()
if (showOrderLines) {
const openOrders = mangoStore.getState().mangoAccount.openOrders
deleteLines()
drawLinesForMarket(openOrders)
}
setChartReady(true)
})
//eslint-disable-next-line
}
}, [theme, isMobile, defaultProps, spotOrPerp])
const createOLButton = () => {
const button = tvWidgetRef?.current?.createButton()
if (!button) {
return
}
button.textContent = 'OL'
if (showOrderLinesLocalStorage) {
button.style.color = COLORS.ACTIVE[theme]
} else {
button.style.color = COLORS.FGD4[theme]
}
button.setAttribute('title', t('tv-chart:toggle-order-line'))
button.addEventListener('click', toggleOrderLines)
}
function toggleOrderLines(this: HTMLElement) {
toggleShowOrderLines((prevState: boolean) => !prevState)
if (this.style.color === hexToRgb(COLORS.ACTIVE[theme])) {
deleteLines()
this.style.color = COLORS.FGD4[theme]
} else {
const openOrders = mangoStore.getState().mangoAccount.openOrders
drawLinesForMarket(openOrders)
this.style.color = COLORS.ACTIVE[theme]
}
}
useEffect(() => {
if (showOrderLines !== showOrderLinesLocalStorage) {
toggleShowOrderLinesLocalStorage(showOrderLines)
}
}, [showOrderLines])
// update order lines if a user's open orders change
useEffect(() => {
let subscription
if (chartReady && tvWidgetRef?.current) {
subscription = mangoStore.subscribe(
(state) => state.mangoAccount.openOrders,
(openOrders) => {
const orderLines = mangoStore.getState().tradingView.orderLines
tvWidgetRef.current?.onChartReady(() => {
let matchingOrderLines = 0
let openOrdersForMarket = 0
const oOrders = Object.entries(openOrders).map(
([marketPk, orders]) => ({
orders,
marketPk,
})
)
for (const [key] of orderLines) {
oOrders?.forEach(({ orders }) => {
for (const order of orders) {
if (order.orderId == key) {
matchingOrderLines += 1
}
}
})
}
const selectedMarket = mangoStore.getState().selectedMarket.current
const selectedMarketPk =
selectedMarket instanceof Serum3Market
? selectedMarket?.serumMarketExternal.toString()
: selectedMarket?.publicKey.toString()
oOrders?.forEach(({ marketPk }) => {
if (marketPk === selectedMarketPk) {
openOrdersForMarket += 1
}
})
tvWidgetRef.current?.activeChart().dataReady(() => {
if (
(showOrderLines &&
matchingOrderLines !== openOrdersForMarket) ||
orderLines?.size != matchingOrderLines
) {
deleteLines()
drawLinesForMarket(openOrders)
}
})
})
}
)
}
return subscription
}, [chartReady, showOrderLines])
const drawLinesForMarket = (
openOrders: Record<string, Order[] | PerpOrder[]>
) => {
const set = mangoStore.getState().set
const newOrderLines = new Map()
const oOrders = Object.entries(openOrders).map(([marketPk, orders]) => ({
orders,
marketPk,
}))
if (oOrders?.length) {
const selectedMarket = mangoStore.getState().selectedMarket.current
const selectedMarketPk =
selectedMarket instanceof Serum3Market
? selectedMarket?.serumMarketExternal.toString()
: selectedMarket?.publicKey.toString()
for (const { orders, marketPk } of oOrders) {
if (marketPk === selectedMarketPk) {
for (const order of orders) {
newOrderLines.set(order.orderId.toString(), drawLine(order))
}
}
}
}
set((state) => {
state.tradingView.orderLines = newOrderLines
})
}
const deleteLines = () => {
const set = mangoStore.getState().set
const orderLines = mangoStore.getState().tradingView.orderLines
if (orderLines.size > 0) {
orderLines?.forEach((value: IOrderLineAdapter, key: string | BN) => {
orderLines.get(key)?.remove()
})
set((state) => {
state.tradingView.orderLines = new Map()
})
}
}
function getOrderDecimals() {
const selectedMarket = mangoStore.getState().selectedMarket.current
let minOrderDecimals = 4
let tickSizeDecimals = 2
if (!selectedMarket) return [minOrderDecimals, tickSizeDecimals]
if (selectedMarket instanceof PerpMarket) {
minOrderDecimals = getDecimalCount(selectedMarket.minOrderSize)
tickSizeDecimals = getDecimalCount(selectedMarket.tickSize)
} else {
const group = mangoStore.getState().group
const market = group?.getSerum3ExternalMarket(
selectedMarket.serumMarketExternal
)
if (market) {
minOrderDecimals = getDecimalCount(market.minOrderSize)
tickSizeDecimals = getDecimalCount(market.tickSize)
}
}
return [minOrderDecimals, tickSizeDecimals]
}
function drawLine(order: Order | PerpOrder) {
const side =
typeof order.side === 'string'
? t(order.side)
: 'bid' in order.side
? t('trade:long')
: t('trade:short')
const isLong = side === 'buy' || side === 'long'
const isShort = side === 'sell' || side === 'short'
const [minOrderDecimals, tickSizeDecimals] = getOrderDecimals()
const orderSizeUi: string = formatNumericValue(order.size, minOrderDecimals)
if (!tvWidgetRef?.current?.chart()) return
return (
tvWidgetRef.current
.chart()
.createOrderLine({ disableUndo: false })
.onMove(function (this: IOrderLineAdapter) {
const currentOrderPrice = order.price
const updatedOrderPrice = this.getPrice()
const selectedMarketPrice =
mangoStore.getState().selectedMarket.markPrice
if (
(isLong && updatedOrderPrice > 1.05 * selectedMarketPrice) ||
(isShort && updatedOrderPrice < 0.95 * selectedMarketPrice)
) {
tvWidgetRef.current?.showNoticeDialog({
title: t('tv-chart:outside-range'),
body:
t('tv-chart:slippage-warning', {
updatedOrderPrice: updatedOrderPrice,
aboveBelow:
side == 'buy' || side === 'long' ? t('above') : t('below'),
selectedMarketPrice: selectedMarketPrice,
}) +
'<p><p>' +
t('tv-chart:slippage-accept'),
callback: () => {
this.setPrice(currentOrderPrice)
},
})
} else {
tvWidgetRef.current?.showConfirmDialog({
title: t('tv-chart:modify-order'),
body: t('tv-chart:modify-order-details', {
marketName: selectedMarketName,
orderSize: orderSizeUi,
orderSide: side.toUpperCase(),
currentOrderPrice: formatNumericValue(
currentOrderPrice,
tickSizeDecimals
),
updatedOrderPrice: formatNumericValue(
updatedOrderPrice,
tickSizeDecimals
),
}),
callback: (res) => {
if (res) {
modifyOrder(order, updatedOrderPrice)
} else {
this.setPrice(currentOrderPrice)
}
},
})
}
})
.onCancel(function () {
tvWidgetRef.current?.showConfirmDialog({
title: t('tv-chart:cancel-order'),
body: t('tv-chart:cancel-order-details', {
marketName: selectedMarketName,
orderSize: orderSizeUi,
orderSide: side.toUpperCase(),
orderPrice: formatNumericValue(order.price, tickSizeDecimals),
}),
callback: (res) => {
if (res) {
if (order instanceof PerpOrder) {
cancelPerpOrder(order)
} else {
cancelSpotOrder(order)
}
}
},
})
})
.setPrice(order.price)
.setQuantity(orderSizeUi)
.setText(side.toUpperCase())
// .setTooltip(
// order.perpTrigger?.clientOrderId
// ? `${order.orderType} Order #: ${order.orderId}`
// : `Order #: ${order.orderId}`
// )
.setBodyTextColor(isLong ? COLORS.UP[theme] : COLORS.DOWN[theme])
.setQuantityTextColor(isLong ? COLORS.UP[theme] : COLORS.DOWN[theme])
.setCancelButtonIconColor(COLORS.FGD4[theme])
.setBodyBorderColor(isLong ? COLORS.UP[theme] : COLORS.DOWN[theme])
.setQuantityBorderColor(isLong ? COLORS.UP[theme] : COLORS.DOWN[theme])
.setCancelButtonBorderColor(
isLong ? COLORS.UP[theme] : COLORS.DOWN[theme]
)
.setBodyBackgroundColor(COLORS.BKG1[theme])
.setQuantityBackgroundColor(COLORS.BKG1[theme])
.setCancelButtonBackgroundColor(COLORS.BKG1[theme])
.setLineColor(isLong ? COLORS.UP[theme] : COLORS.DOWN[theme])
.setLineLength(3)
.setLineWidth(1)
.setLineStyle(1)
)
}
const modifyOrder = useCallback(
async (o: PerpOrder | Order, price: number) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
const baseSize = o.size
if (!group || !mangoAccount) return
try {
let tx = ''
if (o instanceof PerpOrder) {
tx = await client.modifyPerpOrder(
group,
mangoAccount,
o.perpMarketIndex,
o.orderId,
o.side,
price,
Math.abs(baseSize),
undefined, // maxQuoteQuantity
Date.now(),
PerpOrderType.limit,
undefined,
undefined
)
} else {
const marketPk = findSerum3MarketPkInOpenOrders(o)
if (!marketPk) return
const market = group.getSerum3MarketByExternalMarket(
new PublicKey(marketPk)
)
tx = await client.modifySerum3Order(
group,
o.orderId,
mangoAccount,
market.serumMarketExternal,
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
price,
baseSize,
Serum3SelfTradeBehavior.decrementTake,
Serum3OrderType.limit,
Date.now(),
10
)
}
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
} catch (e: any) {
console.error('Error canceling', e)
notify({
title: 'Unable to modify order',
description: e.message,
txid: e.txid,
type: 'error',
})
}
},
[t]
)
const cancelSpotOrder = useCallback(
async (o: Order) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
if (!group || !mangoAccount) return
const marketPk = findSerum3MarketPkInOpenOrders(o)
if (!marketPk) return
const market = group.getSerum3MarketByExternalMarket(
new PublicKey(marketPk)
)
try {
const tx = await client.serum3CancelOrder(
group,
mangoAccount,
market!.serumMarketExternal,
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
o.orderId
)
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
} catch (e: any) {
console.error('Error canceling', e)
notify({
title: t('trade:cancel-order-error'),
description: e.message,
txid: e.txid,
type: 'error',
})
}
},
[t]
)
const cancelPerpOrder = useCallback(
async (o: PerpOrder) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
if (!group || !mangoAccount) return
try {
const tx = await client.perpCancelOrder(
group,
mangoAccount,
o.perpMarketIndex,
o.orderId
)
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
} catch (e: any) {
console.error('Error canceling', e)
notify({
title: t('trade:cancel-order-error'),
description: e.message,
txid: e.txid,
type: 'error',
})
}
},
[t]
)
const findSerum3MarketPkInOpenOrders = (o: Order): string | undefined => {
const openOrders = mangoStore.getState().mangoAccount.openOrders
let foundedMarketPk: string | undefined = undefined
for (const [marketPk, orders] of Object.entries(openOrders)) {
for (const order of orders) {
if (order.orderId.eq(o.orderId)) {
foundedMarketPk = marketPk
break
}
}
if (foundedMarketPk) {
break
}
}
return foundedMarketPk
}
return (
<div id={defaultProps.container as string} className="tradingview-chart" />
)

View File

@ -23,6 +23,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'settings',
'trade',
'close-account',
'tv-chart',
])),
},
}

View File

@ -22,6 +22,7 @@
"instantaneous-funding": "Instantaneous Funding",
"interval-seconds": "Interval (seconds)",
"limit-price": "Limit Price",
"long": "Long",
"margin": "Margin",
"no-balances": "No balances",
"no-orders": "No open orders",
@ -46,6 +47,7 @@
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
"short": "Short",
"show-asks": "Show Asks",
"show-bids": "Show Bids",
"side": "Side",

View File

@ -1,13 +1,14 @@
{
"advanced-order": "Advanced Order Type",
"advanced-order-details": "Advanced order types in the chart window may only be cancelled. If new conditions are required, please cancel this order and use the Advanced Trade Form.",
"cancel-order": "Cancel Your Order?",
"cancel-order-details": "Would you like to cancel your order for {{orderSize}} {{baseSymbol}} {{orderSide}} at ${{orderPrice}}?",
"modify-order": "Modify Your Order?",
"modify-order-details": "Would you like to change your order from a {{orderSize}} {{baseSymbol}} {{orderSide}} at ${{currentOrderPrice}} to a {{orderSize}} {{baseSymbol}} LIMIT {{orderSide}} at ${{updatedOrderPrice}}?",
"advanced-order-details": "Advanced order types in the chart window may only be cancelled. If new conditions are required, cancel this order and use the trade order form.",
"cancel-order": "Cancel Order",
"cancel-order-details": "Cancel your order for {{orderSide}} {{orderSize}} {{marketName}} at {{orderPrice}}",
"modify-order": "Modify Order",
"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",
"slippage-accept": "Please use the trade input form if you wish to accept the potential slippage.",
"slippage-warning": "Your order price ({{updatedOrderPrice}}) is greater than 5% {{aboveBelow}} the current market price ({{selectedMarketPrice}}) indicating you might incur significant slippage.",
"toggle-order-line": "Toggle order line visibility"
"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",
"toggle-trade-executions": "Toggle trade execution visibility"
}

View File

@ -22,6 +22,7 @@
"instantaneous-funding": "Instantaneous Funding",
"interval-seconds": "Interval (seconds)",
"limit-price": "Limit Price",
"long": "Long",
"margin": "Margin",
"no-balances": "No balances",
"no-orders": "No open orders",
@ -46,6 +47,7 @@
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
"short": "Short",
"show-asks": "Show Asks",
"show-bids": "Show Bids",
"side": "Side",

View File

@ -1,13 +1,14 @@
{
"advanced-order": "Advanced Order Type",
"advanced-order-details": "Advanced order types in the chart window may only be cancelled. If new conditions are required, please cancel this order and use the Advanced Trade Form.",
"cancel-order": "Cancel Your Order?",
"cancel-order-details": "Would you like to cancel your order for {{orderSize}} {{baseSymbol}} {{orderSide}} at ${{orderPrice}}?",
"modify-order": "Modify Your Order?",
"modify-order-details": "Would you like to change your order from a {{orderSize}} {{baseSymbol}} {{orderSide}} at ${{currentOrderPrice}} to a {{orderSize}} {{baseSymbol}} LIMIT {{orderSide}} at ${{updatedOrderPrice}}?",
"advanced-order-details": "Advanced order types in the chart window may only be cancelled. If new conditions are required, cancel this order and use the trade order form.",
"cancel-order": "Cancel Order",
"cancel-order-details": "Cancel your order for {{orderSide}} {{orderSize}} {{marketName}} at {{orderPrice}}",
"modify-order": "Modify Order",
"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",
"slippage-accept": "Please use the trade input form if you wish to accept the potential slippage.",
"slippage-warning": "Your order price ({{updatedOrderPrice}}) is greater than 5% {{aboveBelow}} the current market price ({{selectedMarketPrice}}) indicating you might incur significant slippage.",
"toggle-order-line": "Toggle order line visibility"
"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",
"toggle-trade-executions": "Toggle trade execution visibility"
}

View File

@ -22,6 +22,7 @@
"instantaneous-funding": "Instantaneous Funding",
"interval-seconds": "Interval (seconds)",
"limit-price": "Limit Price",
"long": "Long",
"margin": "Margin",
"no-balances": "No balances",
"no-orders": "No open orders",
@ -46,6 +47,7 @@
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
"short": "Short",
"show-asks": "Show Asks",
"show-bids": "Show Bids",
"side": "Side",

View File

@ -1,13 +1,14 @@
{
"advanced-order": "高級訂單類型",
"advanced-order-details": "在圖表窗口中高級訂單類型只能取消。如果需要新條件,請取消此訂單並使用高級交易表格進行。",
"cancel-order": "取消訂單嗎?",
"cancel-order-details": "您確定要取消{{orderSize}} {{baseSymbol}} {{orderSide}} 價格${{orderPrice}}的掛單嗎?",
"modify-order": "改您的訂單嗎?",
"modify-order-details": "您確定要把{{orderSize}} {{baseSymbol}}{{orderSide}} 價格${{currentOrderPrice}}的掛單改成{{orderSize}} {{baseSymbol}}限價{{orderSide}} 價格${{updatedOrderPrice}}嗎?",
"order-details": "({{orderType}}{{orderSide}})若價格{{triggerCondition}}{{triggerPrice}}",
"outside-range": "訂單價格在範圍之外",
"slippage-accept": "若您接受潛在的下滑請使用交易表格進行。",
"slippage-warning": "您的訂單價格({{updatedOrderPrice}})多餘5%{{aboveBelow}}市場價格({{selectedMarketPrice}})表是您也許遭受可觀的下滑。",
"toggle-order-line": "切換訂單線可見性"
"advanced-order": "Advanced Order Type",
"advanced-order-details": "Advanced order types in the chart window may only be cancelled. If new conditions are required, cancel this order and use the trade order form.",
"cancel-order": "Cancel Order",
"cancel-order-details": "Cancel your order for {{orderSide}} {{orderSize}} {{marketName}} at {{orderPrice}}",
"modify-order": "Modify Order",
"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",
"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",
"toggle-trade-executions": "Toggle trade execution visibility"
}

View File

@ -22,6 +22,7 @@
"instantaneous-funding": "Instantaneous Funding",
"interval-seconds": "Interval (seconds)",
"limit-price": "Limit Price",
"long": "Long",
"margin": "Margin",
"no-balances": "No balances",
"no-orders": "No open orders",
@ -45,6 +46,7 @@
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
"short": "Short",
"show-asks": "Show Asks",
"show-bids": "Show Bids",
"side": "Side",

View File

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

View File

@ -22,6 +22,7 @@
"instantaneous-funding": "Instantaneous Funding",
"interval-seconds": "Interval (seconds)",
"limit-price": "Limit Price",
"long": "Long",
"margin": "Margin",
"no-balances": "No balances",
"no-orders": "No open orders",
@ -45,6 +46,7 @@
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
"short": "Short",
"show-asks": "Show Asks",
"show-bids": "Show Bids",
"side": "Side",

View File

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

View File

@ -2,7 +2,7 @@ import dayjs from 'dayjs'
import produce from 'immer'
import create from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
import { AnchorProvider, BN, Wallet, web3 } from '@project-serum/anchor'
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
import { Orderbook } from '@project-serum/serum'
@ -49,6 +49,7 @@ 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'
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
@ -329,6 +330,9 @@ export type MangoStore = {
data: TokenStatsItem[] | null
}
tradeForm: TradeForm
tradingView: {
orderLines: Map<string | BN, IOrderLineAdapter>
}
wallet: {
tokens: TokenAccount[]
nfts: {
@ -473,6 +477,9 @@ const mangoStore = create<MangoStore>()(
data: [],
},
tradeForm: DEFAULT_TRADE_FORM,
tradingView: {
orderLines: new Map(),
},
wallet: {
tokens: [],
nfts: {

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_ORDER_LINES_KEY = 'showOrderLines-0.1'
// Unused
export const PROFILE_CATEGORIES = [
'borrower',