query symbols and use react number format

This commit is contained in:
c9s 2021-02-03 01:27:25 +08:00
parent 578451bb51
commit f94a45de40
8 changed files with 317 additions and 11 deletions

View File

@ -27,12 +27,19 @@ export function testSessionConnection(session, cb) {
} }
export function querySessions(cb) { export function querySessions(cb) {
axios.get(baseURL + '/api/sessions', {}) return axios.get(baseURL + '/api/sessions', {})
.then(response => { .then(response => {
cb(response.data.sessions) cb(response.data.sessions)
}); });
} }
export function querySessionSymbols(sessionName, cb) {
return axios.get(baseURL + `/api/sessions/${ sessionName }/symbols`, {})
.then(response => {
cb(response.data.symbols)
});
}
export function queryTrades(params, cb) { export function queryTrades(params, cb) {
axios.get(baseURL + '/api/trades', {params: params}) axios.get(baseURL + '/api/trades', {params: params})
.then(response => { .then(response => {

View File

@ -113,7 +113,7 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<Grid item xs={12}> <Grid item xs={12}>
<FormControl className={classes.formControl}> <FormControl className={classes.formControl}>
<InputLabel id="exchange-type-select-label">Exchange Type</InputLabel> <InputLabel id="exchange-type-select-label">Exchange</InputLabel>
<Select <Select
labelId="exchange-type-select-label" labelId="exchange-type-select-label"
id="exchange-type-select" id="exchange-type-select"

View File

@ -52,10 +52,10 @@ export default function ConfigureDatabaseForm({ onConfigured }) {
setTimeout(onConfigured, 3000); setTimeout(onConfigured, 3000);
} }
}).catch((reason) => { }).catch((err) => {
console.error(reason); console.error(err);
setTesting(false); setTesting(false);
setTestResponse(reason); setTestResponse(err.response.data);
}) })
} }
@ -65,10 +65,10 @@ export default function ConfigureDatabaseForm({ onConfigured }) {
console.log(response) console.log(response)
setTesting(false) setTesting(false)
setTestResponse(response) setTestResponse(response)
}).catch((reason) => { }).catch((err) => {
console.error(reason) console.error(err)
setTesting(false) setTesting(false)
setTestResponse(reason) setTestResponse(err.response.data)
}) })
}; };

View File

