From f4cba91d5b6fb577ed6d9cbe109b7904d45c8b21 Mon Sep 17 00:00:00 2001 From: Philippe Maes Date: Wed, 23 Sep 2020 12:38:44 +0200 Subject: [PATCH 1/3] Market order --- src/components/TradeForm.jsx | 49 ++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/components/TradeForm.jsx b/src/components/TradeForm.jsx index cf0fe7786..6d40bbcc5 100644 --- a/src/components/TradeForm.jsx +++ b/src/components/TradeForm.jsx @@ -1,4 +1,4 @@ -import { Button, Input, Radio, Switch, Slider } from 'antd'; +import { Button, Input, Radio, Switch, Slider, Select } from 'antd'; import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import { @@ -9,6 +9,7 @@ import { useSelectedOpenOrdersAccount, useSelectedBaseCurrencyAccount, useSelectedQuoteCurrencyAccount, + useOrderbook, } from '../utils/markets'; import { useWallet } from '../utils/wallet'; import { notify } from '../utils/notifications'; @@ -21,6 +22,12 @@ import { useSendConnection } from '../utils/connection'; import FloatingElement from './layout/FloatingElement'; import { placeOrder } from '../utils/send'; +const DEFAULT_ORDER_TYPE = 'limit'; +const ORDER_TYPES = [ + { label: 'Limit', value: 'limit' }, + { label: 'Market', value: 'market' }, +]; + const SellButton = styled(Button)` margin: 20px 0px 0px 0px; background: #f23b69; @@ -42,7 +49,6 @@ const sliderMarks = { }; export default function TradeForm({ style, setChangeOrderRef }) { - const [side, setSide] = useState('buy'); const { baseCurrency, quoteCurrency, market } = useMarket(); const baseCurrencyBalances = useBaseCurrencyBalances(); const quoteCurrencyBalances = useQuoteCurrencyBalances(); @@ -52,7 +58,10 @@ export default function TradeForm({ style, setChangeOrderRef }) { const { wallet } = useWallet(); const sendConnection = useSendConnection(); const markPrice = useMarkPrice(); + const [orderbook] = useOrderbook(); + const [side, setSide] = useState('buy'); + const [orderType, setOrderType] = useState(DEFAULT_ORDER_TYPE); const [postOnly, setPostOnly] = useState(false); const [ioc, setIoc] = useState(false); const [baseSize, setBaseSize] = useState(null); @@ -152,8 +161,16 @@ export default function TradeForm({ style, setChangeOrderRef }) { }; async function onSubmit() { - const parsedPrice = parseFloat(price); - const parsedSize = parseFloat(baseSize); + let parsedSize = parseFloat(baseSize); + let parsedPrice; + if (orderType === 'market') { + const bbo = + side === 'buy' ? orderbook.asks[0]?.[0] : orderbook.bids[0]?.[0]; + parsedPrice = + bbo + 100 * (side === 'buy' ? market?.tickSize : -market?.tickSize); + } else { + parsedPrice = parseFloat(price); + } setSubmitting(true); try { @@ -217,15 +234,31 @@ export default function TradeForm({ style, setChangeOrderRef }) { SELL +
+ + Type + + Price
} suffix={ {quoteCurrency} } - value={price} - type="number" + value={orderType === 'market' ? 'MARKET' : price} + type={orderType === 'market' ? 'text' : 'number'} step={market?.tickSize || 1} + disabled={orderType === 'market'} onChange={(e) => setPrice(e.target.value)} /> @@ -272,7 +305,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { {side === 'buy' ? ( ) : ( Date: Wed, 23 Sep 2020 17:42:02 +0200 Subject: [PATCH 2/3] Change market prices --- src/components/TradeForm.jsx | 7 ++++--- src/utils/markets.js | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/TradeForm.jsx b/src/components/TradeForm.jsx index 6d40bbcc5..f55e5296a 100644 --- a/src/components/TradeForm.jsx +++ b/src/components/TradeForm.jsx @@ -10,6 +10,7 @@ import { useSelectedBaseCurrencyAccount, useSelectedQuoteCurrencyAccount, useOrderbook, + calculateBestPrice, } from '../utils/markets'; import { useWallet } from '../utils/wallet'; import { notify } from '../utils/notifications'; @@ -164,10 +165,10 @@ export default function TradeForm({ style, setChangeOrderRef }) { let parsedSize = parseFloat(baseSize); let parsedPrice; if (orderType === 'market') { - const bbo = - side === 'buy' ? orderbook.asks[0]?.[0] : orderbook.bids[0]?.[0]; parsedPrice = - bbo + 100 * (side === 'buy' ? market?.tickSize : -market?.tickSize); + side === 'buy' + ? calculateBestPrice(orderbook, parsedSize) + : market?.tickSize; } else { parsedPrice = parseFloat(price); } diff --git a/src/utils/markets.js b/src/utils/markets.js index 92e08ffcd..e9402dd09 100644 --- a/src/utils/markets.js +++ b/src/utils/markets.js @@ -996,3 +996,12 @@ export function useBalancesForDeprecatedMarkets() { }); return openOrderAccountBalances; } + +export function calculateBestPrice(orderbook, size) { + let remainingSize = size; + for (let [price, size] of orderbook?.asks) { + remainingSize -= size; + if (remainingSize <= 0) return price; + } + return orderbook?.asks[orderbook.asks.length - 1]?.[0]; +} From 9de2dbaa4f31568f5a487e9428b935903b3dfc32 Mon Sep 17 00:00:00 2001 From: Philippe Maes Date: Tue, 29 Sep 2020 12:12:02 +0200 Subject: [PATCH 3/3] Take fees into account when taker buy --- src/components/TradeForm.jsx | 40 ++++++++++++++++++++++++++++-------- src/utils/utils.js | 6 ++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/components/TradeForm.jsx b/src/components/TradeForm.jsx index 43dd8a837..794e0c20a 100644 --- a/src/components/TradeForm.jsx +++ b/src/components/TradeForm.jsx @@ -10,14 +10,17 @@ import { useSelectedBaseCurrencyAccount, useSelectedQuoteCurrencyAccount, useOrderbook, + useFeeDiscountKeys, calculateBestPrice, } from '../utils/markets'; +import { getFeeRates } from '@project-serum/serum'; import { useWallet } from '../utils/wallet'; import { notify } from '../utils/notifications'; import { getDecimalCount, roundToDecimal, floorToDecimal, + isIncrement, } from '../utils/utils'; import { useSendConnection } from '../utils/connection'; import FloatingElement from './layout/FloatingElement'; @@ -60,6 +63,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { const sendConnection = useSendConnection(); const markPrice = useMarkPrice(); const [orderbook] = useOrderbook(); + const [feeAccounts] = useFeeDiscountKeys(); const [side, setSide] = useState('buy'); const [orderType, setOrderType] = useState(DEFAULT_ORDER_TYPE); @@ -75,6 +79,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { ? market.quoteSplSizeToNumber(openOrdersAccount.quoteTokenFree) : 0; + let feeRates = getFeeRates(feeAccounts && feeAccounts[0]?.feeTier); let quoteBalance = (quoteCurrencyBalances || 0) + (availableQuote || 0); let baseBalance = baseCurrencyBalances || 0; let sizeDecimalCount = @@ -96,11 +101,21 @@ export default function TradeForm({ style, setChangeOrderRef }) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [price, baseSize]); + const onSetPrice = (price) => { + const formattedPrice = isIncrement(price, market?.tickSize) + ? price + : floorToDecimal(price, priceDecimalCount); + setPrice(formattedPrice); + }; + const onSetBaseSize = (baseSize) => { - setBaseSize(baseSize); - const rawQuoteSize = baseSize * (price || markPrice); + const formattedBaseSize = isIncrement(baseSize, market?.minOrderSize) + ? baseSize + : floorToDecimal(baseSize, sizeDecimalCount); + setBaseSize(formattedBaseSize); + const rawQuoteSize = formattedBaseSize * (price || markPrice); const quoteSize = - baseSize && roundToDecimal(rawQuoteSize, sizeDecimalCount); + formattedBaseSize && roundToDecimal(rawQuoteSize, sizeDecimalCount); setQuoteSize(quoteSize); }; @@ -115,11 +130,20 @@ export default function TradeForm({ style, setChangeOrderRef }) { const formattedSize = size && roundToDecimal(size, sizeDecimalCount); const formattedPrice = price && roundToDecimal(price, priceDecimalCount); formattedSize && onSetBaseSize(formattedSize); - formattedPrice && setPrice(formattedPrice); + formattedPrice && onSetPrice(formattedPrice); }; const updateSizeFraction = () => { - const rawMaxSize = side === 'buy' ? quoteBalance / price : baseBalance; + let rawMaxSize; + if (side === 'sell') { + rawMaxSize = baseBalance; + } else { + let maxQuoteBalance = + orderType === 'market' || price >= orderbook?.asks[0]?.[0] + ? (1 - feeRates.taker) * quoteBalance + : quoteBalance; + rawMaxSize = maxQuoteBalance / price; + } const maxSize = floorToDecimal(rawMaxSize, sizeDecimalCount); const sizeFraction = Math.min((baseSize / maxSize) * 100, 100); setSizeFraction(sizeFraction); @@ -130,7 +154,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { let formattedMarkPrice = priceDecimalCount ? markPrice.toFixed(priceDecimalCount) : markPrice; - setPrice(formattedMarkPrice); + onSetPrice(formattedMarkPrice); } let newSize; @@ -186,7 +210,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { baseCurrencyAccount: baseCurrencyAccount?.pubkey, quoteCurrencyAccount: quoteCurrencyAccount?.pubkey, }); - setPrice(null); + onSetPrice(null); onSetBaseSize(null); } catch (e) { console.warn(e); @@ -262,7 +286,7 @@ export default function TradeForm({ style, setChangeOrderRef }) { type={orderType === 'market' ? 'text' : 'number'} step={market?.tickSize || 1} disabled={orderType === 'market'} - onChange={(e) => setPrice(e.target.value)} + onChange={(e) => onSetPrice(e.target.value)} /> setTimeout(resolve, ms)); }