Browse Source

Merge #716

716: Improve margin calc and quantity input r=da-kami a=thomaseizinger

Quantity input change now looks like this:

![image](https://user-images.githubusercontent.com/5486389/143510846-77af9b9f-761d-4e29-9e3e-df4e90fdd741.png)

Fixes #688.

Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
debug-collab-settlement
bors[bot] 3 years ago
committed by GitHub
parent
commit
86348059d4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 55
      taker-frontend/src/App.tsx
  2. 81
      taker-frontend/src/components/Trade.tsx
  3. 11
      taker-frontend/src/useDebouncedEffect.ts

55
taker-frontend/src/App.tsx

@ -11,7 +11,7 @@ import {
VStack,
} from "@chakra-ui/react";
import * as React from "react";
import { useEffect, useState } from "react";
import { useState } from "react";
import { Route, Routes } from "react-router-dom";
import { useEventSource } from "react-sse-hooks";
import useWebSocket from "react-use-websocket";
@ -33,6 +33,7 @@ import {
StateGroupKey,
WalletInfo,
} from "./types";
import useDebouncedEffect from "./useDebouncedEffect";
import useLatestEvent from "./useLatestEvent";
import usePostRequest from "./usePostRequest";
@ -66,7 +67,7 @@ export const App = () => {
const connectedToMaker = connectedToMakerOrUndefined ? connectedToMakerOrUndefined! : false;
let [quantity, setQuantity] = useState("0");
let [margin, setMargin] = useState("0");
let [margin, setMargin] = useState(0);
let [userHasEdited, setUserHasEdited] = useState(false);
const { price: askPrice, min_quantity, max_quantity, leverage, liquidation_price: liquidationPrice } = order || {};
@ -76,30 +77,28 @@ export const App = () => {
let [calculateMargin] = usePostRequest<MarginRequestPayload, MarginResponse>(
"/api/calculate/margin",
(response) => {
setMargin(response.margin.toString());
setMargin(response.margin);
},
);
let [makeNewOrderRequest, isCreatingNewOrderRequest] = usePostRequest<CfdOrderRequestPayload>("/api/cfd/order");
useEffect(() => {
if (!order) {
return;
}
let quantity = effectiveQuantity ? Number.parseFloat(effectiveQuantity) : 0;
let payload: MarginRequestPayload = {
leverage: order.leverage,
price: order.price,
quantity,
};
calculateMargin(payload);
}, // Eslint demands us to include `calculateMargin` in the list of dependencies.
// We don't want that as we will end up in an endless loop. It is safe to ignore `calculateMargin` because
// nothing in `calculateMargin` depends on outside values, i.e. is guaranteed to be stable.
// eslint-disable-next-line react-hooks/exhaustive-deps
[margin, effectiveQuantity, order]);
useDebouncedEffect(
() => {
if (!order) {
return;
}
let quantity = effectiveQuantity ? Number.parseFloat(effectiveQuantity) : 0;
let payload: MarginRequestPayload = {
leverage: order.leverage,
price: order.price,
quantity,
};
const format = (val: any) => `$` + val;
const parse = (val: any) => val.replace(/^\$/, "");
calculateMargin(payload);
},
[margin, effectiveQuantity, order],
500,
);
return (
<>
@ -117,7 +116,7 @@ export const App = () => {
<Trade
connectedToMaker={connectedToMaker}
orderId={order?.id}
quantity={format(effectiveQuantity)}
quantity={effectiveQuantity}
maxQuantity={max_quantity || 0}
minQuantity={min_quantity || 0}
referencePrice={referencePrice}
@ -127,17 +126,7 @@ export const App = () => {
liquidationPrice={liquidationPrice}
onQuantityChange={(valueString: string) => {
setUserHasEdited(true);
setQuantity(parse(valueString));
if (!order) {
return;
}
let quantity = valueString ? Number.parseFloat(valueString) : 0;
let payload: MarginRequestPayload = {
leverage: order.leverage,
price: order.price,
quantity,
};
calculateMargin(payload);
setQuantity(valueString);
}}
onLongSubmit={makeNewOrderRequest}
isLongSubmitting={isCreatingNewOrderRequest}

81
taker-frontend/src/components/Trade.tsx

@ -15,6 +15,8 @@ import {
Grid,
GridItem,
HStack,
InputGroup,
InputLeftAddon,
Modal,
ModalBody,
ModalCloseButton,
@ -56,7 +58,7 @@ interface TradeProps {
maxQuantity: number;
referencePrice?: number;
askPrice?: number;
margin?: string;
margin: number;
leverage?: number;
quantity: string;
liquidationPrice?: number;
@ -79,37 +81,34 @@ function AlertBox({ title, description }: AlertBoxProps) {
</Alert>);
}
const Trade = (
{
connectedToMaker,
minQuantity,
maxQuantity,
referencePrice: referencePriceAsNumber,
askPrice: askPriceAsNumber,
quantity,
onQuantityChange,
margin: marginAsNumber,
leverage,
liquidationPrice: liquidationPriceAsNumber,
onLongSubmit,
isLongSubmitting,
orderId,
walletBalance,
}: TradeProps,
) => {
export default function Trade({
connectedToMaker,
minQuantity,
maxQuantity,
referencePrice: referencePriceAsNumber,
askPrice: askPriceAsNumber,
quantity,
onQuantityChange,
margin,
leverage,
liquidationPrice: liquidationPriceAsNumber,
onLongSubmit,
isLongSubmitting,
orderId,
walletBalance,
}: TradeProps) {
let outerCircleBg = useColorModeValue("gray.100", "gray.700");
let innerCircleBg = useColorModeValue("gray.200", "gray.600");
const referencePrice = `$${referencePriceAsNumber?.toLocaleString() || "0.0"}`;
const askPrice = `$${askPriceAsNumber?.toLocaleString() || "0.0"}`;
const liquidationPrice = `$${liquidationPriceAsNumber?.toLocaleString() || "0.0"}`;
const margin = `${marginAsNumber?.toLocaleString() || "0.0"}`;
const { isOpen, onOpen, onClose } = useDisclosure();
const parse = (val: any) => Number.parseInt(val.replace(/^\$/, ""));
const balanceTooLow = walletBalance && walletBalance < parse(margin);
const balanceTooLow = walletBalance && walletBalance < margin;
const quantityTooHigh = maxQuantity < parse(quantity);
const quantityTooLow = minQuantity > parse(quantity);
const quantityGreaterZero = parse(quantity) > 0;
@ -223,12 +222,12 @@ const Trade = (
<ModalBody>
<Table variant="striped" colorScheme="gray" size="sm">
<TableCaption>
By submitting, {margin} will be locked on-chain in a contract.
By submitting, {margin} will be locked on-chain in a contract.
</TableCaption>
<Tbody>
<Tr>
<Td><Text as={"b"}>Margin</Text></Td>
<Td>{margin}</Td>
<Td>{margin}</Td>
</Tr>
<Tr>
<Td><Text as={"b"}>Leverage</Text></Td>
@ -271,8 +270,7 @@ const Trade = (
</Grid>
</Center>
);
};
export default Trade;
}
interface QuantityProps {
min: number;
@ -285,19 +283,22 @@ function Quantity({ min, max, onChange, quantity }: QuantityProps) {
return (
<FormControl id="quantity">
<FormLabel>Quantity</FormLabel>
<NumberInput
min={min}
max={max}
default={min}
onChange={onChange}
value={quantity}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<InputGroup>
<InputLeftAddon>$</InputLeftAddon>
<NumberInput
min={min}
max={max}
default={min}
onChange={onChange}
value={quantity}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</InputGroup>
<FormHelperText>How much do you want to buy or sell?</FormHelperText>
</FormControl>
);
@ -330,7 +331,7 @@ function Leverage({ leverage }: LeverageProps) {
}
interface MarginProps {
margin?: string;
margin: number;
}
function Margin({ margin }: MarginProps) {
@ -338,7 +339,7 @@ function Margin({ margin }: MarginProps) {
<VStack>
<HStack>
<Text as={"b"}>Required margin:</Text>
<Text>{margin}</Text>
<Text>{margin}</Text>
</HStack>
<Text fontSize={"sm"} color={"darkgrey"}>The collateral you will need to provide</Text>
</VStack>

11
taker-frontend/src/useDebouncedEffect.ts

@ -0,0 +1,11 @@
import { useEffect } from "react";
// Copied from: https://stackoverflow.com/a/61127960/2489334
export default function useDebouncedEffect(effect: () => void, deps: any[] | undefined, delay: number) {
return useEffect(() => {
const handler = setTimeout(() => effect(), delay);
return () => clearTimeout(handler);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...deps || [], delay]);
}
Loading…
Cancel
Save