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