diff --git a/components/MarginBalances.tsx b/components/MarginBalances.tsx index 2bed36a..609dfca 100644 --- a/components/MarginBalances.tsx +++ b/components/MarginBalances.tsx @@ -73,17 +73,23 @@ export default function MarginBalances() { {selectedMangoGroup ? ( - - + - - - @@ -101,7 +107,7 @@ export default function MarginBalances() { /> {name} - - -
+
Assets + Deposits + Borrows + Interest (APY)
+ {selectedMarginAccount ? floorToDecimal( selectedMarginAccount.getUiDeposit( @@ -112,14 +118,14 @@ export default function MarginBalances() { ).toFixed(tokenPrecision[name]) : (0).toFixed(tokenPrecision[name])} + {selectedMarginAccount ? selectedMarginAccount .getUiBorrow(selectedMangoGroup, i) .toFixed(tokenPrecision[name]) : (0).toFixed(tokenPrecision[name])} + +{(selectedMangoGroup.getDepositRate(i) * 100).toFixed(2)} % diff --git a/components/Orderbook.tsx b/components/Orderbook.tsx index 845d873..be03cd3 100644 --- a/components/Orderbook.tsx +++ b/components/Orderbook.tsx @@ -1,48 +1,74 @@ import React, { useRef, useEffect, useState } from 'react' import styled from '@emotion/styled' +import { css, keyframes } from '@emotion/react' import useInterval from '../hooks/useInterval' import usePrevious from '../hooks/usePrevious' import { isEqual, getDecimalCount } from '../utils/' -import { ArrowUpIcon, ArrowDownIcon } from '@heroicons/react/solid' +import { + ArrowUpIcon, + ArrowDownIcon, + SwitchHorizontalIcon, +} from '@heroicons/react/solid' import useMarkPrice from '../hooks/useMarkPrice' import useOrderbook from '../hooks/useOrderbook' import useMarket from '../hooks/useMarket' import { ElementTitle } from './styles' import useMangoStore from '../stores/useMangoStore' +import Tooltip from './Tooltip' +import FloatingElement from './FloatingElement' const Line = styled.div` text-align: ${(props) => (props.invert ? 'left' : 'right')}; - float: ${(props) => (props.invert ? 'left' : 'right')}; height: 100%; - filter: opacity(70%); + filter: opacity(40%); ${(props) => props['data-width'] && `width: ${props['data-width']};`} ` - -function getCumulativeOrderbookSide( - orders, - totalSize, - depth, - backwards = false -) { - let cumulative = orders - .slice(0, depth) - .reduce((cumulative, [price, size], i) => { - const cumulativeSize = (cumulative[i - 1]?.cumulativeSize || 0) + size - cumulative.push({ - price, - size, - cumulativeSize, - sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100), - }) - return cumulative - }, []) - if (backwards) { - cumulative = cumulative.reverse() +const fadeIn = keyframes` + from { + opacity: 0; } - return cumulative -} -export default function Orderbook({ depth = 7 }) { + to { + opacity: 1; + } +` + +const FlipCard = styled.div` + background-color: transparent; + height: 100%; + perspective: 1000px; +` + +const FlipCardInner = styled.div` + position: relative; + width: 100%; + height: 100%; + text-align: center; + transition: transform 0.8s ease-out; + transform-style: preserve-3d; + transform: ${({ flip }) => (flip ? 'rotateY(0deg)' : 'rotateY(180deg)')}; +` + +const FlipCardFront = styled.div` + position: absolute; + width: 100%; + height: 100%; +` + +const FlipCardBack = styled.div` + position: absolute; + width: 100%; + height: 100%; + transform: rotateY(180deg); +` + +const StyledFloatingElement = styled(FloatingElement)` + animation: ${css` + ${fadeIn} 1s linear + `}; +` + +export default function Orderbook({ depth = 8 }) { const markPrice = useMarkPrice() const [orderbook] = useOrderbook() const { baseCurrency, quoteCurrency } = useMarket() @@ -52,6 +78,8 @@ export default function Orderbook({ depth = 7 }) { const lastOrderbookData = useRef(null) const [orderbookData, setOrderbookData] = useState(null) + const [defaultLayout, setDefaultLayout] = useState(true) + const [loading, setLoading] = useState(true) useInterval(() => { if ( @@ -72,19 +100,30 @@ export default function Orderbook({ depth = 7 }) { depth, false ) - const asksToDisplay = getCumulativeOrderbookSide( - asks, - totalSize, - depth, - true - ) + const asksToDisplay = defaultLayout + ? getCumulativeOrderbookSide(asks, totalSize, depth, false) + : getCumulativeOrderbookSide(asks, totalSize, depth, true) currentOrderbookData.current = { bids: orderbook?.bids, asks: orderbook?.asks, } + if (bidsToDisplay[0] && asksToDisplay[0]) { + const bid = bidsToDisplay[0].price + const ask = defaultLayout + ? asksToDisplay[0].price + : asksToDisplay[7].price + const spread = ask - bid + const spreadPercentage = (spread / ask) * 100 - setOrderbookData({ bids: bidsToDisplay, asks: asksToDisplay }) + setOrderbookData({ + bids: bidsToDisplay, + asks: asksToDisplay, + spread: spread, + spreadPercentage: spreadPercentage, + }) + setLoading(false) + } } }, 250) @@ -95,6 +134,30 @@ export default function Orderbook({ depth = 7 }) { } }, [orderbook]) + const getCumulativeOrderbookSide = ( + orders, + totalSize, + depth, + backwards = false + ) => { + let cumulative = orders + .slice(0, depth) + .reduce((cumulative, [price, size], i) => { + const cumulativeSize = (cumulative[i - 1]?.cumulativeSize || 0) + size + cumulative.push({ + price, + size, + cumulativeSize, + sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100), + }) + return cumulative + }, []) + if (backwards) { + cumulative = cumulative.reverse() + } + return cumulative + } + const handlePriceClick = (price) => { console.log('price click') @@ -111,36 +174,188 @@ export default function Orderbook({ depth = 7 }) { }) } + const handleLayoutChange = () => { + setLoading(true) + setDefaultLayout(!defaultLayout) + } + return ( <> - Orderbook -
-
Size ({baseCurrency})
-
Price ({quoteCurrency})
-
- {orderbookData?.asks.map(({ price, size, sizePercent }) => ( - handlePriceClick(price)} - onSizeClick={() => handleSizeClick(size)} - /> - ))} - - {orderbookData?.bids.map(({ price, size, sizePercent }) => ( - handlePriceClick(price)} - onSizeClick={() => handleSizeClick(size)} - /> - ))} + + + {defaultLayout ? ( + + +
+
+ Orderbook +
+ + + +
+
+ + {!loading ? ( + <> +
+
Size ({baseCurrency})
+
+ Price ({quoteCurrency}) +
+
Size ({baseCurrency})
+
+
+
+ {orderbookData?.bids.map( + ({ price, size, sizePercent }) => ( + handlePriceClick(price)} + onSizeClick={() => handleSizeClick(size)} + /> + ) + )} +
+
+ {/*
+
+ Ask ({quoteCurrency}) +
+
+ Size ({baseCurrency}) +
+
*/} + {orderbookData?.asks.map( + ({ price, size, sizePercent }) => ( + handlePriceClick(price)} + onSizeClick={() => handleSizeClick(size)} + /> + ) + )} +
+
+
+
Spread
+
+ {orderbookData?.spread.toFixed(2)} +
+
+ {orderbookData?.spreadPercentage.toFixed(2)}% +
+
+ + ) : ( +
+
+
+
+
+
+
+
+
+
+ )} + + + ) : ( + + +
+
+ Orderbook +
+ + + +
+
+ + {!loading ? ( + <> +
+
+ Size ({baseCurrency}) +
+
+ Price ({quoteCurrency}) +
+
+ {orderbookData?.asks.map(({ price, size, sizePercent }) => ( + handlePriceClick(price)} + onSizeClick={() => handleSizeClick(size)} + /> + ))} +
+
Spread
+
+ {orderbookData?.spread.toFixed(2)} +
+
+ {orderbookData?.spreadPercentage.toFixed(2)}% +
+
+ {orderbookData?.bids.map(({ price, size, sizePercent }) => ( + handlePriceClick(price)} + onSizeClick={() => handleSizeClick(size)} + /> + ))} + + ) : ( +
+
+
+
+
+
+
+
+
+
+ )} + + + )} + + ) } @@ -176,14 +391,21 @@ const OrderbookRow = React.memo(
{invert ? ( <> -
+
-
{formattedPrice}
+
+ {formattedPrice} +
{formattedSize} @@ -206,7 +428,7 @@ const OrderbookRow = React.memo( side={side} />
{formattedPrice} @@ -233,7 +455,7 @@ const MarkPriceComponent = React.memo<{ markPrice: number }>( return (
previousMarkPrice ? `text-th-green` : markPrice < previousMarkPrice @@ -242,10 +464,10 @@ const MarkPriceComponent = React.memo<{ markPrice: number }>( }`} > {markPrice > previousMarkPrice && ( - + )} {markPrice < previousMarkPrice && ( - + )} {formattedMarkPrice || '----'}
diff --git a/components/RecentMarketTrades.tsx b/components/RecentMarketTrades.tsx index fdb3ca1..305bccf 100644 --- a/components/RecentMarketTrades.tsx +++ b/components/RecentMarketTrades.tsx @@ -35,7 +35,7 @@ export default function PublicTrades() { return ( Recent Trades -
+
Price ({quoteCurrency})
Size ({baseCurrency})
Time
diff --git a/components/TradeForm.tsx b/components/TradeForm.tsx index 183d941..10d7e23 100644 --- a/components/TradeForm.tsx +++ b/components/TradeForm.tsx @@ -259,7 +259,7 @@ export default function TradeForm() { disabled={tradeType === 'Market'} prefix={'Price'} suffix={quoteCurrency} - className="rounded-none" + className="rounded-r-none" wrapperClassName="w-3/5" /> onSetBaseSize(parseFloat(e.target.value))} value={baseSize} - className="rounded-none" + className="rounded-r-none" wrapperClassName="w-3/5" prefix={'Size'} suffix={baseCurrency} diff --git a/components/TradePageGrid.tsx b/components/TradePageGrid.tsx index 71066e9..e490f6f 100644 --- a/components/TradePageGrid.tsx +++ b/components/TradePageGrid.tsx @@ -73,9 +73,7 @@ const TradePageGrid = () => {
- - - +
diff --git a/public/tradingview-chart.css b/public/tradingview-chart.css index 331cbd4..f545778 100644 --- a/public/tradingview-chart.css +++ b/public/tradingview-chart.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400&display=swap'); + :root:not(.theme-dark) { --black: #fff; --primary: #ff9c24; @@ -48,6 +50,10 @@ /* light theme */ +html .chart-page { + font-family: 'Lato', sans-serif; +} + html .loading-indicator { background: transparent; } @@ -371,6 +377,10 @@ html .item-stVdeCwG.interactive-3E0jwVyG:active { /* dark theme */ +html.theme-dark .chart-page { + font-family: 'Lato', sans-serif; +} + html.theme-dark .loading-indicator { background: transparent; }