errors and translations

This commit is contained in:
saml33 2023-06-24 22:44:15 +10:00
parent 6337b696e7
commit a8fd16eb29
9 changed files with 263 additions and 132 deletions

View File

@ -2,7 +2,7 @@ import ButtonGroup from '@components/forms/ButtonGroup'
import Checkbox from '@components/forms/Checkbox'
import Input from '@components/forms/Input'
import Label from '@components/forms/Label'
import Button, { IconButton, LinkButton } from '@components/shared/Button'
import Button, { IconButton } from '@components/shared/Button'
import InlineNotification from '@components/shared/InlineNotification'
import Modal from '@components/shared/Modal'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
@ -28,7 +28,7 @@ export type HotKey = {
}
const HotKeysSettings = () => {
const { t } = useTranslation('settings')
const { t } = useTranslation(['common', 'settings', 'trade'])
const [hotKeys, setHotKeys] = useLocalStorageState(HOT_KEYS_KEY, [])
const [showHotKeyModal, setShowHotKeyModal] = useState(false)
@ -39,25 +39,27 @@ const HotKeysSettings = () => {
return (
<>
<div className="flex items-center justify-between">
<h2 className="mb-1 text-base">{t('hot-keys')}</h2>
<div className="mb-4 flex items-center justify-between">
<div>
<h2 className="mb-1 text-base">{t('settings:hot-keys')}</h2>
<p>{t('settings:hot-keys-desc')}</p>
</div>
{hotKeys.length ? (
<LinkButton onClick={() => setShowHotKeyModal(true)}>
{t('create-new-key')}
</LinkButton>
<Button onClick={() => setShowHotKeyModal(true)} secondary>
{t('settings:new-hot-key')}
</Button>
) : null}
</div>
<p className="mb-4">{t('hot-keys-desc')}</p>
{hotKeys.length ? (
<Table>
<thead>
<TrHead>
<Th className="text-left">{t('key')}</Th>
<Th className="text-right">{t('order-type')}</Th>
<Th className="text-right">{t('side')}</Th>
<Th className="text-right">{t('size')}</Th>
<Th className="text-left">{t('settings:key-sequence')}</Th>
<Th className="text-right">{t('trade:order-type')}</Th>
<Th className="text-right">{t('trade:side')}</Th>
<Th className="text-right">{t('trade:size')}</Th>
<Th className="text-right">{t('price')}</Th>
<Th className="text-right">{t('options')}</Th>
<Th className="text-right">{t('settings:options')}</Th>
<Th />
</TrHead>
</thead>
@ -77,9 +79,15 @@ const HotKeysSettings = () => {
} = hk
const size =
orderSizeType === 'percentage'
? `${orderSize}% of max`
? t('settings:percentage-of-max', { size: orderSize })
: `$${orderSize}`
const price = orderPrice ? `${orderPrice}% from oracle` : 'market'
const price = orderPrice
? `${orderPrice}% ${
orderSide === 'buy'
? t('settings:below')
: t('settings:above')
} oracle`
: t('trade:market')
const options = {
margin: margin,
@ -91,14 +99,16 @@ const HotKeysSettings = () => {
return (
<TrBody key={keySequence} className="text-right">
<Td className="text-left">{keySequence}</Td>
<Td className="text-right">{orderType}</Td>
<Td className="text-right">{orderSide}</Td>
<Td className="text-right">{t(`trade:${orderType}`)}</Td>
<Td className="text-right">{t(orderSide)}</Td>
<Td className="text-right">{size}</Td>
<Td className="text-right">{price}</Td>
<Td className="text-right">
{Object.entries(options).map((e) => {
return e[1]
? `${e[0] !== 'margin' ? ', ' : ''}${e[0]}`
? `${e[0] !== 'margin' ? ', ' : ''}${t(
`trade:${e[0]}`
)}`
: ''
})}
</Td>
@ -121,9 +131,11 @@ const HotKeysSettings = () => {
<div className="rounded-lg border border-th-bkg-3 p-6">
<div className="flex flex-col items-center">
<KeyIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
<p className="mb-4">{t('no-hot-keys')}</p>
<p className="mb-4">{t('settings:no-hot-keys')}</p>
<Button onClick={() => setShowHotKeyModal(true)}>
<div className="flex items-center">{t('create-hot-key')}</div>
<div className="flex items-center">
{t('settings:new-hot-key')}
</div>
</Button>
</div>
</div>
@ -171,7 +183,7 @@ const DEFAULT_FORM_VALUES: HotKeyForm = {
}
const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
const { t } = useTranslation(['settings', 'trade'])
const { t } = useTranslation(['common', 'settings', 'trade'])
const [hotKeys, setHotKeys] = useLocalStorageState<HotKey[]>(HOT_KEYS_KEY, [])
const [hotKeyForm, setHotKeyForm] = useState<HotKeyForm>({
...DEFAULT_FORM_VALUES,
@ -207,10 +219,10 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
for (const key of triggerKey) {
const value = form[key] as string
if (value.length > 1) {
invalidFields[key] = t('error-too-many-characters')
invalidFields[key] = t('settings:error-too-many-characters')
}
if (!alphanumericRegex.test(value)) {
invalidFields[key] = t('error-alphanumeric-only')
invalidFields[key] = t('settings:error-alphanumeric-only')
}
}
for (const key of requiredFields) {
@ -219,10 +231,10 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
if (hotKeyForm.orderType === 'market') {
console.log(key, invalidFields[key])
if (key !== 'price') {
invalidFields[key] = t('error-required-field')
invalidFields[key] = t('settings:error-required-field')
}
} else {
invalidFields[key] = t('error-required-field')
invalidFields[key] = t('settings:error-required-field')
}
}
}
@ -230,17 +242,17 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
const value = form[key] as string
if (value) {
if (isNaN(parseFloat(value))) {
invalidFields[key] = t('error-must-be-number')
invalidFields[key] = t('settings:error-must-be-number')
}
if (parseFloat(value) < 0) {
invalidFields[key] = t('error-must-be-above-zero')
invalidFields[key] = t('settings:error-must-be-above-zero')
}
if (parseFloat(value) > 100) {
if (key === 'price') {
invalidFields[key] = t('error-must-be-below-100')
invalidFields[key] = t('settings:error-must-be-below-100')
} else {
if (hotKeyForm.sizeType === 'percentage') {
invalidFields[key] = t('error-must-be-below-100')
invalidFields[key] = t('settings:error-must-be-below-100')
}
}
}
@ -276,9 +288,9 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<>
<h2 className="mb-4 text-center">{t('create-hot-key')}</h2>
<h2 className="mb-4 text-center">{t('settings:new-hot-key')}</h2>
<div className="mb-4">
<Label text={t('base-key')} />
<Label text={t('settings:base-key')} />
<ButtonGroup
activeValue={hotKeyForm.baseKey}
onChange={(key) => handleSetForm('baseKey', key)}
@ -286,7 +298,7 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
/>
</div>
<div className="mb-4">
<Label text={t('trigger-key')} />
<Label text={t('settings:trigger-key')} />
<Input
hasError={formErrors.triggerKey !== undefined}
type="text"
@ -305,32 +317,43 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
) : null}
</div>
<div className="mb-4">
<Label text={t('order-side')} />
<Label text={t('settings:order-side')} />
<ButtonGroup
activeValue={hotKeyForm.side}
names={[t('buy'), t('sell')]}
onChange={(side) => handleSetForm('side', side)}
values={['buy', 'sell']}
/>
</div>
<div className="mb-4">
<Label text={t('order-type')} />
<Label text={t('trade:order-type')} />
<ButtonGroup
activeValue={hotKeyForm.orderType}
names={[t('trade:limit'), t('market')]}
onChange={(type) => handleSetForm('orderType', type)}
values={['limit', 'market']}
/>
</div>
<div className="mb-4">
<Label text={t('order-size-type')} />
<Label text={t('settings:order-size-type')} />
<ButtonGroup
activeValue={hotKeyForm.sizeType}
names={[t('settings:percentage'), t('settings:notional')]}
onChange={(type) => handleSetForm('sizeType', type)}
values={['percentage', 'notional']}
/>
</div>
<div className="flex items-start space-x-4">
<div className="w-full">
<Label text={t('size')} />
<Tooltip
content={
hotKeyForm.sizeType === 'notional'
? t('settings:tooltip-hot-key-notional-size')
: t('settings:tooltip-hot-key-percentage-size')
}
>
<Label className="tooltip-underline" text={t('trade:size')} />
</Tooltip>
<Input
hasError={formErrors.size !== undefined}
type="text"
@ -351,7 +374,7 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
</div>
{hotKeyForm.orderType === 'limit' ? (
<div className="w-full">
<Tooltip content="Set a price as a percentage change from the oracle price">
<Tooltip content={t('settings:tooltip-hot-key-price')}>
<Label className="tooltip-underline" text={t('price')} />
</Tooltip>
<Input
@ -444,7 +467,7 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
</div>
</div>
<Button className="mt-6 w-full" onClick={handleSave}>
{t('save-hot-key')}
{t('settings:save-hot-key')}
</Button>
</>
</Modal>

View File

@ -1,5 +1,6 @@
import {
Group,
MangoAccount,
PerpMarket,
PerpOrderSide,
PerpOrderType,
@ -10,9 +11,9 @@ import {
} from '@blockworks-foundation/mango-v4'
import { HotKey } from '@components/settings/HotKeysSettings'
import mangoStore from '@store/mangoStore'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { ReactNode, useCallback } from 'react'
import Hotkeys from 'react-hot-keys'
import { isMangoError } from 'types'
import { GenericMarket, isMangoError } from 'types'
import { HOT_KEYS_KEY, SOUND_SETTINGS_KEY } from 'utils/constants'
import { notify } from 'utils/notifications'
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
@ -21,11 +22,11 @@ import useLocalStorageState from 'hooks/useLocalStorageState'
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import { useSpotMarketMax } from './SpotSlider'
import useMangoAccount from 'hooks/useMangoAccount'
import { Market } from '@project-serum/serum'
import { useRouter } from 'next/router'
import useUnownedAccount from 'hooks/useUnownedAccount'
import { useTranslation } from 'next-i18next'
const set = mangoStore.getState().set
@ -39,102 +40,145 @@ const calcBaseSize = (
limitPrice?: number
) => {
const { orderSize, orderSide, orderSizeType, orderType } = orderDetails
let quoteSize: number
if (!quoteTokenIndex) {
quoteSize =
orderSizeType === 'percentage'
? (Number(orderSize) / 100) * maxSize
: Number(orderSize)
} else {
const quoteBank = group.getFirstBankByTokenIndex(quoteTokenIndex)
const quotePrice = quoteBank.uiPrice
const orderSizeInQuote = Number(orderSize) / quotePrice
quoteSize =
orderSizeType === 'percentage'
? (orderSizeInQuote / 100) * maxSize
: orderSizeInQuote
}
let baseSize: number
if (orderType === 'market') {
if (orderSide === 'buy') {
let quoteSize: number
if (orderSide === 'buy') {
// assumes USDC = $1 as tokenIndex is 0
if (!quoteTokenIndex) {
quoteSize =
orderSizeType === 'percentage'
? (Number(orderSize) / 100) * maxSize
: Number(orderSize)
} else {
// required for non USDC quote tokens
const quoteBank = group.getFirstBankByTokenIndex(quoteTokenIndex)
const quotePrice = quoteBank.uiPrice
const orderSizeInQuote = Number(orderSize) / quotePrice
quoteSize =
orderSizeType === 'percentage'
? (orderSizeInQuote / 100) * maxSize
: orderSizeInQuote
}
if (orderType === 'market') {
baseSize = floorToDecimal(
quoteSize / oraclePrice,
getDecimalCount(market.minOrderSize)
).toNumber()
} else {
if (orderSizeType === 'percentage') {
baseSize = floorToDecimal(
(Number(orderSize) / 100) * maxSize,
getDecimalCount(market.minOrderSize)
).toNumber()
} else {
const price = limitPrice ? limitPrice : 0
baseSize = floorToDecimal(
quoteSize / price,
getDecimalCount(market.minOrderSize)
).toNumber()
}
} else {
if (orderSizeType === 'percentage') {
baseSize = floorToDecimal(
(Number(orderSize) / 100) * maxSize,
getDecimalCount(market.minOrderSize)
).toNumber()
} else {
if (orderType === 'market') {
baseSize = floorToDecimal(
Number(orderSize) / oraclePrice,
getDecimalCount(market.minOrderSize)
).toNumber()
} else {
const price = limitPrice ? limitPrice : 0
baseSize = floorToDecimal(
Number(orderSize) / price,
getDecimalCount(market.minOrderSize)
).toNumber()
}
}
} else {
const price = limitPrice ? limitPrice : 0
baseSize = floorToDecimal(
quoteSize / price,
getDecimalCount(market.minOrderSize)
).toNumber()
}
return baseSize
}
const calcSpotMarketMax = (
mangoAccount: MangoAccount | undefined,
selectedMarket: GenericMarket | undefined,
side: string,
useMargin: boolean
) => {
const spotBalances = mangoStore.getState().mangoAccount.spotBalances
const group = mangoStore.getState().group
if (!mangoAccount || !group || !selectedMarket) return 0
if (!(selectedMarket instanceof Serum3Market)) return 0
let leverageMax = 0
let spotMax = 0
try {
if (side === 'buy') {
leverageMax = mangoAccount.getMaxQuoteForSerum3BidUi(
group,
selectedMarket.serumMarketExternal
)
const bank = group.getFirstBankByTokenIndex(
selectedMarket.quoteTokenIndex
)
const balance = mangoAccount.getTokenBalanceUi(bank)
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
spotMax = balance + unsettled
} else {
leverageMax = mangoAccount.getMaxBaseForSerum3AskUi(
group,
selectedMarket.serumMarketExternal
)
const bank = group.getFirstBankByTokenIndex(selectedMarket.baseTokenIndex)
const balance = mangoAccount.getTokenBalanceUi(bank)
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
spotMax = balance + unsettled
}
return useMargin ? leverageMax : Math.max(spotMax, 0)
} catch (e) {
console.error('Error calculating max size: ', e)
return 0
}
}
const calcPerpMax = (
mangoAccount: MangoAccount,
selectedMarket: GenericMarket,
side: string
) => {
const group = mangoStore.getState().group
if (
!mangoAccount ||
!group ||
!selectedMarket ||
selectedMarket instanceof Serum3Market
)
return 0
try {
if (side === 'buy') {
return mangoAccount.getMaxQuoteForPerpBidUi(
group,
selectedMarket.perpMarketIndex
)
} else {
return mangoAccount.getMaxBaseForPerpAskUi(
group,
selectedMarket.perpMarketIndex
)
}
} catch (e) {
console.error('Error calculating max leverage: ', e)
return 0
}
}
const TradeHotKeys = ({ children }: { children: ReactNode }) => {
const {
price: oraclePrice,
selectedMarket,
serumOrPerpMarket,
} = useSelectedMarket()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const { t } = useTranslation(['common', 'settings'])
const { price: oraclePrice, serumOrPerpMarket } = useSelectedMarket()
const { mangoAccountAddress } = useMangoAccount()
const { isUnownedAccount } = useUnownedAccount()
const { asPath } = useRouter()
const [hotKeys] = useLocalStorageState(HOT_KEYS_KEY, [])
const [placingOrder, setPlacingOrder] = useState(false)
const [useMargin, setUseMargin] = useState(false)
const [side, setSide] = useState('buy')
const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS
)
const spotMax = useSpotMarketMax(
mangoAccount,
selectedMarket,
side,
useMargin
)
const perpMax = useMemo(() => {
const group = mangoStore.getState().group
if (
!mangoAccount ||
!group ||
!selectedMarket ||
selectedMarket instanceof Serum3Market
)
return 0
try {
if (side === 'buy') {
return mangoAccount.getMaxQuoteForPerpBidUi(
group,
selectedMarket.perpMarketIndex
)
} else {
return mangoAccount.getMaxBaseForPerpAskUi(
group,
selectedMarket.perpMarketIndex
)
}
} catch (e) {
console.error('Error calculating max leverage: ', e)
return 0
}
}, [mangoAccount, side, selectedMarket])
const handlePlaceOrder = useCallback(
async (hkOrder: HotKey) => {
@ -143,15 +187,23 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
const selectedMarket = mangoStore.getState().selectedMarket.current
const { ioc, orderPrice, orderSide, orderType, postOnly, reduceOnly } =
hkOrder
const {
ioc,
orderPrice,
orderSide,
orderType,
postOnly,
reduceOnly,
margin,
} = hkOrder
if (!group || !mangoAccount || !serumOrPerpMarket || !selectedMarket)
return
setPlacingOrder(true)
try {
const orderMax =
serumOrPerpMarket instanceof PerpMarket ? perpMax : spotMax
serumOrPerpMarket instanceof PerpMarket
? calcPerpMax(mangoAccount, selectedMarket, orderSide)
: calcSpotMarketMax(mangoAccount, selectedMarket, orderSide, margin)
const quoteTokenIndex =
selectedMarket instanceof PerpMarket
? 0
@ -196,6 +248,36 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
)
}
// check if size < max
if (orderSide === 'buy') {
if (baseSize * price > orderMax) {
notify({
type: 'error',
title: t('settings:error-order-exceeds-max'),
})
return
}
} else {
console.log(baseSize, orderMax)
if (baseSize > orderMax) {
notify({
type: 'error',
title: t('settings:error-order-exceeds-max'),
})
return
}
}
notify({
type: 'info',
title: t('settings:placing-order'),
description: `${t(orderSide)} ${baseSize} ${selectedMarket.name} ${
orderType === 'limit'
? `${t('settings:at')} ${price}`
: `${t('settings:at')} ${t('market')}`
}`,
})
if (selectedMarket instanceof Serum3Market) {
const spotOrderType = ioc
? Serum3OrderType.immediateOrCancel
@ -273,37 +355,23 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
txid: e?.txid,
type: 'error',
})
} finally {
setPlacingOrder(false)
}
},
[perpMax, serumOrPerpMarket, spotMax]
[serumOrPerpMarket]
)
const onKeyDown = useCallback(
(keyName: string) => {
console.log('sdfsdf')
const orderDetails = hotKeys.find(
(hk: HotKey) => hk.keySequence === keyName
)
if (orderDetails) {
setUseMargin(orderDetails.margin)
setSide(orderDetails.orderSide)
handlePlaceOrder(orderDetails)
}
},
[handlePlaceOrder, hotKeys]
)
useEffect(() => {
if (placingOrder) {
notify({
type: 'success',
title: 'Placing order for...',
})
}
}, [placingOrder])
const showHotKeys =
hotKeys.length &&
asPath.includes('/trade') &&

View File

@ -17,6 +17,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'profile',
'search',
'settings',
'trade',
])),
},
}

View File

@ -1,7 +1,11 @@
{
"above": "Above",
"animations": "Animations",
"at": "at",
"avocado": "Avocado",
"banana": "Banana",
"base-key": "Base Key",
"below": "Below",
"blueberry": "Blueberry",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
@ -16,18 +20,38 @@
"custom": "Custom",
"dark": "Dark",
"display": "Display",
"error-alphanumeric-only": "Alphanumeric characters only",
"error-must-be-above-zero": "Must be greater than zero",
"error-must-be-below-100": "Must be below 100",
"error-must-be-number": "Must be a number",
"error-order-exceeds-max": "Order exceeds max size",
"error-required-field": "This field is required",
"error-too-many-characters": "Enter one alphanumeric character",
"english": "English",
"high-contrast": "High Contrast",
"hot-keys": "Hot Keys",
"hot-keys-desc": "Create hot keys to place trades",
"key-sequence": "Key Sequence",
"language": "Language",
"light": "Light",
"lychee": "Lychee",
"mango": "Mango",
"mango-classic": "Mango Classic",
"medium": "Medium",
"new-hot-key": "New Hot Key",
"no-hot-keys": "Create your first hot key",
"notification-position": "Notification Position",
"notional": "Notional",
"number-scroll": "Number Scroll",
"olive": "Olive",
"options": "Options",
"oracle": "Oracle",
"orderbook-flash": "Orderbook Flash",
"order-side": "Order Side",
"order-size-type": "Order Size Type",
"percentage": "Percentage",
"percentage-of-max": "{{size}}% of Max",
"placing-order": "Placing Order...",
"preferred-explorer": "Preferred Explorer",
"recent-trades": "Recent Trades",
"rpc": "RPC",
@ -35,6 +59,7 @@
"rpc-url": "Enter RPC URL",
"russian": "Русский",
"save": "Save",
"save-hot-key": "Save Hot Key",
"slider": "Slider",
"solana-beach": "Solana Beach",
"solana-explorer": "Solana Explorer",
@ -45,6 +70,9 @@
"swap-success": "Swap/Trade Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme",
"tooltip-hot-key-notional-size": "Set size as a USD value.",
"tooltip-hot-key-percentage-size": "Set size as a percentage of your max leverage.",
"tooltip-hot-key-price": "Set a price as a percentage change from the oracle price.",
"top-left": "Top-Left",
"top-right": "Top-Right",
"trade-layout": "Trade Layout",
@ -52,6 +80,7 @@
"transaction-success": "Transaction Success",
"trade-chart": "Trade Chart",
"trading-view": "Trading View",
"trigger-key": "Trigger Key",
"notifications": "Notifications",
"limit-order-filled": "Limit Order Fills",
"orderbook-bandwidth-saving": "Orderbook Bandwidth Saving",

View File

@ -35,6 +35,7 @@
"maker": "Maker",
"maker-fee": "Maker Fee",
"margin": "Margin",
"market": "Market",
"market-details": "{{market}} Market Details",
"max-leverage": "Max Leverage",
"min-order-size": "Min Order Size",
@ -63,6 +64,7 @@
"price-expect": "The price you receive may be worse than you expect and full execution is not guaranteed. Max slippage is 2.5% for your safety. The part of your position with slippage beyond 2.5% will not be closed.",
"price-provided-by": "Oracle by",
"quote": "Quote",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"sells": "Sells",
"settle-funds": "Settle Funds",

View File

@ -35,6 +35,7 @@
"maker": "Maker",
"maker-fee": "Maker Fee",
"margin": "Margin",
"market": "Market",
"market-details": "{{market}} Market Details",
"max-leverage": "Max Leverage",
"min-order-size": "Min Order Size",
@ -63,6 +64,7 @@
"price-expect": "The price you receive may be worse than you expect and full execution is not guaranteed. Max slippage is 2.5% for your safety. The part of your position with slippage beyond 2.5% will not be closed.",
"price-provided-by": "Oracle by",
"quote": "Quote",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"sells": "Sells",
"settle-funds": "Settle Funds",

View File

@ -35,6 +35,7 @@
"maker": "Maker",
"maker-fee": "Maker Fee",
"margin": "Margin",
"market": "Market",
"market-details": "{{market}} Market Details",
"max-leverage": "Max Leverage",
"min-order-size": "Min Order Size",
@ -63,6 +64,7 @@
"price-expect": "The price you receive may be worse than you expect and full execution is not guaranteed. Max slippage is 2.5% for your safety. The part of your position with slippage beyond 2.5% will not be closed.",
"price-provided-by": "Oracle by",
"quote": "Quote",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"sells": "Sells",
"settle-funds": "Settle Funds",

View File

@ -34,6 +34,7 @@
"long": "做多",
"maker": "挂单者",
"margin": "保证金",
"market": "Market",
"market-details": "{{market}}市场细节",
"max-leverage": "最多杠杆",
"min-order-size": "最小订单量",
@ -62,6 +63,7 @@
"price-expect": "您收到的价格可能与您预期有差异,并且无法保证完全执行。为了您的安全,最大滑点保持为 2.5%。超过 2.5%滑点的部分不会被平仓。",
"price-provided-by": "语言机来自",
"quote": "计价",
"reduce": "Reduce",
"reduce-only": "限减少",
"sells": "卖单",
"settle-funds": "借清资金",

View File

@ -35,6 +35,7 @@
"maker": "掛單者",
"maker-fee": "掛單者 Fee",
"margin": "保證金",
"market": "Market",
"market-details": "{{market}}市場細節",
"max-leverage": "最多槓桿",
"min-order-size": "最小訂單量",
@ -63,6 +64,7 @@
"price-expect": "您收到的價格可能與您預期有差異,並且無法保證完全執行。為了您的安全,最大滑點保持為 2.5%。超過 2.5%滑點的部分不會被平倉。",
"price-provided-by": "語言機來自",
"quote": "計價",
"reduce": "Reduce",
"reduce-only": "限減少",
"sells": "賣單",
"settle-funds": "借清資金",