|
|
@ -1,11 +1,8 @@ |
|
|
|
import { |
|
|
|
Button, |
|
|
|
Container, |
|
|
|
Flex, |
|
|
|
Grid, |
|
|
|
GridItem, |
|
|
|
Flex, Grid, GridItem, |
|
|
|
HStack, |
|
|
|
Stack, |
|
|
|
Tab, |
|
|
|
TabList, |
|
|
|
TabPanel, |
|
|
@ -15,13 +12,13 @@ import { |
|
|
|
useToast, |
|
|
|
VStack, |
|
|
|
} from "@chakra-ui/react"; |
|
|
|
import React, { useState } from "react"; |
|
|
|
import { useAsync } from "react-async"; |
|
|
|
import { useEventSource } from "react-sse-hooks"; |
|
|
|
import { CfdTable } from "./components/cfdtables/CfdTable"; |
|
|
|
import React, {useState} from "react"; |
|
|
|
import {useAsync} from "react-async"; |
|
|
|
import {useEventSource} from "react-sse-hooks"; |
|
|
|
import {CfdTable} from "./components/cfdtables/CfdTable"; |
|
|
|
import CurrencyInputField from "./components/CurrencyInputField"; |
|
|
|
import useLatestEvent from "./components/Hooks"; |
|
|
|
import { Cfd, Order, WalletInfo } from "./components/Types"; |
|
|
|
import {Cfd, Order, WalletInfo} from "./components/Types"; |
|
|
|
import Wallet from "./components/Wallet"; |
|
|
|
|
|
|
|
interface CfdOrderRequestPayload { |
|
|
@ -40,7 +37,7 @@ interface MarginResponse { |
|
|
|
} |
|
|
|
|
|
|
|
async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { |
|
|
|
let res = await fetch(`/api/cfd`, { method: "POST", body: JSON.stringify(payload) }); |
|
|
|
let res = await fetch(`/api/cfd`, {method: "POST", body: JSON.stringify(payload)}); |
|
|
|
|
|
|
|
if (!res.status.toString().startsWith("2")) { |
|
|
|
throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); |
|
|
@ -48,7 +45,7 @@ async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { |
|
|
|
} |
|
|
|
|
|
|
|
async function getMargin(payload: MarginRequestPayload): Promise<MarginResponse> { |
|
|
|
let res = await fetch(`/api/calculate/margin`, { method: "POST", body: JSON.stringify(payload) }); |
|
|
|
let res = await fetch(`/api/calculate/margin`, {method: "POST", body: JSON.stringify(payload)}); |
|
|
|
|
|
|
|
if (!res.status.toString().startsWith("2")) { |
|
|
|
throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); |
|
|
@ -58,7 +55,7 @@ async function getMargin(payload: MarginRequestPayload): Promise<MarginResponse> |
|
|
|
} |
|
|
|
|
|
|
|
export default function App() { |
|
|
|
let source = useEventSource({ source: "/api/feed" }); |
|
|
|
let source = useEventSource({source: "/api/feed"}); |
|
|
|
|
|
|
|
const cfdsOrUndefined = useLatestEvent<Cfd[]>(source, "cfds"); |
|
|
|
let cfds = cfdsOrUndefined ? cfdsOrUndefined! : []; |
|
|
@ -69,7 +66,7 @@ export default function App() { |
|
|
|
let [quantity, setQuantity] = useState("0"); |
|
|
|
let [margin, setMargin] = useState("0"); |
|
|
|
|
|
|
|
let { run: calculateMargin } = useAsync({ |
|
|
|
let {run: calculateMargin} = useAsync({ |
|
|
|
deferFn: async ([payload]: any[]) => { |
|
|
|
try { |
|
|
|
let res = await getMargin(payload as MarginRequestPayload); |
|
|
@ -91,7 +88,7 @@ export default function App() { |
|
|
|
const format = (val: any) => `$` + val; |
|
|
|
const parse = (val: any) => val.replace(/^\$/, ""); |
|
|
|
|
|
|
|
let { run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest } = useAsync({ |
|
|
|
let {run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest} = useAsync({ |
|
|
|
deferFn: async ([payload]: any[]) => { |
|
|
|
try { |
|
|
|
await postCfdOrderRequest(payload as CfdOrderRequestPayload); |
|
|
@ -120,87 +117,88 @@ export default function App() { |
|
|
|
|
|
|
|
return ( |
|
|
|
<Container maxWidth="120ch" marginTop="1rem"> |
|
|
|
<Grid templateColumns="repeat(6, 1fr)" gap={4}> |
|
|
|
<GridItem colStart={5} colSpan={2}> |
|
|
|
<Wallet walletInfo={walletInfo} /> |
|
|
|
<VStack spacing={5} shadow={"md"} padding={5} align={"stretch"}> |
|
|
|
<HStack> |
|
|
|
{/*TODO: Do we need this? does it make sense to only display the price from the order?*/} |
|
|
|
<Text align={"left"}>Current Price (Kraken):</Text> |
|
|
|
<Text>tbd</Text> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text align={"left"}>Order Price:</Text> |
|
|
|
<Text>{order?.price}</Text> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text>Quantity:</Text> |
|
|
|
<CurrencyInputField |
|
|
|
onChange={(valueString: string) => { |
|
|
|
setQuantity(parse(valueString)); |
|
|
|
|
|
|
|
if (!order) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
let quantity = valueString ? Number.parseFloat(valueString) : 0; |
|
|
|
let payload: MarginRequestPayload = { |
|
|
|
leverage: order.leverage, |
|
|
|
price: order.price, |
|
|
|
quantity, |
|
|
|
<VStack> |
|
|
|
<Grid templateColumns="repeat(6, 1fr)" gap={4}> |
|
|
|
<GridItem colStart={1} colSpan={2}> |
|
|
|
<Wallet walletInfo={walletInfo}/> |
|
|
|
<VStack shadow={"md"} padding={5} align="stretch" spacing={4}> |
|
|
|
<HStack> |
|
|
|
{/*TODO: Do we need this? does it make sense to only display the price from the order?*/} |
|
|
|
<Text align={"left"}>Current Price (Kraken):</Text> |
|
|
|
<Text>tbd</Text> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text align={"left"}>Order Price:</Text> |
|
|
|
<Text>{order?.price}</Text> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text>Quantity:</Text> |
|
|
|
<CurrencyInputField |
|
|
|
onChange={(valueString: string) => { |
|
|
|
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); |
|
|
|
}} |
|
|
|
value={format(quantity)} |
|
|
|
/> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text>Margin in BTC:</Text> |
|
|
|
<Text>{margin}</Text> |
|
|
|
</HStack> |
|
|
|
<Text>Leverage:</Text> |
|
|
|
{/* TODO: consider button group */} |
|
|
|
<Flex justifyContent={"space-between"}> |
|
|
|
<Button disabled={true}>x1</Button> |
|
|
|
<Button disabled={true}>x2</Button> |
|
|
|
<Button colorScheme="blue" variant="solid">x{order?.leverage}</Button> |
|
|
|
</Flex> |
|
|
|
{<Button |
|
|
|
disabled={isCreatingNewOrderRequest || !order} |
|
|
|
variant={"solid"} |
|
|
|
colorScheme={"blue"} |
|
|
|
onClick={() => { |
|
|
|
let payload: CfdOrderRequestPayload = { |
|
|
|
order_id: order!.id, |
|
|
|
quantity: Number.parseFloat(quantity), |
|
|
|
}; |
|
|
|
calculateMargin(payload); |
|
|
|
makeNewOrderRequest(payload); |
|
|
|
}} |
|
|
|
value={format(quantity)} |
|
|
|
/> |
|
|
|
</HStack> |
|
|
|
<HStack> |
|
|
|
<Text>Margin in BTC:</Text> |
|
|
|
<Text>{margin}</Text> |
|
|
|
</HStack> |
|
|
|
<Text>Leverage:</Text> |
|
|
|
{/* TODO: consider button group */} |
|
|
|
<Flex justifyContent={"space-between"}> |
|
|
|
<Button disabled={true}>x1</Button> |
|
|
|
<Button disabled={true}>x2</Button> |
|
|
|
<Button colorScheme="blue" variant="solid">x{order?.leverage}</Button> |
|
|
|
</Flex> |
|
|
|
{<Button |
|
|
|
disabled={isCreatingNewOrderRequest || !order} |
|
|
|
variant={"solid"} |
|
|
|
colorScheme={"blue"} |
|
|
|
onClick={() => { |
|
|
|
let payload: CfdOrderRequestPayload = { |
|
|
|
order_id: order!.id, |
|
|
|
quantity: Number.parseFloat(quantity), |
|
|
|
}; |
|
|
|
makeNewOrderRequest(payload); |
|
|
|
}} |
|
|
|
> |
|
|
|
BUY |
|
|
|
</Button>} |
|
|
|
</VStack> |
|
|
|
</GridItem> |
|
|
|
</Grid> |
|
|
|
<Tabs> |
|
|
|
<TabList> |
|
|
|
<Tab>Running [{running.length}]</Tab> |
|
|
|
<Tab>Closed [{closed.length}]</Tab> |
|
|
|
<Tab>Unsorted [{unsorted.length}] (should be empty)</Tab> |
|
|
|
</TabList> |
|
|
|
|
|
|
|
<TabPanels> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={running} /> |
|
|
|
</TabPanel> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={closed} /> |
|
|
|
</TabPanel> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={unsorted} /> |
|
|
|
</TabPanel> |
|
|
|
</TabPanels> |
|
|
|
</Tabs> |
|
|
|
> |
|
|
|
BUY |
|
|
|
</Button>} |
|
|
|
</VStack> |
|
|
|
|
|
|
|
</GridItem> |
|
|
|
</Grid> |
|
|
|
<Tabs> |
|
|
|
<TabList> |
|
|
|
<Tab>Running [{running.length}]</Tab> |
|
|
|
<Tab>Closed [{closed.length}]</Tab> |
|
|
|
<Tab>Unsorted [{unsorted.length}] (should be empty)</Tab> |
|
|
|
</TabList> |
|
|
|
|
|
|
|
<TabPanels> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={running}/> |
|
|
|
</TabPanel> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={closed}/> |
|
|
|
</TabPanel> |
|
|
|
<TabPanel> |
|
|
|
<CfdTable data={unsorted}/> |
|
|
|
</TabPanel> |
|
|
|
</TabPanels> |
|
|
|
</Tabs> |
|
|
|
</VStack> |
|
|
|
</Container> |
|
|
|
); |
|
|
|
} |
|
|
|