mirror of https://github.com/certusone/oyster.git
feat: First screen attempt: Try to come up with a clever way to visualize your margin order and just get something up there. Next up, move to a better order flow.
This commit is contained in:
parent
18ee623ef5
commit
61cf44e95a
|
@ -1,28 +1,29 @@
|
||||||
import { Button, Select, Slider } from 'antd';
|
import { Button, Select, Slider } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { IPosition } from '.';
|
||||||
import { NumericInput } from '../../components/Input/numeric';
|
import { NumericInput } from '../../components/Input/numeric';
|
||||||
import { TokenIcon } from '../../components/TokenIcon';
|
import { TokenIcon } from '../../components/TokenIcon';
|
||||||
import tokens from '../../config/tokens.json';
|
import tokens from '../../config/tokens.json';
|
||||||
import { LABELS } from '../../constants/labels';
|
import { LABELS } from '../../constants/labels';
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
interface EditableAssetProps {
|
interface IEditableAssetProps {
|
||||||
label: string;
|
label: string;
|
||||||
itemAssetKey: string;
|
assetKey: string;
|
||||||
itemAssetValueKey: string;
|
|
||||||
setItem: (item: any) => void;
|
setItem: (item: any) => void;
|
||||||
item: any;
|
item: any;
|
||||||
}
|
}
|
||||||
function EditableAsset({ label, itemAssetKey, itemAssetValueKey, setItem, item }: EditableAssetProps) {
|
function EditableAsset({ label, assetKey, setItem, item }: IEditableAssetProps) {
|
||||||
console.log('Now looking at', item);
|
if (!item[assetKey]?.type) {
|
||||||
if (!item[itemAssetKey]) {
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
size='large'
|
size='large'
|
||||||
showSearch
|
showSearch
|
||||||
style={{ margin: '5px 0px' }}
|
style={{ margin: '5px 0px' }}
|
||||||
placeholder={label}
|
placeholder={label}
|
||||||
onChange={(v) => setItem({ ...item, [itemAssetKey]: tokens.find((t) => t.mintAddress === v) })}
|
onChange={(v) =>
|
||||||
|
setItem({ ...item, [assetKey]: { ...(item[assetKey] || {}), type: tokens.find((t) => t.mintAddress === v) } })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{tokens.map((token) => (
|
{tokens.map((token) => (
|
||||||
<Option key={token.mintAddress} value={token.mintAddress} name={token.tokenName} title={token.tokenName}>
|
<Option key={token.mintAddress} value={token.mintAddress} name={token.tokenName} title={token.tokenName}>
|
||||||
|
@ -38,33 +39,45 @@ function EditableAsset({ label, itemAssetKey, itemAssetValueKey, setItem, item }
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-start' }}>
|
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-start' }}>
|
||||||
<NumericInput
|
<NumericInput
|
||||||
value={item[itemAssetValueKey]}
|
value={item[assetKey].value}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
outline: 'transparent',
|
outline: 'transparent',
|
||||||
}}
|
}}
|
||||||
|
onChange={(v: number) => {
|
||||||
|
setItem({ ...item, [assetKey]: { ...(item[assetKey] || {}), value: v } });
|
||||||
|
}}
|
||||||
placeholder='0.00'
|
placeholder='0.00'
|
||||||
/>
|
/>
|
||||||
<TokenIcon mintAddress={item[itemAssetKey]?.mintAddress} />
|
<TokenIcon mintAddress={item[assetKey]?.type?.mintAddress} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MarginTradePosition({ item, setItem }: { item: any; setItem?: (item: any) => void }) {
|
export default function MarginTradePosition({ item, setItem }: { item: IPosition; setItem?: (item: any) => void }) {
|
||||||
return (
|
return (
|
||||||
<div className='trading-item'>
|
<div className='trading-item'>
|
||||||
<div>
|
<div>
|
||||||
{setItem && (
|
{setItem && (
|
||||||
<EditableAsset
|
<Select
|
||||||
item={item}
|
size='large'
|
||||||
setItem={setItem}
|
showSearch
|
||||||
label={LABELS.TRADING_TABLE_TITLE_MY_COLLATERAL}
|
style={{ margin: '5px 0px' }}
|
||||||
itemAssetKey={'collateralType'}
|
placeholder={LABELS.TRADING_TABLE_TITLE_MY_COLLATERAL}
|
||||||
itemAssetValueKey={'collateralValue'}
|
onChange={(v) => setItem({ ...item, collateral: tokens.find((t) => t.mintAddress === v) })}
|
||||||
/>
|
>
|
||||||
|
{tokens.map((token) => (
|
||||||
|
<Option key={token.mintAddress} value={token.mintAddress} name={token.tokenName} title={token.tokenName}>
|
||||||
|
<div key={token.mintAddress} style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<TokenIcon mintAddress={token.mintAddress} />
|
||||||
|
{token.tokenName}
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -72,14 +85,26 @@ export default function MarginTradePosition({ item, setItem }: { item: any; setI
|
||||||
<EditableAsset
|
<EditableAsset
|
||||||
item={item}
|
item={item}
|
||||||
setItem={setItem}
|
setItem={setItem}
|
||||||
label={LABELS.TRADING_TABLE_TITLE_MY_COLLATERAL}
|
label={LABELS.TRADING_TABLE_TITLE_DESIRED_ASSET}
|
||||||
itemAssetKey={'assetType'}
|
assetKey={'asset'}
|
||||||
itemAssetValueKey={'assetValue'}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Slider tooltipVisible={true} defaultValue={1} dots={true} max={5} min={1} step={1} tooltipPlacement={'top'} />
|
{setItem && (
|
||||||
|
<Slider
|
||||||
|
tooltipVisible={true}
|
||||||
|
defaultValue={1}
|
||||||
|
dots={true}
|
||||||
|
max={5}
|
||||||
|
min={1}
|
||||||
|
step={1}
|
||||||
|
tooltipPlacement={'top'}
|
||||||
|
onChange={(v: number) => {
|
||||||
|
setItem({ ...item, leverage: v });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>123</div>
|
<div>123</div>
|
||||||
<div>123</div>
|
<div>123</div>
|
||||||
|
|
|
@ -1,11 +1,116 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { LABELS } from '../../constants';
|
import { LABELS } from '../../constants';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
import { Card } from 'antd';
|
import { Card, Progress, Slider, Statistic } from 'antd';
|
||||||
import MarginTradePosition from './MarginTradePosition';
|
import MarginTradePosition from './MarginTradePosition';
|
||||||
|
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
export interface IToken {
|
||||||
|
mintAddress: string;
|
||||||
|
tokenName: string;
|
||||||
|
tokenSymbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPosition {
|
||||||
|
id?: number | null;
|
||||||
|
leverage: number;
|
||||||
|
collateral?: IToken;
|
||||||
|
asset?: {
|
||||||
|
type: IToken;
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Breakdown({ item }: { item: IPosition }) {
|
||||||
|
let myPart = (item.asset?.value || 0) / item.leverage;
|
||||||
|
const brokeragePart = (item.asset?.value || 0) - myPart;
|
||||||
|
const brokerageColor = 'brown';
|
||||||
|
const myColor = 'blue';
|
||||||
|
const gains = 'green';
|
||||||
|
const losses = 'red';
|
||||||
|
|
||||||
|
const [myGain, setMyGain] = useState<number>(0);
|
||||||
|
const profitPart = (myPart + brokeragePart) * (myGain / 100);
|
||||||
|
let progressBar = null;
|
||||||
|
if (profitPart > 0) {
|
||||||
|
// normalize...
|
||||||
|
const total = profitPart + myPart + brokeragePart;
|
||||||
|
progressBar = (
|
||||||
|
<Progress
|
||||||
|
percent={(myPart / total) * 100 + (brokeragePart / total) * 100}
|
||||||
|
success={{ percent: (brokeragePart / total) * 100, strokeColor: brokerageColor }}
|
||||||
|
strokeColor={myColor}
|
||||||
|
trailColor={gains}
|
||||||
|
showInfo={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// now, we're eating away your profit...
|
||||||
|
myPart += profitPart; // profit is negative
|
||||||
|
const total = myPart + brokeragePart;
|
||||||
|
if (myPart < 0) {
|
||||||
|
progressBar = <p>Your position has been liquidated at this price swing.</p>;
|
||||||
|
} else
|
||||||
|
progressBar = (
|
||||||
|
<Progress
|
||||||
|
showInfo={false}
|
||||||
|
success={{ percent: (brokeragePart / total) * 100, strokeColor: brokerageColor }}
|
||||||
|
trailColor={myColor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Slider
|
||||||
|
tooltipVisible={true}
|
||||||
|
defaultValue={0}
|
||||||
|
dots={true}
|
||||||
|
max={100}
|
||||||
|
min={-100}
|
||||||
|
step={5}
|
||||||
|
tooltipPlacement={'top'}
|
||||||
|
onChange={(v: number) => {
|
||||||
|
setMyGain(v);
|
||||||
|
}}
|
||||||
|
style={{ marginBottom: '20px' }}
|
||||||
|
/>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' }}>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title='Leverage'
|
||||||
|
value={brokeragePart}
|
||||||
|
precision={2}
|
||||||
|
valueStyle={{ color: brokerageColor }}
|
||||||
|
suffix={item.asset?.type.tokenName}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title='My Collateral Value'
|
||||||
|
value={myPart}
|
||||||
|
precision={2}
|
||||||
|
valueStyle={{ color: myColor }}
|
||||||
|
suffix={item.asset?.type.tokenName}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title='Profit/Loss'
|
||||||
|
value={profitPart}
|
||||||
|
precision={2}
|
||||||
|
valueStyle={{ color: profitPart > 0 ? gains : losses }}
|
||||||
|
suffix={item.asset?.type.tokenSymbol}
|
||||||
|
prefix={profitPart > 0 ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
{progressBar}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
export const MarginTrading = () => {
|
export const MarginTrading = () => {
|
||||||
const [newPosition, setNewPosition] = useState<any>({ id: null });
|
const [newPosition, setNewPosition] = useState<IPosition>({ id: null, leverage: 1 });
|
||||||
|
|
||||||
const positions: any[] = [];
|
const positions: any[] = [];
|
||||||
return (
|
return (
|
||||||
|
@ -22,6 +127,7 @@ export const MarginTrading = () => {
|
||||||
<div>{LABELS.TRADING_TABLE_TITLE_ACTIONS}</div>
|
<div>{LABELS.TRADING_TABLE_TITLE_ACTIONS}</div>
|
||||||
</div>
|
</div>
|
||||||
<MarginTradePosition key={newPosition.id} item={newPosition} setItem={setNewPosition} />
|
<MarginTradePosition key={newPosition.id} item={newPosition} setItem={setNewPosition} />
|
||||||
|
<Breakdown item={newPosition} />
|
||||||
{positions.map((item) => (
|
{positions.map((item) => (
|
||||||
<MarginTradePosition key={item.id} item={item} />
|
<MarginTradePosition key={item.id} item={item} />
|
||||||
))}
|
))}
|
||||||
|
|
Loading…
Reference in New Issue