@ -0,0 +1,286 @@
import React from 'react';
import PropTypes from 'prop-types';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import {querySessions, querySessionSymbols} from "../api/bbgo";
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormHelperText from '@material-ui/core/FormHelperText';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import Checkbox from '@material-ui/core/Checkbox';
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import Alert from '@material-ui/lab/Alert';
import Box from "@material-ui/core/Box";
import NumberFormat from 'react-number-format';
function NumberFormatCustom(props) {
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
prefix="$"
/>
);
}
NumberFormatCustom.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, onConfigured}) {
const classes = useStyles();
const [sessions, setSessions] = React.useState([]);
const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]);
const [selectedSessionName, setSelectedSessionName] = React.useState(null);
const [selectedSymbol, setSelectedSymbol] = React.useState('');
const [upperPrice, setUpperPrice] = React.useState(30000.0);
const [lowerPrice, setLowerPrice] = React.useState(10000.0);
const [response, setResponse] = React.useState({});
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
});
}, [])
const handleAdd = (event) => {
};
const handleSessionChange = (event) => {
const sessionName = event.target.value;
setSelectedSessionName(sessionName)
querySessionSymbols(sessionName, (symbols) => {
setActiveSessionSymbols(symbols);
})
};
const sessionMenuItems = sessions.map((session, index) => {
return (
<MenuItem key={session.name} value={session.name}>
{session.name}
</MenuItem>
);
})
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
return (
<MenuItem key={symbol} value={symbol}>
{symbol}
</MenuItem>
);
})
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Add Grid Strategy
</Typography>
<Typography variant="body1" gutterBottom>
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.
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl className={classes.formControl}>
<InputLabel id="session-select-label">Session</InputLabel>
<Select
labelId="session-select-label"
id="session-select"
value={selectedSessionName ? selectedSessionName : ''}
onChange={handleSessionChange}
>
{sessionMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the exchange session you want to mount this strategy.
</FormHelperText>
</Grid>
<Grid item xs={12}>
<FormControl className={classes.formControl}>
<InputLabel id="symbol-select-label">Market</InputLabel>
<Select
labelId="symbol-select-label"
id="symbol-select"
value={selectedSymbol ? selectedSymbol : ''}
onChange={(event) => {
setSelectedSymbol(event.target.value);
}}>
{symbolMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the market you want to run this strategy
</FormHelperText>
</Grid>
<Grid item xs={12}>
<TextField
id="upperPrice"
name="upper_price"
label="Upper Price"
fullWidth
required
onChange={(event) => {
if (event.target.value) {
const v = parseFloat(event.target.value)
if (!isNaN(v)) {
setUpperPrice(v);
}
}
}}
value={upperPrice}
InputProps={{
inputComponent: NumberFormatCustom,
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="lowerPrice"
name="lower_price"
label="Lower Price"
fullWidth
required
onChange={(event) => {
if (event.target.value) {
const v = parseFloat(event.target.value)
if (!isNaN(v)) {
setLowerPrice(v);
}
}
}}
value={lowerPrice}
InputProps={{
inputComponent: NumberFormatCustom,
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormControlLabel
control={<Checkbox color="secondary" name="custom_session_name"
onChange={(event) => {
}} value="1"/>}
label="Custom exchange session name"
/>
<FormHelperText id="session-name-helper-text">
By default, the session name will be the exchange type name,
e.g. <code>binance</code> or <code>max</code>.<br/>
If you're using multiple exchange sessions, you might need to custom the session name. <br/>
This is for advanced users.
</FormHelperText>
</Grid>
<Grid item xs={12}>
<TextField id="key" name="api_key" label="API Key"
fullWidth
required
onChange={(event) => {
}}
/>
</Grid>
<Grid item xs={12}>
<TextField id="secret" name="api_secret" label="API Secret"
fullWidth
required
onChange={(event) => {
}}
/>
</Grid>
</Grid>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleAdd}
>
Add
</Button>
</div>
{
response ? response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Exchange Session Added</Alert>
</Box>
) : null : null
}
</React.Fragment>
);
}

View File

@ -10,7 +10,6 @@ import PowerIcon from '@material-ui/icons/Power';
import {makeStyles} from '@material-ui/core/styles'; import {makeStyles} from '@material-ui/core/styles';
import {querySessions} from "../api/bbgo"; import {querySessions} from "../api/bbgo";
import {Power} from "@material-ui/icons";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
formControl: { formControl: {

View File

@ -20,7 +20,8 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"next": "^10.0.5", "next": "^10.0.5",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1" "react-dom": "^17.0.1",
"react-number-format": "^4.4.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^14.14.22", "@types/node": "^14.14.22",

View File

@ -11,6 +11,7 @@ import StepLabel from '@material-ui/core/StepLabel';
import ConfigureDatabaseForm from "../../components/ConfigureDatabaseForm"; import ConfigureDatabaseForm from "../../components/ConfigureDatabaseForm";
import AddExchangeSessionForm from "../../components/AddExchangeSessionForm"; import AddExchangeSessionForm from "../../components/AddExchangeSessionForm";
import ReviewSessions from "../../components/ReviewSessions"; import ReviewSessions from "../../components/ReviewSessions";
import ConfigureGridStrategyForm from "../../components/ConfigureGridStrategyForm";
import PlainLayout from '../../layouts/PlainLayout'; import PlainLayout from '../../layouts/PlainLayout';
@ -20,7 +21,7 @@ const useStyles = makeStyles((theme) => ({
}, },
})); }));
const steps = ['Configure Database', 'Add Exchange Session', 'Configure Strategy', 'Restart BBGO']; const steps = ['Configure Database', 'Add Exchange Session', 'Review Sessions', 'Configure Strategy', 'Save Config and Restart'];
function getStepContent(step, setActiveStep) { function getStepContent(step, setActiveStep) {
switch (step) { switch (step) {
@ -41,6 +42,11 @@ function getStepContent(step, setActiveStep) {
setActiveStep(3) setActiveStep(3)
}}/> }}/>
case 3: case 3:
return (
<ConfigureGridStrategyForm />
);
case 4:
return; return;
default: default:
throw new Error('Unknown step'); throw new Error('Unknown step');

View File

@ -4096,6 +4096,13 @@ react-motion@^0.5.2:
prop-types "^15.5.8" prop-types "^15.5.8"
raf "^3.1.0" raf "^3.1.0"
react-number-format@^4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-4.4.4.tgz#2a7f50be404f990ec15855cc6babfeae1be16351"
integrity sha512-/MuF1GOs1Z0xBaQie8+TTqCxUTT8xxjc6RqIFVWcWB6FM8GNIenSh2ayzN6Y1J2WMcXUDVwMdDXjweHKVjXm/w==
dependencies:
prop-types "^15.7.2"
react-refresh@0.8.3: react-refresh@0.8.3:
version "0.8.3" version "0.8.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"