mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 06:53:52 +00:00
format js codes
This commit is contained in:
parent
fd60e9e366
commit
4e9a915cf6
|
@ -1,114 +1,121 @@
|
|||
import axios from "axios";
|
||||
import axios from 'axios';
|
||||
|
||||
const baseURL = process.env.NODE_ENV === "development" ? "http://localhost:8080" : ""
|
||||
const baseURL =
|
||||
process.env.NODE_ENV === 'development' ? 'http://localhost:8080' : '';
|
||||
|
||||
export function ping(cb) {
|
||||
return axios.get(baseURL + '/api/ping').then(response => {
|
||||
cb(response.data)
|
||||
});
|
||||
return axios.get(baseURL + '/api/ping').then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryOutboundIP(cb) {
|
||||
return axios.get(baseURL + '/api/outbound-ip').then(response => {
|
||||
cb(response.data.outboundIP)
|
||||
});
|
||||
return axios.get(baseURL + '/api/outbound-ip').then((response) => {
|
||||
cb(response.data.outboundIP);
|
||||
});
|
||||
}
|
||||
|
||||
export function querySyncStatus(cb) {
|
||||
return axios.get(baseURL + '/api/environment/syncing').then(response => {
|
||||
cb(response.data.syncing)
|
||||
});
|
||||
return axios.get(baseURL + '/api/environment/syncing').then((response) => {
|
||||
cb(response.data.syncing);
|
||||
});
|
||||
}
|
||||
|
||||
export function testDatabaseConnection(params, cb) {
|
||||
return axios.post(baseURL + '/api/setup/test-db', params).then(response => {
|
||||
cb(response.data)
|
||||
});
|
||||
return axios.post(baseURL + '/api/setup/test-db', params).then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function configureDatabase(params, cb) {
|
||||
return axios.post(baseURL + '/api/setup/configure-db', params).then(response => {
|
||||
cb(response.data)
|
||||
return axios
|
||||
.post(baseURL + '/api/setup/configure-db', params)
|
||||
.then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function saveConfig(cb) {
|
||||
return axios.post(baseURL + '/api/setup/save').then(response => {
|
||||
cb(response.data)
|
||||
});
|
||||
return axios.post(baseURL + '/api/setup/save').then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function setupRestart(cb) {
|
||||
return axios.post(baseURL + '/api/setup/restart').then(response => {
|
||||
cb(response.data)
|
||||
});
|
||||
return axios.post(baseURL + '/api/setup/restart').then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function addSession(session, cb) {
|
||||
return axios.post(baseURL + '/api/sessions', session).then(response => {
|
||||
cb(response.data || [])
|
||||
});
|
||||
return axios.post(baseURL + '/api/sessions', session).then((response) => {
|
||||
cb(response.data || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function attachStrategyOn(session, strategyID, strategy, cb) {
|
||||
return axios.post(baseURL + `/api/setup/strategy/single/${strategyID}/session/${session}`, strategy).then(response => {
|
||||
cb(response.data)
|
||||
return axios
|
||||
.post(
|
||||
baseURL + `/api/setup/strategy/single/${strategyID}/session/${session}`,
|
||||
strategy
|
||||
)
|
||||
.then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function testSessionConnection(session, cb) {
|
||||
return axios.post(baseURL + '/api/sessions/test', session).then(response => {
|
||||
cb(response.data)
|
||||
return axios
|
||||
.post(baseURL + '/api/sessions/test', session)
|
||||
.then((response) => {
|
||||
cb(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryStrategies(cb) {
|
||||
return axios.get(baseURL + '/api/strategies/single').then(response => {
|
||||
cb(response.data.strategies || [])
|
||||
});
|
||||
return axios.get(baseURL + '/api/strategies/single').then((response) => {
|
||||
cb(response.data.strategies || []);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function querySessions(cb) {
|
||||
return axios.get(baseURL + '/api/sessions', {})
|
||||
.then(response => {
|
||||
cb(response.data.sessions || [])
|
||||
});
|
||||
return axios.get(baseURL + '/api/sessions', {}).then((response) => {
|
||||
cb(response.data.sessions || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function querySessionSymbols(sessionName, cb) {
|
||||
return axios.get(baseURL + `/api/sessions/${sessionName}/symbols`, {})
|
||||
.then(response => {
|
||||
cb(response.data.symbols || [])
|
||||
});
|
||||
return axios
|
||||
.get(baseURL + `/api/sessions/${sessionName}/symbols`, {})
|
||||
.then((response) => {
|
||||
cb(response.data.symbols || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryTrades(params, cb) {
|
||||
axios.get(baseURL + '/api/trades', {params: params})
|
||||
.then(response => {
|
||||
cb(response.data.trades || [])
|
||||
});
|
||||
axios.get(baseURL + '/api/trades', { params: params }).then((response) => {
|
||||
cb(response.data.trades || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryClosedOrders(params, cb) {
|
||||
axios.get(baseURL + '/api/orders/closed', {params: params})
|
||||
.then(response => {
|
||||
cb(response.data.orders || [])
|
||||
});
|
||||
axios
|
||||
.get(baseURL + '/api/orders/closed', { params: params })
|
||||
.then((response) => {
|
||||
cb(response.data.orders || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryAssets(cb) {
|
||||
axios.get(baseURL + '/api/assets', {})
|
||||
.then(response => {
|
||||
cb(response.data.assets || [])
|
||||
});
|
||||
axios.get(baseURL + '/api/assets', {}).then((response) => {
|
||||
cb(response.data.assets || []);
|
||||
});
|
||||
}
|
||||
|
||||
export function queryTradingVolume(params, cb) {
|
||||
axios.get(baseURL + '/api/trading-volume', {params: params})
|
||||
.then(response => {
|
||||
cb(response.data.tradingVolumes || [])
|
||||
});
|
||||
axios
|
||||
.get(baseURL + '/api/trading-volume', { params: params })
|
||||
.then((response) => {
|
||||
cb(response.data.tradingVolumes || []);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,297 +20,323 @@ import Alert from '@material-ui/lab/Alert';
|
|||
import VisibilityOff from '@material-ui/icons/VisibilityOff';
|
||||
import Visibility from '@material-ui/icons/Visibility';
|
||||
|
||||
import {addSession, testSessionConnection} from '../api/bbgo';
|
||||
import { addSession, testSessionConnection } from '../api/bbgo';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
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),
|
||||
}
|
||||
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 AddExchangeSessionForm({onBack, onAdded}) {
|
||||
const classes = useStyles();
|
||||
const [exchangeType, setExchangeType] = React.useState('max');
|
||||
const [customSessionName, setCustomSessionName] = React.useState(false);
|
||||
const [sessionName, setSessionName] = React.useState(exchangeType);
|
||||
export default function AddExchangeSessionForm({ onBack, onAdded }) {
|
||||
const classes = useStyles();
|
||||
const [exchangeType, setExchangeType] = React.useState('max');
|
||||
const [customSessionName, setCustomSessionName] = React.useState(false);
|
||||
const [sessionName, setSessionName] = React.useState(exchangeType);
|
||||
|
||||
const [testing, setTesting] = React.useState(false);
|
||||
const [testResponse, setTestResponse] = React.useState(null);
|
||||
const [response, setResponse] = React.useState(null);
|
||||
const [testing, setTesting] = React.useState(false);
|
||||
const [testResponse, setTestResponse] = React.useState(null);
|
||||
const [response, setResponse] = React.useState(null);
|
||||
|
||||
const [apiKey, setApiKey] = React.useState('');
|
||||
const [apiSecret, setApiSecret] = React.useState('');
|
||||
const [apiKey, setApiKey] = React.useState('');
|
||||
const [apiSecret, setApiSecret] = React.useState('');
|
||||
|
||||
const [showApiKey, setShowApiKey] = React.useState(false);
|
||||
const [showApiSecret, setShowApiSecret] = React.useState(false);
|
||||
const [showApiKey, setShowApiKey] = React.useState(false);
|
||||
const [showApiSecret, setShowApiSecret] = React.useState(false);
|
||||
|
||||
const [isMargin, setIsMargin] = React.useState(false);
|
||||
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
|
||||
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState("");
|
||||
const [isMargin, setIsMargin] = React.useState(false);
|
||||
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
|
||||
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState('');
|
||||
|
||||
const resetTestResponse = () => {
|
||||
setTestResponse(null)
|
||||
}
|
||||
const resetTestResponse = () => {
|
||||
setTestResponse(null);
|
||||
};
|
||||
|
||||
const handleExchangeTypeChange = (event) => {
|
||||
setExchangeType(event.target.value);
|
||||
setSessionName(event.target.value);
|
||||
resetTestResponse()
|
||||
const handleExchangeTypeChange = (event) => {
|
||||
setExchangeType(event.target.value);
|
||||
setSessionName(event.target.value);
|
||||
resetTestResponse();
|
||||
};
|
||||
|
||||
const createSessionConfig = () => {
|
||||
return {
|
||||
name: sessionName,
|
||||
exchange: exchangeType,
|
||||
key: apiKey,
|
||||
secret: apiSecret,
|
||||
margin: isMargin,
|
||||
envVarPrefix: exchangeType.toUpperCase(),
|
||||
isolatedMargin: isIsolatedMargin,
|
||||
isolatedMarginSymbol: isolatedMarginSymbol,
|
||||
};
|
||||
};
|
||||
|
||||
const createSessionConfig = () => {
|
||||
return {
|
||||
name: sessionName,
|
||||
exchange: exchangeType,
|
||||
key: apiKey,
|
||||
secret: apiSecret,
|
||||
margin: isMargin,
|
||||
envVarPrefix: exchangeType.toUpperCase(),
|
||||
isolatedMargin: isIsolatedMargin,
|
||||
isolatedMarginSymbol: isolatedMarginSymbol,
|
||||
}
|
||||
}
|
||||
const handleAdd = (event) => {
|
||||
const payload = createSessionConfig();
|
||||
addSession(payload, (response) => {
|
||||
setResponse(response);
|
||||
if (onAdded) {
|
||||
setTimeout(onAdded, 3000);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
setResponse(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
const handleAdd = (event) => {
|
||||
const payload = createSessionConfig()
|
||||
addSession(payload, (response) => {
|
||||
setResponse(response)
|
||||
if (onAdded) {
|
||||
setTimeout(onAdded, 3000)
|
||||
const handleTestConnection = (event) => {
|
||||
const payload = createSessionConfig();
|
||||
setTesting(true);
|
||||
testSessionConnection(payload, (response) => {
|
||||
console.log(response);
|
||||
setTesting(false);
|
||||
setTestResponse(response);
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
setTesting(false);
|
||||
setTestResponse(error.response);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Add Exchange Session
|
||||
</Typography>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
|
||||
<Select
|
||||
labelId="exchange-type-select-label"
|
||||
id="exchange-type-select"
|
||||
value={exchangeType}
|
||||
onChange={handleExchangeTypeChange}
|
||||
>
|
||||
<MenuItem value={'binance'}>Binance</MenuItem>
|
||||
<MenuItem value={'max'}>Max</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="name"
|
||||
name="name"
|
||||
label="Session Name"
|
||||
fullWidth
|
||||
required
|
||||
disabled={!customSessionName}
|
||||
onChange={(event) => {
|
||||
setSessionName(event.target.value);
|
||||
}}
|
||||
value={sessionName}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="secondary"
|
||||
name="custom_session_name"
|
||||
onChange={(event) => {
|
||||
setCustomSessionName(event.target.checked);
|
||||
}}
|
||||
value="1"
|
||||
/>
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
setResponse(error.response)
|
||||
})
|
||||
};
|
||||
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>
|
||||
|
||||
const handleTestConnection = (event) => {
|
||||
const payload = createSessionConfig()
|
||||
setTesting(true)
|
||||
testSessionConnection(payload, (response) => {
|
||||
console.log(response)
|
||||
setTesting(false)
|
||||
setTestResponse(response)
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
setTesting(false)
|
||||
setTestResponse(error.response)
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Add Exchange Session
|
||||
</Typography>
|
||||
<Grid container spacing={3}>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
|
||||
<Select
|
||||
labelId="exchange-type-select-label"
|
||||
id="exchange-type-select"
|
||||
value={exchangeType}
|
||||
onChange={handleExchangeTypeChange}
|
||||
>
|
||||
<MenuItem value={"binance"}>Binance</MenuItem>
|
||||
<MenuItem value={"max"}>Max</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="name"
|
||||
name="name"
|
||||
label="Session Name"
|
||||
fullWidth
|
||||
required
|
||||
disabled={!customSessionName}
|
||||
onChange={(event) => {
|
||||
setSessionName(event.target.value)
|
||||
}}
|
||||
value={sessionName}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="secondary" name="custom_session_name"
|
||||
onChange={(event) => {
|
||||
setCustomSessionName(event.target.checked);
|
||||
}} 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}>
|
||||
<FormControl fullWidth variant="filled">
|
||||
<InputLabel htmlFor="apiKey">API Key</InputLabel>
|
||||
<FilledInput
|
||||
id="apiKey"
|
||||
type={showApiKey ? 'text' : 'password'}
|
||||
value={apiKey}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle key visibility"
|
||||
onClick={() => { setShowApiKey(!showApiKey) }}
|
||||
onMouseDown={(event) => { event.preventDefault() }}
|
||||
edge="end"
|
||||
>
|
||||
{showApiKey ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
onChange={(event) => {
|
||||
setApiKey(event.target.value)
|
||||
resetTestResponse()
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
|
||||
<FormControl fullWidth variant="filled">
|
||||
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
|
||||
<FilledInput
|
||||
id="apiSecret"
|
||||
type={showApiSecret ? 'text' : 'password'}
|
||||
value={apiSecret}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle key visibility"
|
||||
onClick={() => { setShowApiSecret(!showApiSecret) }}
|
||||
onMouseDown={(event) => { event.preventDefault() }}
|
||||
edge="end"
|
||||
>
|
||||
{showApiSecret ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
onChange={(event) => {
|
||||
setApiSecret(event.target.value)
|
||||
resetTestResponse()
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{exchangeType === "binance" ? (
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="secondary" name="isMargin" onChange={(event) => {
|
||||
setIsMargin(event.target.checked);
|
||||
resetTestResponse();
|
||||
}} value="1"/>}
|
||||
label="Use margin trading."
|
||||
/>
|
||||
<FormHelperText id="isMargin-helper-text">This is only available for Binance. Please use the
|
||||
leverage at your own risk.</FormHelperText>
|
||||
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="secondary" name="isIsolatedMargin"
|
||||
onChange={(event) => {
|
||||
setIsIsolatedMargin(event.target.checked);
|
||||
resetTestResponse()
|
||||
}} value="1"/>}
|
||||
label="Use isolated margin trading."
|
||||
/>
|
||||
<FormHelperText id="isIsolatedMargin-helper-text">This is only available for Binance. If this is
|
||||
set, you can only trade one symbol with one session.</FormHelperText>
|
||||
|
||||
{isIsolatedMargin ?
|
||||
<TextField
|
||||
id="isolatedMarginSymbol"
|
||||
name="isolatedMarginSymbol"
|
||||
label="Isolated Margin Symbol"
|
||||
onChange={(event) => {
|
||||
setIsolatedMarginSymbol(event.target.value);
|
||||
resetTestResponse()
|
||||
}}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
: null}
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
<Grid item xs={12}>
|
||||
<FormControl fullWidth variant="filled">
|
||||
<InputLabel htmlFor="apiKey">API Key</InputLabel>
|
||||
<FilledInput
|
||||
id="apiKey"
|
||||
type={showApiKey ? 'text' : 'password'}
|
||||
value={apiKey}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle key visibility"
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}>
|
||||
Back
|
||||
</Button>
|
||||
setShowApiKey(!showApiKey);
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
edge="end"
|
||||
>
|
||||
{showApiKey ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
onChange={(event) => {
|
||||
setApiKey(event.target.value);
|
||||
resetTestResponse();
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleTestConnection}
|
||||
disabled={testing}>
|
||||
{testing ? "Testing" : "Test Connection"}
|
||||
</Button>
|
||||
<Grid item xs={12}>
|
||||
<FormControl fullWidth variant="filled">
|
||||
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
|
||||
<FilledInput
|
||||
id="apiSecret"
|
||||
type={showApiSecret ? 'text' : 'password'}
|
||||
value={apiSecret}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle key visibility"
|
||||
onClick={() => {
|
||||
setShowApiSecret(!showApiSecret);
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
edge="end"
|
||||
>
|
||||
{showApiSecret ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
onChange={(event) => {
|
||||
setApiSecret(event.target.value);
|
||||
resetTestResponse();
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
{exchangeType === 'binance' ? (
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="secondary"
|
||||
name="isMargin"
|
||||
onChange={(event) => {
|
||||
setIsMargin(event.target.checked);
|
||||
resetTestResponse();
|
||||
}}
|
||||
value="1"
|
||||
/>
|
||||
}
|
||||
label="Use margin trading."
|
||||
/>
|
||||
<FormHelperText id="isMargin-helper-text">
|
||||
This is only available for Binance. Please use the leverage at
|
||||
your own risk.
|
||||
</FormHelperText>
|
||||
|
||||
{
|
||||
testResponse ? testResponse.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{testResponse.error}</Alert>
|
||||
</Box>
|
||||
) : testResponse.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||
</Box>
|
||||
) : null : null
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="secondary"
|
||||
name="isIsolatedMargin"
|
||||
onChange={(event) => {
|
||||
setIsIsolatedMargin(event.target.checked);
|
||||
resetTestResponse();
|
||||
}}
|
||||
value="1"
|
||||
/>
|
||||
}
|
||||
label="Use isolated margin trading."
|
||||
/>
|
||||
<FormHelperText id="isIsolatedMargin-helper-text">
|
||||
This is only available for Binance. If this is set, you can only
|
||||
trade one symbol with one session.
|
||||
</FormHelperText>
|
||||
|
||||
{isIsolatedMargin ? (
|
||||
<TextField
|
||||
id="isolatedMarginSymbol"
|
||||
name="isolatedMarginSymbol"
|
||||
label="Isolated Margin Symbol"
|
||||
onChange={(event) => {
|
||||
setIsolatedMarginSymbol(event.target.value);
|
||||
resetTestResponse();
|
||||
}}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
) : null}
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
{
|
||||
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
|
||||
}
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleTestConnection}
|
||||
disabled={testing}
|
||||
>
|
||||
{testing ? 'Testing' : 'Test Connection'}
|
||||
</Button>
|
||||
|
||||
<Button variant="contained" color="primary" onClick={handleAdd}>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
{testResponse ? (
|
||||
testResponse.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{testResponse.error}</Alert>
|
||||
</Box>
|
||||
) : testResponse.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||
</Box>
|
||||
) : null
|
||||
) : null}
|
||||
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,184 +13,197 @@ import FormLabel from '@material-ui/core/FormLabel';
|
|||
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
import {configureDatabase, testDatabaseConnection} from '../api/bbgo';
|
||||
import { configureDatabase, testDatabaseConnection } from '../api/bbgo';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
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),
|
||||
}
|
||||
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 ConfigureDatabaseForm({onConfigured}) {
|
||||
const classes = useStyles();
|
||||
export default function ConfigureDatabaseForm({ onConfigured }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [mysqlURL, setMysqlURL] = React.useState("root@tcp(127.0.0.1:3306)/bbgo")
|
||||
const [mysqlURL, setMysqlURL] = React.useState(
|
||||
'root@tcp(127.0.0.1:3306)/bbgo'
|
||||
);
|
||||
|
||||
const [driver, setDriver] = React.useState("sqlite3");
|
||||
const [testing, setTesting] = React.useState(false);
|
||||
const [testResponse, setTestResponse] = React.useState(null);
|
||||
const [configured, setConfigured] = React.useState(false);
|
||||
const [driver, setDriver] = React.useState('sqlite3');
|
||||
const [testing, setTesting] = React.useState(false);
|
||||
const [testResponse, setTestResponse] = React.useState(null);
|
||||
const [configured, setConfigured] = React.useState(false);
|
||||
|
||||
const getDSN = () => driver === "sqlite3" ? "file:bbgo.sqlite3" : mysqlURL
|
||||
const getDSN = () => (driver === 'sqlite3' ? 'file:bbgo.sqlite3' : mysqlURL);
|
||||
|
||||
const resetTestResponse = () => {
|
||||
setTestResponse(null)
|
||||
}
|
||||
const resetTestResponse = () => {
|
||||
setTestResponse(null);
|
||||
};
|
||||
|
||||
const handleConfigureDatabase = (event) => {
|
||||
const dsn = getDSN()
|
||||
const handleConfigureDatabase = (event) => {
|
||||
const dsn = getDSN();
|
||||
|
||||
configureDatabase({driver, dsn}, (response) => {
|
||||
console.log(response);
|
||||
setTesting(false);
|
||||
setTestResponse(response);
|
||||
if (onConfigured) {
|
||||
setConfigured(true);
|
||||
setTimeout(onConfigured, 3000);
|
||||
}
|
||||
configureDatabase({ driver, dsn }, (response) => {
|
||||
console.log(response);
|
||||
setTesting(false);
|
||||
setTestResponse(response);
|
||||
if (onConfigured) {
|
||||
setConfigured(true);
|
||||
setTimeout(onConfigured, 3000);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setTesting(false);
|
||||
setTestResponse(err.response.data);
|
||||
});
|
||||
};
|
||||
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setTesting(false);
|
||||
setTestResponse(err.response.data);
|
||||
})
|
||||
}
|
||||
const handleTestConnection = (event) => {
|
||||
const dsn = getDSN();
|
||||
|
||||
const handleTestConnection = (event) => {
|
||||
const dsn = getDSN()
|
||||
setTesting(true);
|
||||
testDatabaseConnection({ driver, dsn }, (response) => {
|
||||
console.log(response);
|
||||
setTesting(false);
|
||||
setTestResponse(response);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setTesting(false);
|
||||
setTestResponse(err.response.data);
|
||||
});
|
||||
};
|
||||
|
||||
setTesting(true);
|
||||
testDatabaseConnection({driver, dsn}, (response) => {
|
||||
console.log(response)
|
||||
setTesting(false)
|
||||
setTestResponse(response)
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
setTesting(false)
|
||||
setTestResponse(err.response.data)
|
||||
})
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Configure Database
|
||||
</Typography>
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Configure Database
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
If you have database installed on your machine, you can enter the DSN
|
||||
string in the following field. Please note this is optional, you CAN
|
||||
SKIP this step.
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Box m={6}>
|
||||
<FormControl component="fieldset" required={true}>
|
||||
<FormLabel component="legend">Database Driver</FormLabel>
|
||||
<RadioGroup
|
||||
aria-label="driver"
|
||||
name="driver"
|
||||
value={driver}
|
||||
onChange={(event) => {
|
||||
setDriver(event.target.value);
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="sqlite3"
|
||||
control={<Radio />}
|
||||
label="Standard (Default)"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="mysql"
|
||||
control={<Radio />}
|
||||
label="MySQL"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormHelperText></FormHelperText>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{driver === 'mysql' ? (
|
||||
<Grid item xs={12} sm={8}>
|
||||
<TextField
|
||||
id="mysql_url"
|
||||
name="mysql_url"
|
||||
label="MySQL Data Source Name"
|
||||
fullWidth
|
||||
required
|
||||
defaultValue={mysqlURL}
|
||||
onChange={(event) => {
|
||||
setMysqlURL(event.target.value);
|
||||
resetTestResponse();
|
||||
}}
|
||||
/>
|
||||
<FormHelperText>MySQL DSN</FormHelperText>
|
||||
|
||||
<Typography variant="body1" gutterBottom>
|
||||
If you have database installed on your machine, you can enter the DSN string in the following field.
|
||||
Please note this is optional, you CAN SKIP this step.
|
||||
If you have database installed on your machine, you can enter the
|
||||
DSN string like the following format:
|
||||
<br />
|
||||
<pre>
|
||||
<code>root:password@tcp(127.0.0.1:3306)/bbgo</code>
|
||||
</pre>
|
||||
<br />
|
||||
Be sure to create your database before using it. You need to
|
||||
execute the following statement to create a database:
|
||||
<br />
|
||||
<pre>
|
||||
<code>CREATE DATABASE bbgo CHARSET utf8;</code>
|
||||
</pre>
|
||||
</Typography>
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Box m={6}>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
If you don't know what to choose, just pick the standard driver
|
||||
(sqlite3).
|
||||
<br />
|
||||
For professionals, you can pick MySQL driver, BBGO works best
|
||||
with MySQL, especially for larger data scale.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleTestConnection}
|
||||
disabled={testing || configured}
|
||||
>
|
||||
{testing ? 'Testing' : 'Test Connection'}
|
||||
</Button>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Box m={6}>
|
||||
<FormControl component="fieldset" required={true}>
|
||||
<FormLabel component="legend">Database Driver</FormLabel>
|
||||
<RadioGroup aria-label="driver" name="driver" value={driver} onChange={(event) => {
|
||||
setDriver(event.target.value);
|
||||
}}>
|
||||
<FormControlLabel value="sqlite3" control={<Radio/>} label="Standard (Default)"/>
|
||||
<FormControlLabel value="mysql" control={<Radio/>} label="MySQL"/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormHelperText>
|
||||
</FormHelperText>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{driver === "mysql" ? (
|
||||
<Grid item xs={12} sm={8}>
|
||||
<TextField id="mysql_url" name="mysql_url" label="MySQL Data Source Name"
|
||||
fullWidth
|
||||
required
|
||||
defaultValue={mysqlURL}
|
||||
onChange={(event) => {
|
||||
setMysqlURL(event.target.value)
|
||||
resetTestResponse()
|
||||
}}
|
||||
/>
|
||||
<FormHelperText>MySQL DSN</FormHelperText>
|
||||
|
||||
<Typography variant="body1" gutterBottom>
|
||||
If you have database installed on your machine, you can enter the DSN string like the
|
||||
following
|
||||
format:
|
||||
<br/>
|
||||
<pre><code>root:password@tcp(127.0.0.1:3306)/bbgo</code></pre>
|
||||
|
||||
<br/>
|
||||
Be sure to create your database before using it. You need to execute the following statement
|
||||
to
|
||||
create a database:
|
||||
<br/>
|
||||
<pre><code>CREATE DATABASE bbgo CHARSET utf8;</code></pre>
|
||||
</Typography>
|
||||
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Box m={6}>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
If you don't know what to choose, just pick the standard driver (sqlite3).
|
||||
<br/>
|
||||
For professionals, you can pick MySQL driver, BBGO works best with MySQL, especially for
|
||||
larger data scale.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleTestConnection}
|
||||
disabled={testing || configured}
|
||||
>
|
||||
{testing ? "Testing" : "Test Connection"}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={testing || configured}
|
||||
onClick={handleConfigureDatabase}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{
|
||||
testResponse ? testResponse.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{testResponse.error}</Alert>
|
||||
</Box>
|
||||
) : testResponse.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||
</Box>
|
||||
) : null : null
|
||||
}
|
||||
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={testing || configured}
|
||||
onClick={handleConfigureDatabase}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{testResponse ? (
|
||||
testResponse.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{testResponse.error}</Alert>
|
||||
</Box>
|
||||
) : testResponse.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||
</Box>
|
||||
) : null
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,12 @@ 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 {attachStrategyOn, querySessions, querySessionSymbols} from "../api/bbgo";
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import {
|
||||
attachStrategyOn,
|
||||
querySessions,
|
||||
querySessionSymbols,
|
||||
} from '../api/bbgo';
|
||||
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
|
@ -20,409 +24,423 @@ 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 Box from '@material-ui/core/Box';
|
||||
|
||||
import NumberFormat from 'react-number-format';
|
||||
|
||||
function parseFloatValid(s) {
|
||||
if (s) {
|
||||
const f = parseFloat(s)
|
||||
if (!isNaN(f)) {
|
||||
return f
|
||||
}
|
||||
if (s) {
|
||||
const f = parseFloat(s);
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseFloatCall(s, cb) {
|
||||
if (s) {
|
||||
const f = parseFloat(s)
|
||||
if (!isNaN(f)) {
|
||||
cb(f)
|
||||
}
|
||||
if (s) {
|
||||
const f = parseFloat(s);
|
||||
if (!isNaN(f)) {
|
||||
cb(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function StandardNumberFormat(props) {
|
||||
const {inputRef, onChange, ...other} = props;
|
||||
return (
|
||||
<NumberFormat
|
||||
{...other}
|
||||
getInputRef={inputRef}
|
||||
onValueChange={(values) => {
|
||||
onChange({
|
||||
target: {
|
||||
name: props.name,
|
||||
value: values.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
thousandSeparator
|
||||
isNumericString
|
||||
/>
|
||||
);
|
||||
const { inputRef, onChange, ...other } = props;
|
||||
return (
|
||||
<NumberFormat
|
||||
{...other}
|
||||
getInputRef={inputRef}
|
||||
onValueChange={(values) => {
|
||||
onChange({
|
||||
target: {
|
||||
name: props.name,
|
||||
value: values.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
thousandSeparator
|
||||
isNumericString
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
StandardNumberFormat.propTypes = {
|
||||
inputRef: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
inputRef: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
function PriceNumberFormat(props) {
|
||||
const {inputRef, onChange, ...other} = props;
|
||||
const { inputRef, onChange, ...other } = props;
|
||||
|
||||
return (
|
||||
<NumberFormat
|
||||
{...other}
|
||||
getInputRef={inputRef}
|
||||
onValueChange={(values) => {
|
||||
onChange({
|
||||
target: {
|
||||
name: props.name,
|
||||
value: values.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
thousandSeparator
|
||||
isNumericString
|
||||
prefix="$"
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<NumberFormat
|
||||
{...other}
|
||||
getInputRef={inputRef}
|
||||
onValueChange={(values) => {
|
||||
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,
|
||||
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),
|
||||
}
|
||||
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();
|
||||
|
||||
export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||
const classes = useStyles();
|
||||
const [errors, setErrors] = React.useState({});
|
||||
|
||||
const [errors, setErrors] = React.useState({})
|
||||
const [sessions, setSessions] = React.useState([]);
|
||||
|
||||
const [sessions, setSessions] = React.useState([]);
|
||||
const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]);
|
||||
|
||||
const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]);
|
||||
const [selectedSessionName, setSelectedSessionName] = React.useState(null);
|
||||
|
||||
const [selectedSessionName, setSelectedSessionName] = React.useState(null);
|
||||
const [selectedSymbol, setSelectedSymbol] = React.useState('');
|
||||
|
||||
const [selectedSymbol, setSelectedSymbol] = React.useState('');
|
||||
const [quantityBy, setQuantityBy] = React.useState('fixedAmount');
|
||||
|
||||
const [quantityBy, setQuantityBy] = React.useState('fixedAmount');
|
||||
const [upperPrice, setUpperPrice] = React.useState(30000.0);
|
||||
const [lowerPrice, setLowerPrice] = React.useState(10000.0);
|
||||
|
||||
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 [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({});
|
||||
|
||||
const [response, setResponse] = React.useState({});
|
||||
React.useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
setSessions(sessions);
|
||||
});
|
||||
}, []);
|
||||
|
||||
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 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;
|
||||
|
||||
const handleQuantityBy = (event) => {
|
||||
setQuantityBy(event.target.value);
|
||||
};
|
||||
case 'fixedAmount':
|
||||
payload.amount = parseFloatValid(fixedAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
const handleSessionChange = (event) => {
|
||||
const sessionName = event.target.value;
|
||||
setSelectedSessionName(sessionName)
|
||||
if (!selectedSessionName) {
|
||||
setErrors({ session: true });
|
||||
return;
|
||||
}
|
||||
|
||||
querySessionSymbols(sessionName, (symbols) => {
|
||||
setActiveSessionSymbols(symbols);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setResponse(err.response.data)
|
||||
})
|
||||
};
|
||||
if (!selectedSymbol) {
|
||||
setErrors({ symbol: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionMenuItems = sessions.map((session, index) => {
|
||||
return (
|
||||
<MenuItem key={session.name} value={session.name}>
|
||||
{session.name}
|
||||
</MenuItem>
|
||||
);
|
||||
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 symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
|
||||
return (
|
||||
<MenuItem key={symbol} value={symbol}>
|
||||
{symbol}
|
||||
</MenuItem>
|
||||
);
|
||||
})
|
||||
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 (
|
||||
<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 required className={classes.formControl} error={errors.session}>
|
||||
<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 required className={classes.formControl} error={errors.symbol}>
|
||||
<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) => {
|
||||
parseFloatCall(event.target.value, setUpperPrice)
|
||||
}}
|
||||
value={upperPrice}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="lowerPrice"
|
||||
name="lower_price"
|
||||
label="Lower Price"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setLowerPrice)
|
||||
}}
|
||||
value={lowerPrice}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="profitSpread"
|
||||
name="profit_spread"
|
||||
label="Profit Spread"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setProfitSpread)
|
||||
}}
|
||||
value={profitSpread}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid item xs={12} sm={3}>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">Order Quantity By</FormLabel>
|
||||
<RadioGroup name="quantityBy" value={quantityBy} onChange={handleQuantityBy}>
|
||||
<FormControlLabel value="fixedAmount" control={<Radio/>} label="Fixed Amount"/>
|
||||
<FormControlLabel value="fixedQuantity" control={<Radio/>} label="Fixed Quantity"/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={9}>
|
||||
{quantityBy === "fixedQuantity" ? (
|
||||
<TextField
|
||||
id="fixedQuantity"
|
||||
name="order_quantity"
|
||||
label="Fixed Quantity"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setFixedQuantity)
|
||||
}}
|
||||
value={fixedQuantity}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{quantityBy === "fixedAmount" ? (
|
||||
<TextField
|
||||
id="orderAmount"
|
||||
name="order_amount"
|
||||
label="Fixed Amount"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setFixedAmount)
|
||||
}}
|
||||
value={fixedAmount}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="gridNumber"
|
||||
name="grid_number"
|
||||
label="Number of Grid"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setGridNumber)
|
||||
}}
|
||||
value={gridNumber}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add Strategy
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{
|
||||
response ? response.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{response.error}</Alert>
|
||||
</Box>
|
||||
) : response.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Strategy Added</Alert>
|
||||
</Box>
|
||||
) : null : null
|
||||
}
|
||||
|
||||
|
||||
</React.Fragment>
|
||||
<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
|
||||
required
|
||||
className={classes.formControl}
|
||||
error={errors.session}
|
||||
>
|
||||
<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
|
||||
required
|
||||
className={classes.formControl}
|
||||
error={errors.symbol}
|
||||
>
|
||||
<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) => {
|
||||
parseFloatCall(event.target.value, setUpperPrice);
|
||||
}}
|
||||
value={upperPrice}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="lowerPrice"
|
||||
name="lower_price"
|
||||
label="Lower Price"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setLowerPrice);
|
||||
}}
|
||||
value={lowerPrice}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="profitSpread"
|
||||
name="profit_spread"
|
||||
label="Profit Spread"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setProfitSpread);
|
||||
}}
|
||||
value={profitSpread}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={3}>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">Order Quantity By</FormLabel>
|
||||
<RadioGroup
|
||||
name="quantityBy"
|
||||
value={quantityBy}
|
||||
onChange={handleQuantityBy}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="fixedAmount"
|
||||
control={<Radio />}
|
||||
label="Fixed Amount"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="fixedQuantity"
|
||||
control={<Radio />}
|
||||
label="Fixed Quantity"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={9}>
|
||||
{quantityBy === 'fixedQuantity' ? (
|
||||
<TextField
|
||||
id="fixedQuantity"
|
||||
name="order_quantity"
|
||||
label="Fixed Quantity"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setFixedQuantity);
|
||||
}}
|
||||
value={fixedQuantity}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{quantityBy === 'fixedAmount' ? (
|
||||
<TextField
|
||||
id="orderAmount"
|
||||
name="order_amount"
|
||||
label="Fixed Amount"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setFixedAmount);
|
||||
}}
|
||||
value={fixedAmount}
|
||||
InputProps={{
|
||||
inputComponent: PriceNumberFormat,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="gridNumber"
|
||||
name="grid_number"
|
||||
label="Number of Grid"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
parseFloatCall(event.target.value, setGridNumber);
|
||||
}}
|
||||
value={gridNumber}
|
||||
InputProps={{
|
||||
inputComponent: StandardNumberFormat,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button variant="contained" color="primary" onClick={handleAdd}>
|
||||
Add Strategy
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{response ? (
|
||||
response.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{response.error}</Alert>
|
||||
</Box>
|
||||
) : response.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Strategy Added</Alert>
|
||||
</Box>
|
||||
) : null
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
|
||||
|
@ -9,123 +9,135 @@ import Paper from '@material-ui/core/Paper';
|
|||
import Popper from '@material-ui/core/Popper';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import MenuList from '@material-ui/core/MenuList';
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import PersonIcon from "@material-ui/icons/Person";
|
||||
|
||||
import { useEtherBalance, useTokenBalance, useEthers } from '@usedapp/core'
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import PersonIcon from '@material-ui/icons/Person';
|
||||
|
||||
import { useEtherBalance, useTokenBalance, useEthers } from '@usedapp/core';
|
||||
import { formatEther } from '@ethersproject/units';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
buttons: {
|
||||
margin: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
profile: {
|
||||
margin: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
}
|
||||
buttons: {
|
||||
margin: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
profile: {
|
||||
margin: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
const BBG = '0x3Afe98235d680e8d7A52e1458a59D60f45F935C0'
|
||||
const BBG = '0x3Afe98235d680e8d7A52e1458a59D60f45F935C0';
|
||||
|
||||
export default function ConnectWallet() {
|
||||
const classes = useStyles();
|
||||
|
||||
const { activateBrowserWallet, account } = useEthers();
|
||||
const etherBalance = useEtherBalance(account);
|
||||
const tokenBalance = useTokenBalance(BBG, account);
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef(null);
|
||||
|
||||
const classes = useStyles();
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const { activateBrowserWallet, account } = useEthers()
|
||||
const etherBalance = useEtherBalance(account)
|
||||
const tokenBalance = useTokenBalance(BBG, account)
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
function handleListKeyDown(event) {
|
||||
if (event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
} else if (event.key === 'Escape') {
|
||||
setOpen(false);
|
||||
}
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// return focus to the button when we transitioned from !open -> open
|
||||
const prevOpen = React.useRef(open);
|
||||
React.useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current.focus();
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
function handleListKeyDown(event) {
|
||||
if (event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
} else if (event.key === 'Escape') {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{account?
|
||||
(<>
|
||||
<Button
|
||||
ref={anchorRef}
|
||||
id="composition-button"
|
||||
aria-controls={open ? 'composition-menu' : undefined}
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<PersonIcon/>
|
||||
<ListItemText primary="Profile"/>
|
||||
</Button>
|
||||
<Popper
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
placement="bottom-start"
|
||||
transition
|
||||
disablePortal
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin:
|
||||
placement === 'bottom-start' ? 'left top' : 'left bottom',
|
||||
}}
|
||||
>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList
|
||||
autoFocusItem={open}
|
||||
id="composition-menu"
|
||||
aria-labelledby="composition-button"
|
||||
onKeyDown={handleListKeyDown}
|
||||
>
|
||||
<MenuItem onClick={handleClose}>{account && <p>Account: {account}</p>}</MenuItem>
|
||||
<MenuItem onClick={handleClose}>{etherBalance && <a>ETH Balance: {formatEther(etherBalance)}</a>}</MenuItem>
|
||||
<MenuItem onClick={handleClose}>{tokenBalance && <a>BBG Balance: {formatEther(tokenBalance)}</a>}</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</>):(<div>
|
||||
<button onClick={() => activateBrowserWallet()} className={classes.buttons}>Connect Wallet</button>
|
||||
</div>)}
|
||||
</>
|
||||
)
|
||||
// return focus to the button when we transitioned from !open -> open
|
||||
const prevOpen = React.useRef(open);
|
||||
React.useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current.focus();
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{account ? (
|
||||
<>
|
||||
<Button
|
||||
ref={anchorRef}
|
||||
id="composition-button"
|
||||
aria-controls={open ? 'composition-menu' : undefined}
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<PersonIcon />
|
||||
<ListItemText primary="Profile" />
|
||||
</Button>
|
||||
<Popper
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
placement="bottom-start"
|
||||
transition
|
||||
disablePortal
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin:
|
||||
placement === 'bottom-start' ? 'left top' : 'left bottom',
|
||||
}}
|
||||
>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList
|
||||
autoFocusItem={open}
|
||||
id="composition-menu"
|
||||
aria-labelledby="composition-button"
|
||||
onKeyDown={handleListKeyDown}
|
||||
>
|
||||
<MenuItem onClick={handleClose}>
|
||||
{account && <p>Account: {account}</p>}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleClose}>
|
||||
{etherBalance && (
|
||||
<a>ETH Balance: {formatEther(etherBalance)}</a>
|
||||
)}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleClose}>
|
||||
{tokenBalance && (
|
||||
<a>BBG Balance: {formatEther(tokenBalance)}</a>
|
||||
)}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => activateBrowserWallet()}
|
||||
className={classes.buttons}
|
||||
>
|
||||
Connect Wallet
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
import Paper from "@material-ui/core/Paper";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {querySessions} from '../api/bbgo'
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Tabs from '@material-ui/core/Tabs';
|
||||
import Tab from '@material-ui/core/Tab';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { querySessions } from '../api/bbgo';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
}
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ExchangeSessionTabPanel() {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const [tabIndex, setTabIndex] = React.useState(0);
|
||||
const handleTabClick = (event, newValue) => {
|
||||
setTabIndex(newValue);
|
||||
};
|
||||
const [tabIndex, setTabIndex] = React.useState(0);
|
||||
const handleTabClick = (event, newValue) => {
|
||||
setTabIndex(newValue);
|
||||
};
|
||||
|
||||
const [sessions, setSessions] = useState([])
|
||||
const [sessions, setSessions] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
setSessions(sessions)
|
||||
})
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
setSessions(sessions);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return <Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Sessions
|
||||
</Typography>
|
||||
<Tabs
|
||||
value={tabIndex}
|
||||
onChange={handleTabClick}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
{
|
||||
sessions.map((session) => {
|
||||
return <Tab key={session.name} label={session.name}/>
|
||||
})
|
||||
}
|
||||
</Tabs>
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Sessions
|
||||
</Typography>
|
||||
<Tabs
|
||||
value={tabIndex}
|
||||
onChange={handleTabClick}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
{sessions.map((session) => {
|
||||
return <Tab key={session.name} label={session.name} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,81 +8,81 @@ import ListItemText from '@material-ui/core/ListItemText';
|
|||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import PowerIcon from '@material-ui/icons/Power';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import {querySessions} from "../api/bbgo";
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { querySessions } from '../api/bbgo';
|
||||
|
||||
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),
|
||||
}
|
||||
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 ReviewSessions({onBack, onNext}) {
|
||||
const classes = useStyles();
|
||||
export default function ReviewSessions({ onBack, onNext }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [sessions, setSessions] = React.useState([]);
|
||||
const [sessions, setSessions] = React.useState([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
setSessions(sessions)
|
||||
});
|
||||
}, [])
|
||||
|
||||
const items = sessions.map((session, i) => {
|
||||
console.log(session)
|
||||
return (
|
||||
<ListItem key={session.name}>
|
||||
<ListItemIcon>
|
||||
<PowerIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={session.name} secondary={session.exchange}/>
|
||||
</ListItem>
|
||||
);
|
||||
})
|
||||
React.useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
setSessions(sessions);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const items = sessions.map((session, i) => {
|
||||
console.log(session);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Review Sessions
|
||||
</Typography>
|
||||
|
||||
<List component="nav">
|
||||
{items}
|
||||
</List>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (onNext) {
|
||||
onNext();
|
||||
}
|
||||
}}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
<ListItem key={session.name}>
|
||||
<ListItemIcon>
|
||||
<PowerIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={session.name} secondary={session.exchange} />
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Review Sessions
|
||||
</Typography>
|
||||
|
||||
<List component="nav">{items}</List>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (onNext) {
|
||||
onNext();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,144 +15,143 @@ import TableContainer from '@material-ui/core/TableContainer';
|
|||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import {queryStrategies} from "../api/bbgo";
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { queryStrategies } from '../api/bbgo';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
strategyCard: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
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),
|
||||
}
|
||||
strategyCard: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
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),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function configToTable(config) {
|
||||
const rows = Object.getOwnPropertyNames(config).map((k) => {
|
||||
return {
|
||||
key: k,
|
||||
val: config[k],
|
||||
}
|
||||
})
|
||||
const rows = Object.getOwnPropertyNames(config).map((k) => {
|
||||
return {
|
||||
key: k,
|
||||
val: config[k],
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<TableContainer>
|
||||
<Table aria-label="strategy attributes">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Field</TableCell>
|
||||
<TableCell align="right">Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.key}>
|
||||
<TableCell component="th" scope="row">
|
||||
{row.key}
|
||||
</TableCell>
|
||||
<TableCell align="right">{row.val}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
return (
|
||||
<TableContainer>
|
||||
<Table aria-label="strategy attributes">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Field</TableCell>
|
||||
<TableCell align="right">Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.key}>
|
||||
<TableCell component="th" scope="row">
|
||||
{row.key}
|
||||
</TableCell>
|
||||
<TableCell align="right">{row.val}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ReviewStrategies({onBack, onNext}) {
|
||||
const classes = useStyles();
|
||||
export default function ReviewStrategies({ onBack, onNext }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [strategies, setStrategies] = React.useState([]);
|
||||
const [strategies, setStrategies] = React.useState([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
queryStrategies((strategies) => {
|
||||
setStrategies(strategies || [])
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, [])
|
||||
React.useEffect(() => {
|
||||
queryStrategies((strategies) => {
|
||||
setStrategies(strategies || []);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const items = strategies.map((o, i) => {
|
||||
const mounts = o.on || [];
|
||||
delete o.on
|
||||
const items = strategies.map((o, i) => {
|
||||
const mounts = o.on || [];
|
||||
delete o.on;
|
||||
|
||||
const config = o[o.strategy]
|
||||
const config = o[o.strategy];
|
||||
|
||||
const titleComps = [o.strategy.toUpperCase()]
|
||||
if (config.symbol) {
|
||||
titleComps.push(config.symbol)
|
||||
}
|
||||
const titleComps = [o.strategy.toUpperCase()];
|
||||
if (config.symbol) {
|
||||
titleComps.push(config.symbol);
|
||||
}
|
||||
|
||||
const title = titleComps.join(" ")
|
||||
|
||||
return (
|
||||
<Card key={i} className={classes.strategyCard}>
|
||||
<CardHeader
|
||||
avatar={
|
||||
<Avatar aria-label="strategy">G</Avatar>
|
||||
}
|
||||
action={
|
||||
<IconButton aria-label="settings">
|
||||
<MoreVertIcon/>
|
||||
</IconButton>
|
||||
}
|
||||
title={title}
|
||||
subheader={`Exchange ${mounts.map((m) => m.toUpperCase())}`}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography variant="body2" color="textSecondary" component="p">
|
||||
Strategy will be executed on session {mounts.join(',')} with the following configuration:
|
||||
</Typography>
|
||||
|
||||
{configToTable(config)}
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})
|
||||
const title = titleComps.join(' ');
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Review Strategies
|
||||
</Typography>
|
||||
<Card key={i} className={classes.strategyCard}>
|
||||
<CardHeader
|
||||
avatar={<Avatar aria-label="strategy">G</Avatar>}
|
||||
action={
|
||||
<IconButton aria-label="settings">
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
}
|
||||
title={title}
|
||||
subheader={`Exchange ${mounts.map((m) => m.toUpperCase())}`}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography variant="body2" color="textSecondary" component="p">
|
||||
Strategy will be executed on session {mounts.join(',')} with the
|
||||
following configuration:
|
||||
</Typography>
|
||||
|
||||
<List component="nav">
|
||||
{items}
|
||||
</List>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button onClick={() => {
|
||||
if (onBack) {
|
||||
onBack()
|
||||
}
|
||||
}}>
|
||||
Add New Strategy
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (onNext) {
|
||||
onNext();
|
||||
}
|
||||
}}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
{configToTable(config)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Review Strategies
|
||||
</Typography>
|
||||
|
||||
<List component="nav">{items}</List>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Add New Strategy
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (onNext) {
|
||||
onNext();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,107 +1,105 @@
|
|||
import React from 'react';
|
||||
import {useRouter} from 'next/router';
|
||||
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
import {ping, saveConfig, setupRestart} from "../api/bbgo";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import { ping, saveConfig, setupRestart } from '../api/bbgo';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
strategyCard: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
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),
|
||||
}
|
||||
strategyCard: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
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 SaveConfigAndRestart({onBack, onRestarted}) {
|
||||
const classes = useStyles();
|
||||
export default function SaveConfigAndRestart({ onBack, onRestarted }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const {push} = useRouter();
|
||||
const [response, setResponse] = React.useState({});
|
||||
const { push } = useRouter();
|
||||
const [response, setResponse] = React.useState({});
|
||||
|
||||
const handleRestart = () => {
|
||||
saveConfig((resp) => {
|
||||
setResponse(resp);
|
||||
const handleRestart = () => {
|
||||
saveConfig((resp) => {
|
||||
setResponse(resp);
|
||||
|
||||
setupRestart((resp) => {
|
||||
let t
|
||||
t = setInterval(() => {
|
||||
ping(() => {
|
||||
clearInterval(t)
|
||||
push("/");
|
||||
})
|
||||
}, 1000);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setResponse(err.response.data);
|
||||
})
|
||||
setupRestart((resp) => {
|
||||
let t;
|
||||
t = setInterval(() => {
|
||||
ping(() => {
|
||||
clearInterval(t);
|
||||
push('/');
|
||||
});
|
||||
}, 1000);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setResponse(err.response.data);
|
||||
});
|
||||
|
||||
// call restart here
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setResponse(err.response.data);
|
||||
});
|
||||
};
|
||||
// call restart here
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setResponse(err.response.data);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Save Config and Restart
|
||||
</Typography>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Save Config and Restart
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" gutterBottom>
|
||||
Click "Save and Restart" to save the configurations to the config file <code>bbgo.yaml</code>,
|
||||
and save the exchange session credentials to the dotenv file <code>.env.local</code>.
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
Click "Save and Restart" to save the configurations to the config file{' '}
|
||||
<code>bbgo.yaml</code>, and save the exchange session credentials to the
|
||||
dotenv file <code>.env.local</code>.
|
||||
</Typography>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button onClick={() => {
|
||||
if (onBack) {
|
||||
onBack()
|
||||
}
|
||||
}}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleRestart}>
|
||||
Save and Restart
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{
|
||||
response ? response.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{response.error}</Alert>
|
||||
</Box>
|
||||
) : response.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Config Saved</Alert>
|
||||
</Box>
|
||||
) : null : null
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
<Button variant="contained" color="primary" onClick={handleRestart}>
|
||||
Save and Restart
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{response ? (
|
||||
response.error ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="error">{response.error}</Alert>
|
||||
</Box>
|
||||
) : response.success ? (
|
||||
<Box m={2}>
|
||||
<Alert severity="success">Config Saved</Alert>
|
||||
</Box>
|
||||
) : null
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,102 +1,101 @@
|
|||
import Drawer from "@material-ui/core/Drawer";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import List from "@material-ui/core/List";
|
||||
import Link from "next/link";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import DashboardIcon from "@material-ui/icons/Dashboard";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import ListIcon from "@material-ui/icons/List";
|
||||
import TrendingUpIcon from "@material-ui/icons/TrendingUp";
|
||||
import React from "react";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import Drawer from '@material-ui/core/Drawer';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import List from '@material-ui/core/List';
|
||||
import Link from 'next/link';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import DashboardIcon from '@material-ui/icons/Dashboard';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import ListIcon from '@material-ui/icons/List';
|
||||
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
toolbar: {
|
||||
paddingRight: 24, // keep right padding when drawer closed
|
||||
},
|
||||
toolbarIcon: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0 8px',
|
||||
...theme.mixins.toolbar,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
drawerPaper: {
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
},
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
toolbar: {
|
||||
paddingRight: 24, // keep right padding when drawer closed
|
||||
},
|
||||
toolbarIcon: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0 8px',
|
||||
...theme.mixins.toolbar,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
drawerPaper: {
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
},
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
export default function SideBar() {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
return <Drawer
|
||||
variant="permanent"
|
||||
className={classes.drawer}
|
||||
PaperProps={{
|
||||
className: classes.drawerPaper,
|
||||
}}
|
||||
anchor={"left"}
|
||||
open={true}>
|
||||
return (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
className={classes.drawer}
|
||||
PaperProps={{
|
||||
className: classes.drawerPaper,
|
||||
}}
|
||||
anchor={'left'}
|
||||
open={true}
|
||||
>
|
||||
<div className={classes.appBarSpacer} />
|
||||
|
||||
<div className={classes.appBarSpacer}/>
|
||||
|
||||
<List>
|
||||
<Link href={"/"}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<DashboardIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Dashboard"/>
|
||||
</ListItem>
|
||||
</Link>
|
||||
</List>
|
||||
<Divider/>
|
||||
<List>
|
||||
<Link href={"/orders"}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<ListIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Orders"/>
|
||||
</ListItem>
|
||||
</Link>
|
||||
<Link href={"/trades"}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<ListIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Trades"/>
|
||||
</ListItem>
|
||||
</Link>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<TrendingUpIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Strategies"/>
|
||||
</ListItem>
|
||||
</List>
|
||||
<List>
|
||||
<Link href={'/'}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<DashboardIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Dashboard" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
</List>
|
||||
<Divider />
|
||||
<List>
|
||||
<Link href={'/orders'}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<ListIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Orders" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
<Link href={'/trades'}>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<ListIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Trades" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
<ListItem button>
|
||||
<ListItemIcon>
|
||||
<TrendingUpIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Strategies" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Drawer>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
@ -9,78 +9,79 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
|||
import Avatar from '@material-ui/core/Avatar';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
cardContent: {}
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
cardContent: {},
|
||||
}));
|
||||
|
||||
const logoCurrencies = {
|
||||
"BTC": true,
|
||||
"ETH": true,
|
||||
"BCH": true,
|
||||
"LTC": true,
|
||||
"USDT": true,
|
||||
"BNB": true,
|
||||
"COMP": true,
|
||||
"XRP": true,
|
||||
"LINK": true,
|
||||
"DOT": true,
|
||||
"SXP": true,
|
||||
"DAI": true,
|
||||
"MAX": true,
|
||||
"TWD": true,
|
||||
"SNT": true,
|
||||
"YFI": true,
|
||||
"GRT": true,
|
||||
}
|
||||
BTC: true,
|
||||
ETH: true,
|
||||
BCH: true,
|
||||
LTC: true,
|
||||
USDT: true,
|
||||
BNB: true,
|
||||
COMP: true,
|
||||
XRP: true,
|
||||
LINK: true,
|
||||
DOT: true,
|
||||
SXP: true,
|
||||
DAI: true,
|
||||
MAX: true,
|
||||
TWD: true,
|
||||
SNT: true,
|
||||
YFI: true,
|
||||
GRT: true,
|
||||
};
|
||||
|
||||
export default function TotalAssetsDetails({assets}) {
|
||||
const classes = useStyles();
|
||||
export default function TotalAssetsDetails({ assets }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const sortedAssets = [];
|
||||
for (let k in assets) {
|
||||
sortedAssets.push(assets[k]);
|
||||
const sortedAssets = [];
|
||||
for (let k in assets) {
|
||||
sortedAssets.push(assets[k]);
|
||||
}
|
||||
sortedAssets.sort((a, b) => {
|
||||
if (a.inUSD > b.inUSD) {
|
||||
return -1;
|
||||
}
|
||||
sortedAssets.sort((a, b) => {
|
||||
if (a.inUSD > b.inUSD) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (a.inUSD < b.inUSD) {
|
||||
return 1
|
||||
}
|
||||
if (a.inUSD < b.inUSD) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
|
||||
const items = sortedAssets.map((a) => {
|
||||
return (
|
||||
<ListItem key={a.currency} dense>
|
||||
{
|
||||
(a.currency in logoCurrencies) ? (
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={a.currency} src={`/images/${a.currency.toLowerCase()}-logo.svg`}/>
|
||||
</ListItemAvatar>
|
||||
) : (
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={a.currency}/>
|
||||
</ListItemAvatar>
|
||||
)
|
||||
}
|
||||
<ListItemText primary={`${a.currency} ${a.total}`} secondary={`=~ ${Math.round(a.inUSD)} USD`}/>
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
return 0;
|
||||
});
|
||||
|
||||
const items = sortedAssets.map((a) => {
|
||||
return (
|
||||
<Card className={classes.root} variant="outlined">
|
||||
<CardContent className={classes.cardContent}>
|
||||
<List dense>
|
||||
{items}
|
||||
</List>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<ListItem key={a.currency} dense>
|
||||
{a.currency in logoCurrencies ? (
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
alt={a.currency}
|
||||
src={`/images/${a.currency.toLowerCase()}-logo.svg`}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
) : (
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={a.currency} />
|
||||
</ListItemAvatar>
|
||||
)}
|
||||
<ListItemText
|
||||
primary={`${a.currency} ${a.total}`}
|
||||
secondary={`=~ ${Math.round(a.inUSD)} USD`}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Card className={classes.root} variant="outlined">
|
||||
<CardContent className={classes.cardContent}>
|
||||
<List dense>{items}</List>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,95 +1,94 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {ResponsivePie} from '@nivo/pie';
|
||||
import {queryAssets} from '../api/bbgo';
|
||||
import {currencyColor} from '../src/utils';
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import { ResponsivePie } from '@nivo/pie';
|
||||
import { queryAssets } from '../api/bbgo';
|
||||
import { currencyColor } from '../src/utils';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
function reduceAssetsBy(assets, field, minimum) {
|
||||
let as = []
|
||||
let as = [];
|
||||
|
||||
let others = {id: "others", labels: "others", value: 0.0}
|
||||
for (let key in assets) {
|
||||
if (assets[key]) {
|
||||
let a = assets[key]
|
||||
let value = a[field]
|
||||
let others = { id: 'others', labels: 'others', value: 0.0 };
|
||||
for (let key in assets) {
|
||||
if (assets[key]) {
|
||||
let a = assets[key];
|
||||
let value = a[field];
|
||||
|
||||
if (value < minimum) {
|
||||
others.value += value
|
||||
} else {
|
||||
as.push({
|
||||
id: a.currency,
|
||||
label: a.currency,
|
||||
color: currencyColor(a.currency),
|
||||
value: Math.round(value, 1),
|
||||
})
|
||||
}
|
||||
}
|
||||
if (value < minimum) {
|
||||
others.value += value;
|
||||
} else {
|
||||
as.push({
|
||||
id: a.currency,
|
||||
label: a.currency,
|
||||
color: currencyColor(a.currency),
|
||||
value: Math.round(value, 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return as
|
||||
return as;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
cardContent: {
|
||||
height: 350,
|
||||
}
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
cardContent: {
|
||||
height: 350,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function TotalAssetsPie({ assets }) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Card className={classes.root} variant="outlined">
|
||||
<CardContent className={classes.cardContent}>
|
||||
<ResponsivePie
|
||||
data={reduceAssetsBy(assets, "inUSD", 2)}
|
||||
margin={{top: 20, right: 80, bottom: 10, left: 0}}
|
||||
padding={0.1}
|
||||
innerRadius={0.8}
|
||||
padAngle={1.0}
|
||||
valueFormat=" >-$f"
|
||||
colors={{datum: 'data.color'}}
|
||||
// colors={{scheme: 'nivo'}}
|
||||
cornerRadius={0.1}
|
||||
borderWidth={1}
|
||||
borderColor={{from: 'color', modifiers: [['darker', 0.2]]}}
|
||||
radialLabelsSkipAngle={10}
|
||||
radialLabelsTextColor="#333333"
|
||||
radialLabelsLinkColor={{from: 'color'}}
|
||||
sliceLabelsSkipAngle={30}
|
||||
sliceLabelsTextColor="#fff"
|
||||
legends={[
|
||||
{
|
||||
anchor: 'right',
|
||||
direction: 'column',
|
||||
justify: false,
|
||||
translateX: 70,
|
||||
translateY: 0,
|
||||
itemsSpacing: 5,
|
||||
itemWidth: 80,
|
||||
itemHeight: 24,
|
||||
itemTextColor: '#999',
|
||||
itemOpacity: 1,
|
||||
symbolSize: 18,
|
||||
symbolShape: 'circle',
|
||||
effects: [
|
||||
{
|
||||
on: 'hover',
|
||||
style: {
|
||||
itemTextColor: '#000'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Card className={classes.root} variant="outlined">
|
||||
<CardContent className={classes.cardContent}>
|
||||
<ResponsivePie
|
||||
data={reduceAssetsBy(assets, 'inUSD', 2)}
|
||||
margin={{ top: 20, right: 80, bottom: 10, left: 0 }}
|
||||
padding={0.1}
|
||||
innerRadius={0.8}
|
||||
padAngle={1.0}
|
||||
valueFormat=" >-$f"
|
||||
colors={{ datum: 'data.color' }}
|
||||
// colors={{scheme: 'nivo'}}
|
||||
cornerRadius={0.1}
|
||||
borderWidth={1}
|
||||
borderColor={{ from: 'color', modifiers: [['darker', 0.2]] }}
|
||||
radialLabelsSkipAngle={10}
|
||||
radialLabelsTextColor="#333333"
|
||||
radialLabelsLinkColor={{ from: 'color' }}
|
||||
sliceLabelsSkipAngle={30}
|
||||
sliceLabelsTextColor="#fff"
|
||||
legends={[
|
||||
{
|
||||
anchor: 'right',
|
||||
direction: 'column',
|
||||
justify: false,
|
||||
translateX: 70,
|
||||
translateY: 0,
|
||||
itemsSpacing: 5,
|
||||
itemWidth: 80,
|
||||
itemHeight: 24,
|
||||
itemTextColor: '#999',
|
||||
itemOpacity: 1,
|
||||
symbolSize: 18,
|
||||
symbolShape: 'circle',
|
||||
effects: [
|
||||
{
|
||||
on: 'hover',
|
||||
style: {
|
||||
itemTextColor: '#000',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,52 +1,60 @@
|
|||
import {useEffect, useState} from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
function aggregateAssetsBy(assets, field) {
|
||||
let total = 0.0
|
||||
for (let key in assets) {
|
||||
if (assets[key]) {
|
||||
let a = assets[key]
|
||||
let value = a[field]
|
||||
total += value
|
||||
}
|
||||
let total = 0.0;
|
||||
for (let key in assets) {
|
||||
if (assets[key]) {
|
||||
let a = assets[key];
|
||||
let value = a[field];
|
||||
total += value;
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
return total;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
},
|
||||
pos: {
|
||||
marginTop: 12,
|
||||
},
|
||||
root: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
},
|
||||
pos: {
|
||||
marginTop: 12,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function TotalAssetSummary({ assets }) {
|
||||
const classes = useStyles();
|
||||
return <Card className={classes.root} variant="outlined">
|
||||
<CardContent>
|
||||
<Typography className={classes.title} color="textSecondary" gutterBottom>
|
||||
Total Account Balance
|
||||
</Typography>
|
||||
<Typography variant="h5" component="h2">
|
||||
{Math.round(aggregateAssetsBy(assets, "inBTC") * 1e8) / 1e8} <span>BTC</span>
|
||||
</Typography>
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Card className={classes.root} variant="outlined">
|
||||
<CardContent>
|
||||
<Typography
|
||||
className={classes.title}
|
||||
color="textSecondary"
|
||||
gutterBottom
|
||||
>
|
||||
Total Account Balance
|
||||
</Typography>
|
||||
<Typography variant="h5" component="h2">
|
||||
{Math.round(aggregateAssetsBy(assets, 'inBTC') * 1e8) / 1e8}{' '}
|
||||
<span>BTC</span>
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.pos} color="textSecondary">
|
||||
Estimated Value
|
||||
</Typography>
|
||||
<Typography className={classes.pos} color="textSecondary">
|
||||
Estimated Value
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h5" component="h3">
|
||||
{Math.round(aggregateAssetsBy(assets, "inUSD") * 100) / 100} <span>USD</span>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<Typography variant="h5" component="h3">
|
||||
{Math.round(aggregateAssetsBy(assets, 'inUSD') * 100) / 100}{' '}
|
||||
<span>USD</span>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,152 +1,161 @@
|
|||
import {ResponsiveBar} from '@nivo/bar';
|
||||
import {queryTradingVolume} from '../api/bbgo';
|
||||
import {useEffect, useState} from "react";
|
||||
import { ResponsiveBar } from '@nivo/bar';
|
||||
import { queryTradingVolume } from '../api/bbgo';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
function toPeriodDateString(time, period) {
|
||||
switch (period) {
|
||||
case "day":
|
||||
return time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate()
|
||||
case "month":
|
||||
return time.getFullYear() + "-" + (time.getMonth() + 1)
|
||||
case "year":
|
||||
return time.getFullYear()
|
||||
switch (period) {
|
||||
case 'day':
|
||||
return (
|
||||
time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate()
|
||||
);
|
||||
case 'month':
|
||||
return time.getFullYear() + '-' + (time.getMonth() + 1);
|
||||
case 'year':
|
||||
return time.getFullYear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate()
|
||||
return (
|
||||
time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate()
|
||||
);
|
||||
}
|
||||
|
||||
function groupData(rows, period, segment) {
|
||||
let dateIndex = {}
|
||||
let startTime = null
|
||||
let endTime = null
|
||||
let keys = {}
|
||||
let dateIndex = {};
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
let keys = {};
|
||||
|
||||
rows.forEach((v) => {
|
||||
const time = new Date(v.time)
|
||||
if (!startTime) {
|
||||
startTime = time
|
||||
}
|
||||
|
||||
endTime = time
|
||||
|
||||
const dateStr = toPeriodDateString(time, period)
|
||||
const key = v[segment]
|
||||
|
||||
keys[key] = true
|
||||
|
||||
const k = key ? key : "total"
|
||||
const quoteVolume = Math.round(v.quoteVolume * 100) / 100
|
||||
|
||||
if (dateIndex[dateStr]) {
|
||||
dateIndex[dateStr][k] = quoteVolume
|
||||
} else {
|
||||
dateIndex[dateStr] = {
|
||||
date: dateStr,
|
||||
year: time.getFullYear(),
|
||||
month: time.getMonth() + 1,
|
||||
day: time.getDate(),
|
||||
[k]: quoteVolume,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let data = []
|
||||
while (startTime < endTime) {
|
||||
const dateStr = toPeriodDateString(startTime, period)
|
||||
const groupData = dateIndex[dateStr]
|
||||
if (groupData) {
|
||||
data.push(groupData)
|
||||
} else {
|
||||
data.push({
|
||||
date: dateStr,
|
||||
year: startTime.getFullYear(),
|
||||
month: startTime.getMonth() + 1,
|
||||
day: startTime.getDate(),
|
||||
total: 0,
|
||||
})
|
||||
}
|
||||
|
||||
switch (period) {
|
||||
case "day":
|
||||
startTime.setDate(startTime.getDate() + 1)
|
||||
break
|
||||
case "month":
|
||||
startTime.setMonth(startTime.getMonth() + 1)
|
||||
break
|
||||
case "year":
|
||||
startTime.setFullYear(startTime.getFullYear() + 1)
|
||||
break
|
||||
}
|
||||
rows.forEach((v) => {
|
||||
const time = new Date(v.time);
|
||||
if (!startTime) {
|
||||
startTime = time;
|
||||
}
|
||||
|
||||
return [data, Object.keys(keys)]
|
||||
endTime = time;
|
||||
|
||||
const dateStr = toPeriodDateString(time, period);
|
||||
const key = v[segment];
|
||||
|
||||
keys[key] = true;
|
||||
|
||||
const k = key ? key : 'total';
|
||||
const quoteVolume = Math.round(v.quoteVolume * 100) / 100;
|
||||
|
||||
if (dateIndex[dateStr]) {
|
||||
dateIndex[dateStr][k] = quoteVolume;
|
||||
} else {
|
||||
dateIndex[dateStr] = {
|
||||
date: dateStr,
|
||||
year: time.getFullYear(),
|
||||
month: time.getMonth() + 1,
|
||||
day: time.getDate(),
|
||||
[k]: quoteVolume,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let data = [];
|
||||
while (startTime < endTime) {
|
||||
const dateStr = toPeriodDateString(startTime, period);
|
||||
const groupData = dateIndex[dateStr];
|
||||
if (groupData) {
|
||||
data.push(groupData);
|
||||
} else {
|
||||
data.push({
|
||||
date: dateStr,
|
||||
year: startTime.getFullYear(),
|
||||
month: startTime.getMonth() + 1,
|
||||
day: startTime.getDate(),
|
||||
total: 0,
|
||||
});
|
||||
}
|
||||
|
||||
switch (period) {
|
||||
case 'day':
|
||||
startTime.setDate(startTime.getDate() + 1);
|
||||
break;
|
||||
case 'month':
|
||||
startTime.setMonth(startTime.getMonth() + 1);
|
||||
break;
|
||||
case 'year':
|
||||
startTime.setFullYear(startTime.getFullYear() + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [data, Object.keys(keys)];
|
||||
}
|
||||
|
||||
export default function TradingVolumeBar(props) {
|
||||
const [tradingVolumes, setTradingVolumes] = useState([])
|
||||
const [period, setPeriod] = useState(props.period)
|
||||
const [segment, setSegment] = useState(props.segment)
|
||||
const [tradingVolumes, setTradingVolumes] = useState([]);
|
||||
const [period, setPeriod] = useState(props.period);
|
||||
const [segment, setSegment] = useState(props.segment);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.period !== period) {
|
||||
setPeriod(props.period);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (props.period !== period) {
|
||||
setPeriod(props.period);
|
||||
}
|
||||
|
||||
if (props.segment !== segment) {
|
||||
setSegment(props.segment);
|
||||
}
|
||||
if (props.segment !== segment) {
|
||||
setSegment(props.segment);
|
||||
}
|
||||
|
||||
queryTradingVolume({period: props.period, segment: props.segment }, (tradingVolumes) => {
|
||||
setTradingVolumes(tradingVolumes)
|
||||
})
|
||||
}, [props.period, props.segment])
|
||||
queryTradingVolume(
|
||||
{ period: props.period, segment: props.segment },
|
||||
(tradingVolumes) => {
|
||||
setTradingVolumes(tradingVolumes);
|
||||
}
|
||||
);
|
||||
}, [props.period, props.segment]);
|
||||
|
||||
const [data, keys] = groupData(tradingVolumes, period, segment)
|
||||
const [data, keys] = groupData(tradingVolumes, period, segment);
|
||||
|
||||
return <ResponsiveBar keys={keys}
|
||||
data={data}
|
||||
indexBy={"date"}
|
||||
margin={{top: 50, right: 160, bottom: 100, left: 60}}
|
||||
padding={0.3}
|
||||
valueScale={{type: 'linear'}}
|
||||
indexScale={{type: 'band', round: true}}
|
||||
labelSkipWidth={30}
|
||||
labelSkipHeight={20}
|
||||
enableGridY={true}
|
||||
colors={{scheme: 'paired'}}
|
||||
axisBottom={{
|
||||
tickRotation: -90,
|
||||
legend: period,
|
||||
legendPosition: 'middle',
|
||||
legendOffset: 80
|
||||
}}
|
||||
legends={[
|
||||
{
|
||||
dataFrom: 'keys',
|
||||
anchor: 'right',
|
||||
direction: 'column',
|
||||
justify: false,
|
||||
translateX: 120,
|
||||
translateY: 0,
|
||||
itemsSpacing: 2,
|
||||
itemWidth: 100,
|
||||
itemHeight: 20,
|
||||
itemDirection: 'left-to-right',
|
||||
itemOpacity: 0.85,
|
||||
symbolSize: 20,
|
||||
effects: [
|
||||
{
|
||||
on: 'hover',
|
||||
style: {
|
||||
itemOpacity: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
animate={true}
|
||||
motionStiffness={90}
|
||||
motionDamping={15}
|
||||
/>;
|
||||
return (
|
||||
<ResponsiveBar
|
||||
keys={keys}
|
||||
data={data}
|
||||
indexBy={'date'}
|
||||
margin={{ top: 50, right: 160, bottom: 100, left: 60 }}
|
||||
padding={0.3}
|
||||
valueScale={{ type: 'linear' }}
|
||||
indexScale={{ type: 'band', round: true }}
|
||||
labelSkipWidth={30}
|
||||
labelSkipHeight={20}
|
||||
enableGridY={true}
|
||||
colors={{ scheme: 'paired' }}
|
||||
axisBottom={{
|
||||
tickRotation: -90,
|
||||
legend: period,
|
||||
legendPosition: 'middle',
|
||||
legendOffset: 80,
|
||||
}}
|
||||
legends={[
|
||||
{
|
||||
dataFrom: 'keys',
|
||||
anchor: 'right',
|
||||
direction: 'column',
|
||||
justify: false,
|
||||
translateX: 120,
|
||||
translateY: 0,
|
||||
itemsSpacing: 2,
|
||||
itemWidth: 100,
|
||||
itemHeight: 20,
|
||||
itemDirection: 'left-to-right',
|
||||
itemOpacity: 0.85,
|
||||
symbolSize: 20,
|
||||
effects: [
|
||||
{
|
||||
on: 'hover',
|
||||
style: {
|
||||
itemOpacity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
animate={true}
|
||||
motionStiffness={90}
|
||||
motionDamping={15}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,66 +1,72 @@
|
|||
import Paper from "@material-ui/core/Paper";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import React from "react";
|
||||
import TradingVolumeBar from "./TradingVolumeBar";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Tabs from '@material-ui/core/Tabs';
|
||||
import Tab from '@material-ui/core/Tab';
|
||||
import React from 'react';
|
||||
import TradingVolumeBar from './TradingVolumeBar';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
tradingVolumeBarBox: {
|
||||
height: 400,
|
||||
},
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
}
|
||||
tradingVolumeBarBox: {
|
||||
height: 400,
|
||||
},
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
export default function TradingVolumePanel() {
|
||||
const [period, setPeriod] = React.useState("day");
|
||||
const [segment, setSegment] = React.useState("exchange");
|
||||
const classes = useStyles();
|
||||
const handlePeriodChange = (event, newValue) => {
|
||||
setPeriod(newValue);
|
||||
};
|
||||
const [period, setPeriod] = React.useState('day');
|
||||
const [segment, setSegment] = React.useState('exchange');
|
||||
const classes = useStyles();
|
||||
const handlePeriodChange = (event, newValue) => {
|
||||
setPeriod(newValue);
|
||||
};
|
||||
|
||||
const handleSegmentChange = (event, newValue) => {
|
||||
setSegment(newValue);
|
||||
};
|
||||
const handleSegmentChange = (event, newValue) => {
|
||||
setSegment(newValue);
|
||||
};
|
||||
|
||||
return <Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Trading Volume
|
||||
</Typography>
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Trading Volume
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Tabs value={period}
|
||||
onChange={handlePeriodChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary">
|
||||
<Tab label="Day" value={"day"}/>
|
||||
<Tab label="Month" value={"month"}/>
|
||||
<Tab label="Year" value={"year"}/>
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid container justifyContent={"flex-end"}>
|
||||
<Tabs value={segment}
|
||||
onChange={handleSegmentChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary">
|
||||
<Tab label="By Exchange" value={"exchange"}/>
|
||||
<Tab label="By Symbol" value={"symbol"}/>
|
||||
</Tabs>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Tabs
|
||||
value={period}
|
||||
onChange={handlePeriodChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
<Tab label="Day" value={'day'} />
|
||||
<Tab label="Month" value={'month'} />
|
||||
<Tab label="Year" value={'year'} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid container justifyContent={'flex-end'}>
|
||||
<Tabs
|
||||
value={segment}
|
||||
onChange={handleSegmentChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
<Tab label="By Exchange" value={'exchange'} />
|
||||
<Tab label="By Symbol" value={'symbol'} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Box className={classes.tradingVolumeBarBox}>
|
||||
<TradingVolumeBar period={period} segment={segment}/>
|
||||
</Box>
|
||||
</Paper>;
|
||||
<Box className={classes.tradingVolumeBarBox}>
|
||||
<TradingVolumeBar period={period} segment={segment} />
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,58 +1,62 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Container from '@material-ui/core/Container';
|
||||
|
||||
import SideBar from "../components/SideBar";
|
||||
import SideBar from '../components/SideBar';
|
||||
|
||||
import ConnectWallet from '../components/ConnectWallet';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
container: { },
|
||||
toolbar:{
|
||||
justifyContent: 'space-between',
|
||||
}
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
container: {},
|
||||
toolbar: {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
}));
|
||||
|
||||
export default function DashboardLayout({children}) {
|
||||
const classes = useStyles();
|
||||
export default function DashboardLayout({ children }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
BBGO
|
||||
</Typography>
|
||||
{/* <Button color="inherit">Login</Button> */}
|
||||
<ConnectWallet />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
BBGO
|
||||
</Typography>
|
||||
{/* <Button color="inherit">Login</Button> */}
|
||||
<ConnectWallet />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<SideBar/>
|
||||
<SideBar />
|
||||
|
||||
<main className={classes.content}>
|
||||
<div className={classes.appBarSpacer}/>
|
||||
<Container className={classes.container} maxWidth={false} disableGutters={true}>
|
||||
{children}
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
<main className={classes.content}>
|
||||
<div className={classes.appBarSpacer} />
|
||||
<Container
|
||||
className={classes.container}
|
||||
maxWidth={false}
|
||||
disableGutters={true}
|
||||
>
|
||||
{children}
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Container from '@material-ui/core/Container';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
// flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
root: {
|
||||
// flexGrow: 1,
|
||||
display: 'flex',
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
}));
|
||||
|
||||
export default function PlainLayout(props) {
|
||||
const classes = useStyles();
|
||||
return <div className={classes.root}>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
{ props && props.title ? props.title : "BBGO Setup Wizard" }
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
{props && props.title ? props.title : 'BBGO Setup Wizard'}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<main className={classes.content}>
|
||||
<div className={classes.appBarSpacer}/>
|
||||
<Container>
|
||||
{props.children}
|
||||
</Container>
|
||||
</main>
|
||||
</div>;
|
||||
<main className={classes.content}>
|
||||
<div className={classes.appBarSpacer} />
|
||||
<Container>{props.children}</Container>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const withTM = require('next-transpile-modules')
|
||||
([
|
||||
'@react-spring/three',
|
||||
'@react-spring/web',
|
||||
])
|
||||
const withTM = require('next-transpile-modules')([
|
||||
'@react-spring/three',
|
||||
'@react-spring/web',
|
||||
]);
|
||||
|
||||
module.exports = withTM({
|
||||
// disable webpack 5 to make it compatible with the following rules
|
||||
|
@ -11,7 +10,7 @@ module.exports = withTM({
|
|||
config.module.rules.push({
|
||||
test: /react-spring/,
|
||||
sideEffects: true,
|
||||
})
|
||||
return config
|
||||
});
|
||||
return config;
|
||||
},
|
||||
})
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import Head from 'next/head';
|
||||
|
||||
import {ThemeProvider} from '@material-ui/core/styles';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
|
@ -13,117 +13,121 @@ import Box from '@material-ui/core/Box';
|
|||
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import theme from '../src/theme';
|
||||
import '../styles/globals.css'
|
||||
import {querySessions, querySyncStatus} from "../api/bbgo";
|
||||
import '../styles/globals.css';
|
||||
import { querySessions, querySyncStatus } from '../api/bbgo';
|
||||
|
||||
const SyncNotStarted = 0
|
||||
const Syncing = 1
|
||||
const SyncDone = 2
|
||||
const SyncNotStarted = 0;
|
||||
const Syncing = 1;
|
||||
const SyncDone = 2;
|
||||
|
||||
// session is configured, check if we're syncing data
|
||||
let syncStatusPoller = null
|
||||
let syncStatusPoller = null;
|
||||
|
||||
export default function MyApp(props) {
|
||||
const {Component, pageProps} = props;
|
||||
const { Component, pageProps } = props;
|
||||
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const [syncing, setSyncing] = React.useState(false)
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [syncing, setSyncing] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Remove the server-side injected CSS.
|
||||
const jssStyles = document.querySelector('#jss-server-side');
|
||||
if (jssStyles) {
|
||||
jssStyles.parentElement.removeChild(jssStyles);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
// Remove the server-side injected CSS.
|
||||
const jssStyles = document.querySelector('#jss-server-side');
|
||||
if (jssStyles) {
|
||||
jssStyles.parentElement.removeChild(jssStyles);
|
||||
}
|
||||
|
||||
querySessions((sessions) => {
|
||||
if (sessions.length > 0) {
|
||||
setSyncing(true)
|
||||
querySessions((sessions) => {
|
||||
if (sessions.length > 0) {
|
||||
setSyncing(true);
|
||||
|
||||
const pollSyncStatus = () => {
|
||||
querySyncStatus((status) => {
|
||||
switch (status) {
|
||||
case SyncNotStarted:
|
||||
break
|
||||
case Syncing:
|
||||
setSyncing(true);
|
||||
break;
|
||||
case SyncDone:
|
||||
clearInterval(syncStatusPoller);
|
||||
setLoading(false);
|
||||
setSyncing(false);
|
||||
break;
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
syncStatusPoller = setInterval(pollSyncStatus, 1000)
|
||||
} else {
|
||||
// no session found, so we can not sync any data
|
||||
setLoading(false)
|
||||
setSyncing(false)
|
||||
const pollSyncStatus = () => {
|
||||
querySyncStatus((status) => {
|
||||
switch (status) {
|
||||
case SyncNotStarted:
|
||||
break;
|
||||
case Syncing:
|
||||
setSyncing(true);
|
||||
break;
|
||||
case SyncDone:
|
||||
clearInterval(syncStatusPoller);
|
||||
setLoading(false);
|
||||
setSyncing(false);
|
||||
break;
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
}, []);
|
||||
syncStatusPoller = setInterval(pollSyncStatus, 1000);
|
||||
} else {
|
||||
// no session found, so we can not sync any data
|
||||
setLoading(false);
|
||||
setSyncing(false);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>BBGO</title>
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>
|
||||
</Head>
|
||||
<ThemeProvider theme={theme}>
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
<CssBaseline/>
|
||||
{
|
||||
loading ? (syncing ? (
|
||||
<Dialog
|
||||
open={syncing}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Syncing Trades"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
The environment is syncing trades from the exchange sessions.
|
||||
Please wait a moment...
|
||||
</DialogContentText>
|
||||
<Box m={2}>
|
||||
<LinearProgress/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : (
|
||||
<Dialog
|
||||
open={loading}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Loading"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Loading...
|
||||
</DialogContentText>
|
||||
<Box m={2}>
|
||||
<LinearProgress/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)) : (
|
||||
<Component {...pageProps}/>
|
||||
)
|
||||
}
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>BBGO</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||
/>
|
||||
</Head>
|
||||
<ThemeProvider theme={theme}>
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
<CssBaseline />
|
||||
{loading ? (
|
||||
syncing ? (
|
||||
<Dialog
|
||||
open={syncing}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{'Syncing Trades'}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
The environment is syncing trades from the exchange sessions.
|
||||
Please wait a moment...
|
||||
</DialogContentText>
|
||||
<Box m={2}>
|
||||
<LinearProgress />
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : (
|
||||
<Dialog
|
||||
open={loading}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{'Loading'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Loading...
|
||||
</DialogContentText>
|
||||
<Box m={2}>
|
||||
<LinearProgress />
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
) : (
|
||||
<Component {...pageProps} />
|
||||
)}
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
MyApp.propTypes = {
|
||||
Component: PropTypes.elementType.isRequired,
|
||||
pageProps: PropTypes.object.isRequired,
|
||||
Component: PropTypes.elementType.isRequired,
|
||||
pageProps: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/* eslint-disable react/jsx-filename-extension */
|
||||
import React from 'react';
|
||||
import Document, {
|
||||
Html, Head, Main, NextScript,
|
||||
} from 'next/document';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { ServerStyleSheets } from '@material-ui/styles';
|
||||
import theme from '../src/theme';
|
||||
|
||||
|
@ -66,6 +64,9 @@ MyDocument.getInitialProps = async (ctx) => {
|
|||
return {
|
||||
...initialProps,
|
||||
// Styles fragment is rendered after the app and page rendering finish.
|
||||
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
|
||||
styles: [
|
||||
...React.Children.toArray(initialProps.styles),
|
||||
sheets.getStyleElement(),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
export default (req, res) => {
|
||||
res.statusCode = 200
|
||||
res.json({ name: 'John Doe' })
|
||||
}
|
||||
res.statusCode = 200;
|
||||
res.json({ name: 'John Doe' });
|
||||
};
|
||||
|
|
|
@ -1,57 +1,55 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import PlainLayout from '../../layouts/PlainLayout';
|
||||
import {QRCodeSVG} from 'qrcode.react';
|
||||
import {queryOutboundIP} from '../../api/bbgo';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { queryOutboundIP } from '../../api/bbgo';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
textAlign: 'center',
|
||||
alignItems: 'center',
|
||||
alignContent: 'center',
|
||||
height: 320,
|
||||
}
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
textAlign: 'center',
|
||||
alignItems: 'center',
|
||||
alignContent: 'center',
|
||||
height: 320,
|
||||
},
|
||||
}));
|
||||
|
||||
function fetchConnectUrl(cb) {
|
||||
return queryOutboundIP((outboundIP) => {
|
||||
cb(window.location.protocol + "//" + outboundIP + ":" + window.location.port)
|
||||
})
|
||||
return queryOutboundIP((outboundIP) => {
|
||||
cb(
|
||||
window.location.protocol + '//' + outboundIP + ':' + window.location.port
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default function Connect() {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const [connectUrl, setConnectUrl] = useState([])
|
||||
const [connectUrl, setConnectUrl] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchConnectUrl(function (url) {
|
||||
setConnectUrl(url)
|
||||
})
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
fetchConnectUrl(function (url) {
|
||||
setConnectUrl(url);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PlainLayout title={"Connect"}>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Sign In Using QR Codes
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<QRCodeSVG
|
||||
size={160}
|
||||
style={{flexGrow: 1}}
|
||||
value={connectUrl}/>
|
||||
</div>
|
||||
</Paper>
|
||||
</PlainLayout>
|
||||
);
|
||||
return (
|
||||
<PlainLayout title={'Connect'}>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Sign In Using QR Codes
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<QRCodeSVG size={160} style={{ flexGrow: 1 }} value={connectUrl} />
|
||||
</div>
|
||||
</Paper>
|
||||
</PlainLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {useState} from 'react';
|
||||
import {useRouter} from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
@ -16,100 +16,98 @@ import ExchangeSessionTabPanel from '../components/ExchangeSessionTabPanel';
|
|||
|
||||
import DashboardLayout from '../layouts/DashboardLayout';
|
||||
|
||||
import {queryAssets, querySessions} from "../api/bbgo";
|
||||
import { queryAssets, querySessions } from '../api/bbgo';
|
||||
|
||||
import { ChainId, Config, DAppProvider } from '@usedapp/core';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
totalAssetsSummary: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
grid: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
control: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
totalAssetsSummary: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
grid: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
control: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const config: Config = {
|
||||
readOnlyChainId: ChainId.Mainnet,
|
||||
readOnlyUrls: {
|
||||
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
},
|
||||
}
|
||||
|
||||
readOnlyChainId: ChainId.Mainnet,
|
||||
readOnlyUrls: {
|
||||
[ChainId.Mainnet]:
|
||||
'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
},
|
||||
};
|
||||
|
||||
// props are pageProps passed from _app.tsx
|
||||
export default function Home() {
|
||||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
|
||||
const [assets, setAssets] = useState({})
|
||||
const [sessions, setSessions] = React.useState([])
|
||||
const [assets, setAssets] = useState({});
|
||||
const [sessions, setSessions] = React.useState([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
if (sessions && sessions.length > 0) {
|
||||
setSessions(sessions)
|
||||
queryAssets(setAssets)
|
||||
} else {
|
||||
router.push("/setup");
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
})
|
||||
}, [router])
|
||||
|
||||
if (sessions.length == 0) {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Box m={4}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Loading
|
||||
</Typography>
|
||||
</Box>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
console.log("index: assets", assets)
|
||||
React.useEffect(() => {
|
||||
querySessions((sessions) => {
|
||||
if (sessions && sessions.length > 0) {
|
||||
setSessions(sessions);
|
||||
queryAssets(setAssets);
|
||||
} else {
|
||||
router.push('/setup');
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, [router]);
|
||||
|
||||
if (sessions.length == 0) {
|
||||
return (
|
||||
<DAppProvider config={config}>
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.totalAssetsSummary}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Total Assets
|
||||
</Typography>
|
||||
|
||||
<div className={classes.grid}>
|
||||
<Grid container
|
||||
direction="row"
|
||||
justifyContent="space-around"
|
||||
alignItems="flex-start"
|
||||
spacing={1}>
|
||||
<Grid item xs={12} md={8}>
|
||||
<TotalAssetSummary assets={assets}/>
|
||||
<TotalAssetsPie assets={assets}/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={4}>
|
||||
<TotalAssetDetails assets={assets}/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Paper>
|
||||
|
||||
<TradingVolumePanel/>
|
||||
|
||||
<ExchangeSessionTabPanel/>
|
||||
</DashboardLayout>
|
||||
</DAppProvider>
|
||||
|
||||
<DashboardLayout>
|
||||
<Box m={4}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Loading
|
||||
</Typography>
|
||||
</Box>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('index: assets', assets);
|
||||
|
||||
return (
|
||||
<DAppProvider config={config}>
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.totalAssetsSummary}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Total Assets
|
||||
</Typography>
|
||||
|
||||
<div className={classes.grid}>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="space-around"
|
||||
alignItems="flex-start"
|
||||
spacing={1}
|
||||
>
|
||||
<Grid item xs={12} md={8}>
|
||||
<TotalAssetSummary assets={assets} />
|
||||
<TotalAssetsPie assets={assets} />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={4}>
|
||||
<TotalAssetDetails assets={assets} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Paper>
|
||||
|
||||
<TradingVolumePanel />
|
||||
|
||||
<ExchangeSessionTabPanel />
|
||||
</DashboardLayout>
|
||||
</DAppProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,72 +1,81 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import {queryClosedOrders} from '../api/bbgo';
|
||||
import {DataGrid} from '@material-ui/data-grid';
|
||||
import { queryClosedOrders } from '../api/bbgo';
|
||||
import { DataGrid } from '@material-ui/data-grid';
|
||||
import DashboardLayout from '../layouts/DashboardLayout';
|
||||
|
||||
|
||||
const columns = [
|
||||
{field: 'gid', headerName: 'GID', width: 80, type: 'number'},
|
||||
{field: 'clientOrderID', headerName: 'Client Order ID', width: 130},
|
||||
{field: 'exchange', headerName: 'Exchange'},
|
||||
{field: 'symbol', headerName: 'Symbol'},
|
||||
{field: 'orderType', headerName: 'Type'},
|
||||
{field: 'side', headerName: 'Side', width: 90},
|
||||
{field: 'averagePrice', headerName: 'Average Price', type: 'number', width: 120},
|
||||
{field: 'quantity', headerName: 'Quantity', type: 'number'},
|
||||
{field: 'executedQuantity', headerName: 'Executed Quantity', type: 'number'},
|
||||
{field: 'status', headerName: 'Status'},
|
||||
{field: 'isMargin', headerName: 'Margin'},
|
||||
{field: 'isIsolated', headerName: 'Isolated'},
|
||||
{field: 'creationTime', headerName: 'Create Time', width: 200},
|
||||
{ field: 'gid', headerName: 'GID', width: 80, type: 'number' },
|
||||
{ field: 'clientOrderID', headerName: 'Client Order ID', width: 130 },
|
||||
{ field: 'exchange', headerName: 'Exchange' },
|
||||
{ field: 'symbol', headerName: 'Symbol' },
|
||||
{ field: 'orderType', headerName: 'Type' },
|
||||
{ field: 'side', headerName: 'Side', width: 90 },
|
||||
{
|
||||
field: 'averagePrice',
|
||||
headerName: 'Average Price',
|
||||
type: 'number',
|
||||
width: 120,
|
||||
},
|
||||
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
|
||||
{
|
||||
field: 'executedQuantity',
|
||||
headerName: 'Executed Quantity',
|
||||
type: 'number',
|
||||
},
|
||||
{ field: 'status', headerName: 'Status' },
|
||||
{ field: 'isMargin', headerName: 'Margin' },
|
||||
{ field: 'isIsolated', headerName: 'Isolated' },
|
||||
{ field: 'creationTime', headerName: 'Create Time', width: 200 },
|
||||
];
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
height: 'calc(100vh - 64px - 120px)',
|
||||
}
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
height: 'calc(100vh - 64px - 120px)',
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Orders() {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const [orders, setOrders] = useState([])
|
||||
const [orders, setOrders] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
queryClosedOrders({}, (orders) => {
|
||||
setOrders(orders.map((o) => {
|
||||
o.id = o.gid;
|
||||
return o
|
||||
}))
|
||||
useEffect(() => {
|
||||
queryClosedOrders({}, (orders) => {
|
||||
setOrders(
|
||||
orders.map((o) => {
|
||||
o.id = o.gid;
|
||||
return o;
|
||||
})
|
||||
}, [])
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Orders
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<div style={{flexGrow: 1}}>
|
||||
<DataGrid
|
||||
rows={orders}
|
||||
columns={columns}
|
||||
pageSize={50}
|
||||
autoPageSize={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</DashboardLayout>
|
||||
);
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Orders
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<DataGrid
|
||||
rows={orders}
|
||||
columns={columns}
|
||||
pageSize={50}
|
||||
autoPageSize={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
|
@ -8,99 +8,125 @@ import Stepper from '@material-ui/core/Stepper';
|
|||
import Step from '@material-ui/core/Step';
|
||||
import StepLabel from '@material-ui/core/StepLabel';
|
||||
|
||||
import ConfigureDatabaseForm from "../../components/ConfigureDatabaseForm";
|
||||
import AddExchangeSessionForm from "../../components/AddExchangeSessionForm";
|
||||
import ReviewSessions from "../../components/ReviewSessions";
|
||||
import ConfigureGridStrategyForm from "../../components/ConfigureGridStrategyForm";
|
||||
import ReviewStrategies from "../../components/ReviewStrategies";
|
||||
import SaveConfigAndRestart from "../../components/SaveConfigAndRestart";
|
||||
import ConfigureDatabaseForm from '../../components/ConfigureDatabaseForm';
|
||||
import AddExchangeSessionForm from '../../components/AddExchangeSessionForm';
|
||||
import ReviewSessions from '../../components/ReviewSessions';
|
||||
import ConfigureGridStrategyForm from '../../components/ConfigureGridStrategyForm';
|
||||
import ReviewStrategies from '../../components/ReviewStrategies';
|
||||
import SaveConfigAndRestart from '../../components/SaveConfigAndRestart';
|
||||
|
||||
import PlainLayout from '../../layouts/PlainLayout';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
const steps = ['Configure Database', 'Add Exchange Session', 'Review Sessions', 'Configure Strategy', 'Review Strategies', 'Save Config and Restart'];
|
||||
const steps = [
|
||||
'Configure Database',
|
||||
'Add Exchange Session',
|
||||
'Review Sessions',
|
||||
'Configure Strategy',
|
||||
'Review Strategies',
|
||||
'Save Config and Restart',
|
||||
];
|
||||
|
||||
function getStepContent(step, setActiveStep) {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return <ConfigureDatabaseForm onConfigured={() => {
|
||||
setActiveStep(1)
|
||||
}}/>;
|
||||
case 1:
|
||||
return (
|
||||
<AddExchangeSessionForm
|
||||
onBack={() => { setActiveStep(0) }}
|
||||
onAdded={() => { setActiveStep(2) }}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<ReviewSessions
|
||||
onBack={() => { setActiveStep(1) }}
|
||||
onNext={() => { setActiveStep(3) }}
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<ConfigureGridStrategyForm
|
||||
onBack={() => { setActiveStep(2) }}
|
||||
onAdded={() => { setActiveStep(4) }}
|
||||
/>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<ReviewStrategies
|
||||
onBack={() => { setActiveStep(3) }}
|
||||
onNext={() => { setActiveStep(5) }}
|
||||
/>
|
||||
);
|
||||
switch (step) {
|
||||
case 0:
|
||||
return (
|
||||
<ConfigureDatabaseForm
|
||||
onConfigured={() => {
|
||||
setActiveStep(1);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 1:
|
||||
return (
|
||||
<AddExchangeSessionForm
|
||||
onBack={() => {
|
||||
setActiveStep(0);
|
||||
}}
|
||||
onAdded={() => {
|
||||
setActiveStep(2);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<ReviewSessions
|
||||
onBack={() => {
|
||||
setActiveStep(1);
|
||||
}}
|
||||
onNext={() => {
|
||||
setActiveStep(3);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<ConfigureGridStrategyForm
|
||||
onBack={() => {
|
||||
setActiveStep(2);
|
||||
}}
|
||||
onAdded={() => {
|
||||
setActiveStep(4);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<ReviewStrategies
|
||||
onBack={() => {
|
||||
setActiveStep(3);
|
||||
}}
|
||||
onNext={() => {
|
||||
setActiveStep(5);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
case 5:
|
||||
return (
|
||||
<SaveConfigAndRestart
|
||||
onBack={() => { setActiveStep(4) }}
|
||||
onRestarted={() => {
|
||||
case 5:
|
||||
return (
|
||||
<SaveConfigAndRestart
|
||||
onBack={() => {
|
||||
setActiveStep(4);
|
||||
}}
|
||||
onRestarted={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
default:
|
||||
throw new Error('Unknown step');
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown step');
|
||||
}
|
||||
}
|
||||
|
||||
export default function Setup() {
|
||||
const classes = useStyles();
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
const classes = useStyles();
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
|
||||
return (
|
||||
<PlainLayout>
|
||||
<Box m={4}>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" component="h2" gutterBottom>
|
||||
Setup Session
|
||||
</Typography>
|
||||
return (
|
||||
<PlainLayout>
|
||||
<Box m={4}>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" component="h2" gutterBottom>
|
||||
Setup Session
|
||||
</Typography>
|
||||
|
||||
<Stepper activeStep={activeStep} className={classes.stepper}>
|
||||
{steps.map((label) => (
|
||||
<Step key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
<Stepper activeStep={activeStep} className={classes.stepper}>
|
||||
{steps.map((label) => (
|
||||
<Step key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
|
||||
<React.Fragment>
|
||||
{getStepContent(activeStep, setActiveStep)}
|
||||
</React.Fragment>
|
||||
</Paper>
|
||||
</Box>
|
||||
</PlainLayout>
|
||||
);
|
||||
<React.Fragment>
|
||||
{getStepContent(activeStep, setActiveStep)}
|
||||
</React.Fragment>
|
||||
</Paper>
|
||||
</Box>
|
||||
</PlainLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,67 +1,68 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import {queryTrades} from '../api/bbgo';
|
||||
import {DataGrid} from '@material-ui/data-grid';
|
||||
import { queryTrades } from '../api/bbgo';
|
||||
import { DataGrid } from '@material-ui/data-grid';
|
||||
import DashboardLayout from '../layouts/DashboardLayout';
|
||||
|
||||
const columns = [
|
||||
{field: 'gid', headerName: 'GID', width: 80, type: 'number'},
|
||||
{field: 'exchange', headerName: 'Exchange'},
|
||||
{field: 'symbol', headerName: 'Symbol'},
|
||||
{field: 'side', headerName: 'Side', width: 90},
|
||||
{field: 'price', headerName: 'Price', type: 'number', width: 120},
|
||||
{field: 'quantity', headerName: 'Quantity', type: 'number'},
|
||||
{field: 'isMargin', headerName: 'Margin'},
|
||||
{field: 'isIsolated', headerName: 'Isolated'},
|
||||
{field: 'tradedAt', headerName: 'Trade Time', width: 200},
|
||||
{ field: 'gid', headerName: 'GID', width: 80, type: 'number' },
|
||||
{ field: 'exchange', headerName: 'Exchange' },
|
||||
{ field: 'symbol', headerName: 'Symbol' },
|
||||
{ field: 'side', headerName: 'Side', width: 90 },
|
||||
{ field: 'price', headerName: 'Price', type: 'number', width: 120 },
|
||||
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
|
||||
{ field: 'isMargin', headerName: 'Margin' },
|
||||
{ field: 'isIsolated', headerName: 'Isolated' },
|
||||
{ field: 'tradedAt', headerName: 'Trade Time', width: 200 },
|
||||
];
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
height: 'calc(100vh - 64px - 120px)',
|
||||
}
|
||||
paper: {
|
||||
margin: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
dataGridContainer: {
|
||||
display: 'flex',
|
||||
height: 'calc(100vh - 64px - 120px)',
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Trades() {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const [trades, setTrades] = useState([])
|
||||
const [trades, setTrades] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
queryTrades({}, (trades) => {
|
||||
setTrades(trades.map((o) => {
|
||||
o.id = o.gid;
|
||||
return o
|
||||
}))
|
||||
useEffect(() => {
|
||||
queryTrades({}, (trades) => {
|
||||
setTrades(
|
||||
trades.map((o) => {
|
||||
o.id = o.gid;
|
||||
return o;
|
||||
})
|
||||
}, [])
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Trades
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<DataGrid
|
||||
rows={trades}
|
||||
columns={columns}
|
||||
showToolbar={true}
|
||||
autoPageSize={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</DashboardLayout>
|
||||
);
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Paper className={classes.paper}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Trades
|
||||
</Typography>
|
||||
<div className={classes.dataGridContainer}>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<DataGrid
|
||||
rows={trades}
|
||||
columns={columns}
|
||||
showToolbar={true}
|
||||
autoPageSize={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@ module.exports = {
|
|||
'postcss-preset-env',
|
||||
{
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009'
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
features: {
|
||||
'custom-properties': false
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
'custom-properties': false,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
|
||||
export function currencyColor(currency) {
|
||||
switch (currency) {
|
||||
case "BTC":
|
||||
return "#f69c3d"
|
||||
case "ETH":
|
||||
return "#497493"
|
||||
case "MCO":
|
||||
return "#032144"
|
||||
case "OMG":
|
||||
return "#2159ec"
|
||||
case "LTC":
|
||||
return "#949494"
|
||||
case "USDT":
|
||||
return "#2ea07b"
|
||||
case "SAND":
|
||||
return "#2E9AD0"
|
||||
case "XRP":
|
||||
return "#00AAE4"
|
||||
case "BCH":
|
||||
return "#8DC351"
|
||||
case "MAX":
|
||||
return "#2D4692"
|
||||
case "TWD":
|
||||
return "#4A7DED"
|
||||
|
||||
}
|
||||
switch (currency) {
|
||||
case 'BTC':
|
||||
return '#f69c3d';
|
||||
case 'ETH':
|
||||
return '#497493';
|
||||
case 'MCO':
|
||||
return '#032144';
|
||||
case 'OMG':
|
||||
return '#2159ec';
|
||||
case 'LTC':
|
||||
return '#949494';
|
||||
case 'USDT':
|
||||
return '#2ea07b';
|
||||
case 'SAND':
|
||||
return '#2E9AD0';
|
||||
case 'XRP':
|
||||
return '#00AAE4';
|
||||
case 'BCH':
|
||||
return '#8DC351';
|
||||
case 'MAX':
|
||||
return '#2D4692';
|
||||
case 'TWD':
|
||||
return '#4A7DED';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
|
@ -18,12 +14,6 @@
|
|||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user