import React from 'react'; import PropTypes from 'prop-types'; import Grid from '@mui/material/Grid'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; import { makeStyles } from '@mui/material/styles'; import { attachStrategyOn, querySessions, querySessionSymbols, } from '../api/bbgo'; import TextField from '@mui/material/TextField'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormHelperText from '@mui/material/FormHelperText'; import InputLabel from '@mui/material/InputLabel'; import FormControl from '@mui/material/FormControl'; import Radio from '@mui/material/Radio'; import RadioGroup from '@mui/material/RadioGroup'; import FormLabel from '@mui/material/FormLabel'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import Alert from '@mui/lab/Alert'; import Box from '@mui/material/Box'; import NumberFormat from 'react-number-format'; function parseFloatValid(s) { if (s) { const f = parseFloat(s); if (!isNaN(f)) { return f; } } return null; } function parseFloatCall(s, cb) { if (s) { const f = parseFloat(s); if (!isNaN(f)) { cb(f); } } } function StandardNumberFormat(props) { const { inputRef, onChange, ...other } = props; return ( { onChange({ target: { name: props.name, value: values.value, }, }); }} thousandSeparator isNumericString /> ); } StandardNumberFormat.propTypes = { inputRef: PropTypes.func.isRequired, name: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, }; function PriceNumberFormat(props) { const { inputRef, onChange, ...other } = props; return ( { onChange({ target: { name: props.name, value: values.value, }, }); }} thousandSeparator isNumericString prefix="$" /> ); } PriceNumberFormat.propTypes = { inputRef: PropTypes.func.isRequired, name: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, }; const useStyles = makeStyles((theme) => ({ formControl: { marginTop: theme.spacing(1), marginBottom: theme.spacing(1), minWidth: 120, }, buttons: { display: 'flex', justifyContent: 'flex-end', marginTop: theme.spacing(2), paddingTop: theme.spacing(2), paddingBottom: theme.spacing(2), '& > *': { marginLeft: theme.spacing(1), }, }, })); export default function ConfigureGridStrategyForm({ onBack, onAdded }) { const classes = useStyles(); const [errors, setErrors] = React.useState({}); const [sessions, setSessions] = React.useState([]); const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]); const [selectedSessionName, setSelectedSessionName] = React.useState(null); const [selectedSymbol, setSelectedSymbol] = React.useState(''); const [quantityBy, setQuantityBy] = React.useState('fixedAmount'); const [upperPrice, setUpperPrice] = React.useState(30000.0); const [lowerPrice, setLowerPrice] = React.useState(10000.0); const [fixedAmount, setFixedAmount] = React.useState(100.0); const [fixedQuantity, setFixedQuantity] = React.useState(1.234); const [gridNumber, setGridNumber] = React.useState(20); const [profitSpread, setProfitSpread] = React.useState(100.0); const [response, setResponse] = React.useState({}); React.useEffect(() => { querySessions((sessions) => { setSessions(sessions); }); }, []); const handleAdd = (event) => { const payload = { symbol: selectedSymbol, gridNumber: parseFloatValid(gridNumber), profitSpread: parseFloatValid(profitSpread), upperPrice: parseFloatValid(upperPrice), lowerPrice: parseFloatValid(lowerPrice), }; switch (quantityBy) { case 'fixedQuantity': payload.quantity = parseFloatValid(fixedQuantity); break; case 'fixedAmount': payload.amount = parseFloatValid(fixedAmount); break; } if (!selectedSessionName) { setErrors({ session: true }); return; } if (!selectedSymbol) { setErrors({ symbol: true }); return; } console.log(payload); attachStrategyOn(selectedSessionName, 'grid', payload, (response) => { console.log(response); setResponse(response); if (onAdded) { setTimeout(onAdded, 3000); } }) .catch((err) => { console.error(err); setResponse(err.response.data); }) .finally(() => { setErrors({}); }); }; const handleQuantityBy = (event) => { setQuantityBy(event.target.value); }; const handleSessionChange = (event) => { const sessionName = event.target.value; setSelectedSessionName(sessionName); querySessionSymbols(sessionName, (symbols) => { setActiveSessionSymbols(symbols); }).catch((err) => { console.error(err); setResponse(err.response.data); }); }; const sessionMenuItems = sessions.map((session, index) => { return ( {session.name} ); }); const symbolMenuItems = activeSessionSymbols.map((symbol, index) => { return ( {symbol} ); }); return ( Add Grid Strategy Fixed price band grid strategy uses the fixed price band to place buy/sell orders. This strategy places sell orders above the current price, places buy orders below the current price. If any of the order is executed, then it will automatically place a new profit order on the reverse side. Session Select the exchange session you want to mount this strategy. Market Select the market you want to run this strategy { parseFloatCall(event.target.value, setUpperPrice); }} value={upperPrice} InputProps={{ inputComponent: PriceNumberFormat, }} /> { parseFloatCall(event.target.value, setLowerPrice); }} value={lowerPrice} InputProps={{ inputComponent: PriceNumberFormat, }} /> { parseFloatCall(event.target.value, setProfitSpread); }} value={profitSpread} InputProps={{ inputComponent: StandardNumberFormat, }} /> Order Quantity By } label="Fixed Amount" /> } label="Fixed Quantity" /> {quantityBy === 'fixedQuantity' ? ( { parseFloatCall(event.target.value, setFixedQuantity); }} value={fixedQuantity} InputProps={{ inputComponent: StandardNumberFormat, }} /> ) : null} {quantityBy === 'fixedAmount' ? ( { parseFloatCall(event.target.value, setFixedAmount); }} value={fixedAmount} InputProps={{ inputComponent: PriceNumberFormat, }} /> ) : null} { parseFloatCall(event.target.value, setGridNumber); }} value={gridNumber} InputProps={{ inputComponent: StandardNumberFormat, }} />
{response ? ( response.error ? ( {response.error} ) : response.success ? ( Strategy Added ) : null ) : null}
); }