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 || []);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
paddingBottom: theme.spacing(2),
|
paddingBottom: theme.spacing(2),
|
||||||
'& > *': {
|
'& > *': {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -60,16 +60,16 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
|
|
||||||
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 = () => {
|
const createSessionConfig = () => {
|
||||||
|
@ -82,34 +82,34 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
envVarPrefix: exchangeType.toUpperCase(),
|
envVarPrefix: exchangeType.toUpperCase(),
|
||||||
isolatedMargin: isIsolatedMargin,
|
isolatedMargin: isIsolatedMargin,
|
||||||
isolatedMarginSymbol: isolatedMarginSymbol,
|
isolatedMarginSymbol: isolatedMarginSymbol,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleAdd = (event) => {
|
const handleAdd = (event) => {
|
||||||
const payload = createSessionConfig()
|
const payload = createSessionConfig();
|
||||||
addSession(payload, (response) => {
|
addSession(payload, (response) => {
|
||||||
setResponse(response)
|
setResponse(response);
|
||||||
if (onAdded) {
|
if (onAdded) {
|
||||||
setTimeout(onAdded, 3000)
|
setTimeout(onAdded, 3000);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
setResponse(error.response)
|
setResponse(error.response);
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTestConnection = (event) => {
|
const handleTestConnection = (event) => {
|
||||||
const payload = createSessionConfig()
|
const payload = createSessionConfig();
|
||||||
setTesting(true)
|
setTesting(true);
|
||||||
testSessionConnection(payload, (response) => {
|
testSessionConnection(payload, (response) => {
|
||||||
console.log(response)
|
console.log(response);
|
||||||
setTesting(false)
|
setTesting(false);
|
||||||
setTestResponse(response)
|
setTestResponse(response);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
setTesting(false)
|
setTesting(false);
|
||||||
setTestResponse(error.response)
|
setTestResponse(error.response);
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -118,7 +118,6 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
Add Exchange Session
|
Add Exchange Session
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl className={classes.formControl}>
|
||||||
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
|
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
|
||||||
|
@ -128,8 +127,8 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
value={exchangeType}
|
value={exchangeType}
|
||||||
onChange={handleExchangeTypeChange}
|
onChange={handleExchangeTypeChange}
|
||||||
>
|
>
|
||||||
<MenuItem value={"binance"}>Binance</MenuItem>
|
<MenuItem value={'binance'}>Binance</MenuItem>
|
||||||
<MenuItem value={"max"}>Max</MenuItem>
|
<MenuItem value={'max'}>Max</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -143,7 +142,7 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
required
|
required
|
||||||
disabled={!customSessionName}
|
disabled={!customSessionName}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setSessionName(event.target.value)
|
setSessionName(event.target.value);
|
||||||
}}
|
}}
|
||||||
value={sessionName}
|
value={sessionName}
|
||||||
/>
|
/>
|
||||||
|
@ -151,16 +150,23 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
|
|
||||||
<Grid item xs={12} sm={6}>
|
<Grid item xs={12} sm={6}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Checkbox color="secondary" name="custom_session_name"
|
control={
|
||||||
|
<Checkbox
|
||||||
|
color="secondary"
|
||||||
|
name="custom_session_name"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setCustomSessionName(event.target.checked);
|
setCustomSessionName(event.target.checked);
|
||||||
}} value="1"/>}
|
}}
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
}
|
||||||
label="Custom exchange session name"
|
label="Custom exchange session name"
|
||||||
/>
|
/>
|
||||||
<FormHelperText id="session-name-helper-text">
|
<FormHelperText id="session-name-helper-text">
|
||||||
By default, the session name will be the exchange type name,
|
By default, the session name will be the exchange type name, e.g.{' '}
|
||||||
e.g. <code>binance</code> or <code>max</code>.<br/>
|
<code>binance</code> or <code>max</code>.<br />
|
||||||
If you're using multiple exchange sessions, you might need to custom the session name. <br/>
|
If you're using multiple exchange sessions, you might need to custom
|
||||||
|
the session name. <br />
|
||||||
This is for advanced users.
|
This is for advanced users.
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -176,8 +182,12 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="toggle key visibility"
|
aria-label="toggle key visibility"
|
||||||
onClick={() => { setShowApiKey(!showApiKey) }}
|
onClick={() => {
|
||||||
onMouseDown={(event) => { event.preventDefault() }}
|
setShowApiKey(!showApiKey);
|
||||||
|
}}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
edge="end"
|
edge="end"
|
||||||
>
|
>
|
||||||
{showApiKey ? <Visibility /> : <VisibilityOff />}
|
{showApiKey ? <Visibility /> : <VisibilityOff />}
|
||||||
|
@ -185,15 +195,14 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setApiKey(event.target.value)
|
setApiKey(event.target.value);
|
||||||
resetTestResponse()
|
resetTestResponse();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
|
|
||||||
<FormControl fullWidth variant="filled">
|
<FormControl fullWidth variant="filled">
|
||||||
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
|
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
|
||||||
<FilledInput
|
<FilledInput
|
||||||
|
@ -204,8 +213,12 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="toggle key visibility"
|
aria-label="toggle key visibility"
|
||||||
onClick={() => { setShowApiSecret(!showApiSecret) }}
|
onClick={() => {
|
||||||
onMouseDown={(event) => { event.preventDefault() }}
|
setShowApiSecret(!showApiSecret);
|
||||||
|
}}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
edge="end"
|
edge="end"
|
||||||
>
|
>
|
||||||
{showApiSecret ? <Visibility /> : <VisibilityOff />}
|
{showApiSecret ? <Visibility /> : <VisibilityOff />}
|
||||||
|
@ -213,49 +226,66 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setApiSecret(event.target.value)
|
setApiSecret(event.target.value);
|
||||||
resetTestResponse()
|
resetTestResponse();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{exchangeType === "binance" ? (
|
{exchangeType === 'binance' ? (
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Checkbox color="secondary" name="isMargin" onChange={(event) => {
|
control={
|
||||||
|
<Checkbox
|
||||||
|
color="secondary"
|
||||||
|
name="isMargin"
|
||||||
|
onChange={(event) => {
|
||||||
setIsMargin(event.target.checked);
|
setIsMargin(event.target.checked);
|
||||||
resetTestResponse();
|
resetTestResponse();
|
||||||
}} value="1"/>}
|
}}
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
}
|
||||||
label="Use margin trading."
|
label="Use margin trading."
|
||||||
/>
|
/>
|
||||||
<FormHelperText id="isMargin-helper-text">This is only available for Binance. Please use the
|
<FormHelperText id="isMargin-helper-text">
|
||||||
leverage at your own risk.</FormHelperText>
|
This is only available for Binance. Please use the leverage at
|
||||||
|
your own risk.
|
||||||
|
</FormHelperText>
|
||||||
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Checkbox color="secondary" name="isIsolatedMargin"
|
control={
|
||||||
|
<Checkbox
|
||||||
|
color="secondary"
|
||||||
|
name="isIsolatedMargin"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setIsIsolatedMargin(event.target.checked);
|
setIsIsolatedMargin(event.target.checked);
|
||||||
resetTestResponse()
|
resetTestResponse();
|
||||||
}} value="1"/>}
|
}}
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
}
|
||||||
label="Use isolated margin trading."
|
label="Use isolated margin trading."
|
||||||
/>
|
/>
|
||||||
<FormHelperText id="isIsolatedMargin-helper-text">This is only available for Binance. If this is
|
<FormHelperText id="isIsolatedMargin-helper-text">
|
||||||
set, you can only trade one symbol with one session.</FormHelperText>
|
This is only available for Binance. If this is set, you can only
|
||||||
|
trade one symbol with one session.
|
||||||
|
</FormHelperText>
|
||||||
|
|
||||||
{isIsolatedMargin ?
|
{isIsolatedMargin ? (
|
||||||
<TextField
|
<TextField
|
||||||
id="isolatedMarginSymbol"
|
id="isolatedMarginSymbol"
|
||||||
name="isolatedMarginSymbol"
|
name="isolatedMarginSymbol"
|
||||||
label="Isolated Margin Symbol"
|
label="Isolated Margin Symbol"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setIsolatedMarginSymbol(event.target.value);
|
setIsolatedMarginSymbol(event.target.value);
|
||||||
resetTestResponse()
|
resetTestResponse();
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
) : null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -266,28 +296,26 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
if (onBack) {
|
if (onBack) {
|
||||||
onBack();
|
onBack();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleTestConnection}
|
onClick={handleTestConnection}
|
||||||
disabled={testing}>
|
disabled={testing}
|
||||||
{testing ? "Testing" : "Test Connection"}
|
>
|
||||||
|
{testing ? 'Testing' : 'Test Connection'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button variant="contained" color="primary" onClick={handleAdd}>
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={handleAdd}
|
|
||||||
>
|
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{testResponse ? (
|
||||||
testResponse ? testResponse.error ? (
|
testResponse.error ? (
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="error">{testResponse.error}</Alert>
|
<Alert severity="error">{testResponse.error}</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -295,11 +323,11 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
) : null : null
|
) : null
|
||||||
}
|
) : null}
|
||||||
|
|
||||||
{
|
{response ? (
|
||||||
response ? response.error ? (
|
response.error ? (
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="error">{response.error}</Alert>
|
<Alert severity="error">{response.error}</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -307,10 +335,8 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="success">Exchange Session Added</Alert>
|
<Alert severity="success">Exchange Session Added</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
) : null : null
|
) : null
|
||||||
}
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,28 +31,30 @@ const useStyles = makeStyles((theme) => ({
|
||||||
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);
|
||||||
|
@ -62,27 +64,26 @@ export default function ConfigureDatabaseForm({onConfigured}) {
|
||||||
setConfigured(true);
|
setConfigured(true);
|
||||||
setTimeout(onConfigured, 3000);
|
setTimeout(onConfigured, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setTesting(false);
|
setTesting(false);
|
||||||
setTestResponse(err.response.data);
|
setTestResponse(err.response.data);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleTestConnection = (event) => {
|
const handleTestConnection = (event) => {
|
||||||
const dsn = getDSN()
|
const dsn = getDSN();
|
||||||
|
|
||||||
setTesting(true);
|
setTesting(true);
|
||||||
testDatabaseConnection({ driver, dsn }, (response) => {
|
testDatabaseConnection({ driver, dsn }, (response) => {
|
||||||
console.log(response)
|
console.log(response);
|
||||||
setTesting(false)
|
setTesting(false);
|
||||||
setTestResponse(response)
|
setTestResponse(response);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err);
|
||||||
setTesting(false)
|
setTesting(false);
|
||||||
setTestResponse(err.response.data)
|
setTestResponse(err.response.data);
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -92,79 +93,94 @@ export default function ConfigureDatabaseForm({onConfigured}) {
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<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 DSN
|
||||||
Please note this is optional, you CAN SKIP this step.
|
string in the following field. Please note this is optional, you CAN
|
||||||
|
SKIP this step.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<Box m={6}>
|
<Box m={6}>
|
||||||
<FormControl component="fieldset" required={true}>
|
<FormControl component="fieldset" required={true}>
|
||||||
<FormLabel component="legend">Database Driver</FormLabel>
|
<FormLabel component="legend">Database Driver</FormLabel>
|
||||||
<RadioGroup aria-label="driver" name="driver" value={driver} onChange={(event) => {
|
<RadioGroup
|
||||||
|
aria-label="driver"
|
||||||
|
name="driver"
|
||||||
|
value={driver}
|
||||||
|
onChange={(event) => {
|
||||||
setDriver(event.target.value);
|
setDriver(event.target.value);
|
||||||
}}>
|
}}
|
||||||
<FormControlLabel value="sqlite3" control={<Radio/>} label="Standard (Default)"/>
|
>
|
||||||
<FormControlLabel value="mysql" control={<Radio/>} label="MySQL"/>
|
<FormControlLabel
|
||||||
|
value="sqlite3"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Standard (Default)"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="mysql"
|
||||||
|
control={<Radio />}
|
||||||
|
label="MySQL"
|
||||||
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormHelperText>
|
<FormHelperText></FormHelperText>
|
||||||
</FormHelperText>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{driver === "mysql" ? (
|
{driver === 'mysql' ? (
|
||||||
<Grid item xs={12} sm={8}>
|
<Grid item xs={12} sm={8}>
|
||||||
<TextField id="mysql_url" name="mysql_url" label="MySQL Data Source Name"
|
<TextField
|
||||||
|
id="mysql_url"
|
||||||
|
name="mysql_url"
|
||||||
|
label="MySQL Data Source Name"
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
defaultValue={mysqlURL}
|
defaultValue={mysqlURL}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setMysqlURL(event.target.value)
|
setMysqlURL(event.target.value);
|
||||||
resetTestResponse()
|
resetTestResponse();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormHelperText>MySQL DSN</FormHelperText>
|
<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 like the
|
If you have database installed on your machine, you can enter the
|
||||||
following
|
DSN string like the following format:
|
||||||
format:
|
|
||||||
<br />
|
<br />
|
||||||
<pre><code>root:password@tcp(127.0.0.1:3306)/bbgo</code></pre>
|
<pre>
|
||||||
|
<code>root:password@tcp(127.0.0.1:3306)/bbgo</code>
|
||||||
|
</pre>
|
||||||
<br />
|
<br />
|
||||||
Be sure to create your database before using it. You need to execute the following statement
|
Be sure to create your database before using it. You need to
|
||||||
to
|
execute the following statement to create a database:
|
||||||
create a database:
|
|
||||||
<br />
|
<br />
|
||||||
<pre><code>CREATE DATABASE bbgo CHARSET utf8;</code></pre>
|
<pre>
|
||||||
|
<code>CREATE DATABASE bbgo CHARSET utf8;</code>
|
||||||
|
</pre>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
) : (
|
) : (
|
||||||
<Grid item xs={12} sm={8}>
|
<Grid item xs={12} sm={8}>
|
||||||
<Box m={6}>
|
<Box m={6}>
|
||||||
<Typography variant="body1" gutterBottom>
|
<Typography variant="body1" gutterBottom>
|
||||||
If you don't know what to choose, just pick the standard driver (sqlite3).
|
If you don't know what to choose, just pick the standard driver
|
||||||
|
(sqlite3).
|
||||||
<br />
|
<br />
|
||||||
For professionals, you can pick MySQL driver, BBGO works best with MySQL, especially for
|
For professionals, you can pick MySQL driver, BBGO works best
|
||||||
larger data scale.
|
with MySQL, especially for larger data scale.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<div className={classes.buttons}>
|
<div className={classes.buttons}>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleTestConnection}
|
onClick={handleTestConnection}
|
||||||
disabled={testing || configured}
|
disabled={testing || configured}
|
||||||
>
|
>
|
||||||
{testing ? "Testing" : "Test Connection"}
|
{testing ? 'Testing' : 'Test Connection'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -177,8 +193,8 @@ export default function ConfigureDatabaseForm({onConfigured}) {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{testResponse ? (
|
||||||
testResponse ? testResponse.error ? (
|
testResponse.error ? (
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="error">{testResponse.error}</Alert>
|
<Alert severity="error">{testResponse.error}</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -186,11 +202,8 @@ export default function ConfigureDatabaseForm({onConfigured}) {
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="success">Connection Test Succeeded</Alert>
|
<Alert severity="success">Connection Test Succeeded</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
) : null : null
|
) : null
|
||||||
}
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ 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,26 +24,26 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,15 +116,14 @@ const useStyles = makeStyles((theme) => ({
|
||||||
paddingBottom: theme.spacing(2),
|
paddingBottom: theme.spacing(2),
|
||||||
'& > *': {
|
'& > *': {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
export default function ConfigureGridStrategyForm({ onBack, onAdded }) {
|
export default function ConfigureGridStrategyForm({ onBack, onAdded }) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
const [errors, setErrors] = React.useState({})
|
const [errors, setErrors] = React.useState({});
|
||||||
|
|
||||||
const [sessions, setSessions] = React.useState([]);
|
const [sessions, setSessions] = React.useState([]);
|
||||||
|
|
||||||
|
@ -144,54 +147,53 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
querySessions((sessions) => {
|
querySessions((sessions) => {
|
||||||
setSessions(sessions)
|
setSessions(sessions);
|
||||||
});
|
});
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const handleAdd = (event) => {
|
const handleAdd = (event) => {
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
symbol: selectedSymbol,
|
symbol: selectedSymbol,
|
||||||
gridNumber: parseFloatValid(gridNumber),
|
gridNumber: parseFloatValid(gridNumber),
|
||||||
profitSpread: parseFloatValid(profitSpread),
|
profitSpread: parseFloatValid(profitSpread),
|
||||||
upperPrice: parseFloatValid(upperPrice),
|
upperPrice: parseFloatValid(upperPrice),
|
||||||
lowerPrice: parseFloatValid(lowerPrice),
|
lowerPrice: parseFloatValid(lowerPrice),
|
||||||
}
|
};
|
||||||
switch (quantityBy) {
|
switch (quantityBy) {
|
||||||
case "fixedQuantity":
|
case 'fixedQuantity':
|
||||||
payload.quantity = parseFloatValid(fixedQuantity);
|
payload.quantity = parseFloatValid(fixedQuantity);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "fixedAmount":
|
case 'fixedAmount':
|
||||||
payload.amount = parseFloatValid(fixedAmount);
|
payload.amount = parseFloatValid(fixedAmount);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!selectedSessionName) {
|
if (!selectedSessionName) {
|
||||||
setErrors({ session: true })
|
setErrors({ session: true });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedSymbol) {
|
if (!selectedSymbol) {
|
||||||
setErrors({ symbol: true })
|
setErrors({ symbol: true });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(payload)
|
console.log(payload);
|
||||||
attachStrategyOn(selectedSessionName, "grid", payload, (response) => {
|
attachStrategyOn(selectedSessionName, 'grid', payload, (response) => {
|
||||||
console.log(response)
|
console.log(response);
|
||||||
setResponse(response)
|
setResponse(response);
|
||||||
if (onAdded) {
|
if (onAdded) {
|
||||||
setTimeout(onAdded, 3000)
|
setTimeout(onAdded, 3000);
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
setResponse(err.response.data)
|
|
||||||
}).finally(() => {
|
|
||||||
setErrors({})
|
|
||||||
})
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
setResponse(err.response.data);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setErrors({});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQuantityBy = (event) => {
|
const handleQuantityBy = (event) => {
|
||||||
|
@ -200,14 +202,14 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
|
|
||||||
const handleSessionChange = (event) => {
|
const handleSessionChange = (event) => {
|
||||||
const sessionName = event.target.value;
|
const sessionName = event.target.value;
|
||||||
setSelectedSessionName(sessionName)
|
setSelectedSessionName(sessionName);
|
||||||
|
|
||||||
querySessionSymbols(sessionName, (symbols) => {
|
querySessionSymbols(sessionName, (symbols) => {
|
||||||
setActiveSessionSymbols(symbols);
|
setActiveSessionSymbols(symbols);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setResponse(err.response.data)
|
setResponse(err.response.data);
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const sessionMenuItems = sessions.map((session, index) => {
|
const sessionMenuItems = sessions.map((session, index) => {
|
||||||
|
@ -216,7 +218,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
{session.name}
|
{session.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
|
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
|
||||||
return (
|
return (
|
||||||
|
@ -224,7 +226,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
{symbol}
|
{symbol}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -233,15 +235,20 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography variant="body1" gutterBottom>
|
<Typography variant="body1" gutterBottom>
|
||||||
Fixed price band grid strategy uses the fixed price band to place buy/sell orders.
|
Fixed price band grid strategy uses the fixed price band to place
|
||||||
This strategy places sell orders above the current price, places buy orders below the current price.
|
buy/sell orders. This strategy places sell orders above the current
|
||||||
If any of the order is executed, then it will automatically place a new profit order on the reverse
|
price, places buy orders below the current price. If any of the order is
|
||||||
side.
|
executed, then it will automatically place a new profit order on the
|
||||||
|
reverse side.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormControl required className={classes.formControl} error={errors.session}>
|
<FormControl
|
||||||
|
required
|
||||||
|
className={classes.formControl}
|
||||||
|
error={errors.session}
|
||||||
|
>
|
||||||
<InputLabel id="session-select-label">Session</InputLabel>
|
<InputLabel id="session-select-label">Session</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="session-select-label"
|
labelId="session-select-label"
|
||||||
|
@ -258,7 +265,11 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormControl required className={classes.formControl} error={errors.symbol}>
|
<FormControl
|
||||||
|
required
|
||||||
|
className={classes.formControl}
|
||||||
|
error={errors.symbol}
|
||||||
|
>
|
||||||
<InputLabel id="symbol-select-label">Market</InputLabel>
|
<InputLabel id="symbol-select-label">Market</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="symbol-select-label"
|
labelId="symbol-select-label"
|
||||||
|
@ -266,7 +277,8 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
value={selectedSymbol ? selectedSymbol : ''}
|
value={selectedSymbol ? selectedSymbol : ''}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setSelectedSymbol(event.target.value);
|
setSelectedSymbol(event.target.value);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{symbolMenuItems}
|
{symbolMenuItems}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -283,7 +295,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setUpperPrice)
|
parseFloatCall(event.target.value, setUpperPrice);
|
||||||
}}
|
}}
|
||||||
value={upperPrice}
|
value={upperPrice}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -300,7 +312,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setLowerPrice)
|
parseFloatCall(event.target.value, setLowerPrice);
|
||||||
}}
|
}}
|
||||||
value={lowerPrice}
|
value={lowerPrice}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -317,7 +329,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setProfitSpread)
|
parseFloatCall(event.target.value, setProfitSpread);
|
||||||
}}
|
}}
|
||||||
value={profitSpread}
|
value={profitSpread}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -326,19 +338,30 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<Grid item xs={12} sm={3}>
|
<Grid item xs={12} sm={3}>
|
||||||
<FormControl component="fieldset">
|
<FormControl component="fieldset">
|
||||||
<FormLabel component="legend">Order Quantity By</FormLabel>
|
<FormLabel component="legend">Order Quantity By</FormLabel>
|
||||||
<RadioGroup name="quantityBy" value={quantityBy} onChange={handleQuantityBy}>
|
<RadioGroup
|
||||||
<FormControlLabel value="fixedAmount" control={<Radio/>} label="Fixed Amount"/>
|
name="quantityBy"
|
||||||
<FormControlLabel value="fixedQuantity" control={<Radio/>} label="Fixed Quantity"/>
|
value={quantityBy}
|
||||||
|
onChange={handleQuantityBy}
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
value="fixedAmount"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Fixed Amount"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="fixedQuantity"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Fixed Quantity"
|
||||||
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} sm={9}>
|
<Grid item xs={12} sm={9}>
|
||||||
{quantityBy === "fixedQuantity" ? (
|
{quantityBy === 'fixedQuantity' ? (
|
||||||
<TextField
|
<TextField
|
||||||
id="fixedQuantity"
|
id="fixedQuantity"
|
||||||
name="order_quantity"
|
name="order_quantity"
|
||||||
|
@ -346,7 +369,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setFixedQuantity)
|
parseFloatCall(event.target.value, setFixedQuantity);
|
||||||
}}
|
}}
|
||||||
value={fixedQuantity}
|
value={fixedQuantity}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -355,7 +378,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{quantityBy === "fixedAmount" ? (
|
{quantityBy === 'fixedAmount' ? (
|
||||||
<TextField
|
<TextField
|
||||||
id="orderAmount"
|
id="orderAmount"
|
||||||
name="order_amount"
|
name="order_amount"
|
||||||
|
@ -363,7 +386,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setFixedAmount)
|
parseFloatCall(event.target.value, setFixedAmount);
|
||||||
}}
|
}}
|
||||||
value={fixedAmount}
|
value={fixedAmount}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -381,7 +404,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
fullWidth
|
fullWidth
|
||||||
required
|
required
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
parseFloatCall(event.target.value, setGridNumber)
|
parseFloatCall(event.target.value, setGridNumber);
|
||||||
}}
|
}}
|
||||||
value={gridNumber}
|
value={gridNumber}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -397,21 +420,18 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
if (onBack) {
|
if (onBack) {
|
||||||
onBack();
|
onBack();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button variant="contained" color="primary" onClick={handleAdd}>
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={handleAdd}
|
|
||||||
>
|
|
||||||
Add Strategy
|
Add Strategy
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{response ? (
|
||||||
response ? response.error ? (
|
response.error ? (
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="error">{response.error}</Alert>
|
<Alert severity="error">{response.error}</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -419,10 +439,8 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="success">Strategy Added</Alert>
|
<Alert severity="success">Strategy Added</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
) : null : null
|
) : null
|
||||||
}
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
|
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
@ -9,13 +9,11 @@ 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: {
|
||||||
|
@ -25,20 +23,17 @@ const useStyles = makeStyles((theme) => ({
|
||||||
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 classes = useStyles();
|
||||||
|
|
||||||
const { activateBrowserWallet, account } = useEthers()
|
const { activateBrowserWallet, account } = useEthers();
|
||||||
const etherBalance = useEtherBalance(account)
|
const etherBalance = useEtherBalance(account);
|
||||||
const tokenBalance = useTokenBalance(BBG, account)
|
const tokenBalance = useTokenBalance(BBG, account);
|
||||||
|
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
const anchorRef = React.useRef(null);
|
const anchorRef = React.useRef(null);
|
||||||
|
@ -76,8 +71,8 @@ export default function ConnectWallet() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{account?
|
{account ? (
|
||||||
(<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
ref={anchorRef}
|
ref={anchorRef}
|
||||||
id="composition-button"
|
id="composition-button"
|
||||||
|
@ -113,19 +108,36 @@ export default function ConnectWallet() {
|
||||||
aria-labelledby="composition-button"
|
aria-labelledby="composition-button"
|
||||||
onKeyDown={handleListKeyDown}
|
onKeyDown={handleListKeyDown}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleClose}>{account && <p>Account: {account}</p>}</MenuItem>
|
<MenuItem onClick={handleClose}>
|
||||||
<MenuItem onClick={handleClose}>{etherBalance && <a>ETH Balance: {formatEther(etherBalance)}</a>}</MenuItem>
|
{account && <p>Account: {account}</p>}
|
||||||
<MenuItem onClick={handleClose}>{tokenBalance && <a>BBG Balance: {formatEther(tokenBalance)}</a>}</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleClose}>
|
||||||
|
{etherBalance && (
|
||||||
|
<a>ETH Balance: {formatEther(etherBalance)}</a>
|
||||||
|
)}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleClose}>
|
||||||
|
{tokenBalance && (
|
||||||
|
<a>BBG Balance: {formatEther(tokenBalance)}</a>
|
||||||
|
)}
|
||||||
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</ClickAwayListener>
|
</ClickAwayListener>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grow>
|
</Grow>
|
||||||
)}
|
)}
|
||||||
</Popper>
|
</Popper>
|
||||||
</>):(<div>
|
|
||||||
<button onClick={() => activateBrowserWallet()} className={classes.buttons}>Connect Wallet</button>
|
|
||||||
</div>)}
|
|
||||||
</>
|
</>
|
||||||
)
|
) : (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => activateBrowserWallet()}
|
||||||
|
className={classes.buttons}
|
||||||
|
>
|
||||||
|
Connect Wallet
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
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() {
|
||||||
|
@ -21,15 +21,16 @@ export default function ExchangeSessionTabPanel() {
|
||||||
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 (
|
||||||
|
<Paper className={classes.paper}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Typography variant="h4" gutterBottom>
|
||||||
Sessions
|
Sessions
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -39,11 +40,10 @@ export default function ExchangeSessionTabPanel() {
|
||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
textColor="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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ 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: {
|
||||||
|
@ -25,7 +25,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
paddingBottom: theme.spacing(2),
|
paddingBottom: theme.spacing(2),
|
||||||
'& > *': {
|
'& > *': {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -36,12 +36,12 @@ export default function ReviewSessions({onBack, onNext}) {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
querySessions((sessions) => {
|
querySessions((sessions) => {
|
||||||
setSessions(sessions)
|
setSessions(sessions);
|
||||||
});
|
});
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const items = sessions.map((session, i) => {
|
const items = sessions.map((session, i) => {
|
||||||
console.log(session)
|
console.log(session);
|
||||||
return (
|
return (
|
||||||
<ListItem key={session.name}>
|
<ListItem key={session.name}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
@ -50,7 +50,7 @@ export default function ReviewSessions({onBack, onNext}) {
|
||||||
<ListItemText primary={session.name} secondary={session.exchange} />
|
<ListItemText primary={session.name} secondary={session.exchange} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -58,9 +58,7 @@ export default function ReviewSessions({onBack, onNext}) {
|
||||||
Review Sessions
|
Review Sessions
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<List component="nav">
|
<List component="nav">{items}</List>
|
||||||
{items}
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<div className={classes.buttons}>
|
<div className={classes.buttons}>
|
||||||
<Button
|
<Button
|
||||||
|
@ -68,7 +66,8 @@ export default function ReviewSessions({onBack, onNext}) {
|
||||||
if (onBack) {
|
if (onBack) {
|
||||||
onBack();
|
onBack();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
@ -79,7 +78,8 @@ export default function ReviewSessions({onBack, onNext}) {
|
||||||
if (onNext) {
|
if (onNext) {
|
||||||
onNext();
|
onNext();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@ 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: {
|
||||||
|
@ -35,7 +35,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
paddingBottom: theme.spacing(2),
|
paddingBottom: theme.spacing(2),
|
||||||
'& > *': {
|
'& > *': {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ function configToTable(config) {
|
||||||
return {
|
return {
|
||||||
key: k,
|
key: k,
|
||||||
val: config[k],
|
val: config[k],
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
|
@ -78,31 +78,29 @@ export default function ReviewStrategies({onBack, onNext}) {
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<Card key={i} className={classes.strategyCard}>
|
<Card key={i} className={classes.strategyCard}>
|
||||||
<CardHeader
|
<CardHeader
|
||||||
avatar={
|
avatar={<Avatar aria-label="strategy">G</Avatar>}
|
||||||
<Avatar aria-label="strategy">G</Avatar>
|
|
||||||
}
|
|
||||||
action={
|
action={
|
||||||
<IconButton aria-label="settings">
|
<IconButton aria-label="settings">
|
||||||
<MoreVertIcon />
|
<MoreVertIcon />
|
||||||
|
@ -113,15 +111,15 @@ export default function ReviewStrategies({onBack, onNext}) {
|
||||||
/>
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="body2" color="textSecondary" component="p">
|
<Typography variant="body2" color="textSecondary" component="p">
|
||||||
Strategy will be executed on session {mounts.join(',')} with the following configuration:
|
Strategy will be executed on session {mounts.join(',')} with the
|
||||||
|
following configuration:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{configToTable(config)}
|
{configToTable(config)}
|
||||||
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -129,16 +127,16 @@ export default function ReviewStrategies({onBack, onNext}) {
|
||||||
Review Strategies
|
Review Strategies
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<List component="nav">
|
<List component="nav">{items}</List>
|
||||||
{items}
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<div className={classes.buttons}>
|
<div className={classes.buttons}>
|
||||||
<Button onClick={() => {
|
<Button
|
||||||
|
onClick={() => {
|
||||||
if (onBack) {
|
if (onBack) {
|
||||||
onBack()
|
onBack();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Add New Strategy
|
Add New Strategy
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
@ -149,7 +147,8 @@ export default function ReviewStrategies({onBack, onNext}) {
|
||||||
if (onNext) {
|
if (onNext) {
|
||||||
onNext();
|
onNext();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
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: {
|
||||||
|
@ -28,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
paddingBottom: theme.spacing(2),
|
paddingBottom: theme.spacing(2),
|
||||||
'& > *': {
|
'& > *': {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -43,17 +42,17 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
|
||||||
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) => {
|
||||||
|
@ -69,29 +68,29 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
|
||||||
</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
|
||||||
|
dotenv file <code>.env.local</code>.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<div className={classes.buttons}>
|
<div className={classes.buttons}>
|
||||||
<Button onClick={() => {
|
<Button
|
||||||
|
onClick={() => {
|
||||||
if (onBack) {
|
if (onBack) {
|
||||||
onBack()
|
onBack();
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button variant="contained" color="primary" onClick={handleRestart}>
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={handleRestart}>
|
|
||||||
Save and Restart
|
Save and Restart
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{response ? (
|
||||||
response ? response.error ? (
|
response.error ? (
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="error">{response.error}</Alert>
|
<Alert severity="error">{response.error}</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -99,9 +98,8 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
|
||||||
<Box m={2}>
|
<Box m={2}>
|
||||||
<Alert severity="success">Config Saved</Alert>
|
<Alert severity="success">Config Saved</Alert>
|
||||||
</Box>
|
</Box>
|
||||||
) : null : null
|
) : null
|
||||||
}
|
) : null}
|
||||||
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
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;
|
||||||
|
|
||||||
|
@ -46,23 +46,23 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
export default function SideBar() {
|
export default function SideBar() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
return <Drawer
|
return (
|
||||||
|
<Drawer
|
||||||
variant="permanent"
|
variant="permanent"
|
||||||
className={classes.drawer}
|
className={classes.drawer}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
className: classes.drawerPaper,
|
className: classes.drawerPaper,
|
||||||
}}
|
}}
|
||||||
anchor={"left"}
|
anchor={'left'}
|
||||||
open={true}>
|
open={true}
|
||||||
|
>
|
||||||
<div className={classes.appBarSpacer} />
|
<div className={classes.appBarSpacer} />
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<Link href={"/"}>
|
<Link href={'/'}>
|
||||||
<ListItem button>
|
<ListItem button>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DashboardIcon />
|
<DashboardIcon />
|
||||||
|
@ -73,7 +73,7 @@ export default function SideBar() {
|
||||||
</List>
|
</List>
|
||||||
<Divider />
|
<Divider />
|
||||||
<List>
|
<List>
|
||||||
<Link href={"/orders"}>
|
<Link href={'/orders'}>
|
||||||
<ListItem button>
|
<ListItem button>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<ListIcon />
|
<ListIcon />
|
||||||
|
@ -81,7 +81,7 @@ export default function SideBar() {
|
||||||
<ListItemText primary="Orders" />
|
<ListItemText primary="Orders" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={"/trades"}>
|
<Link href={'/trades'}>
|
||||||
<ListItem button>
|
<ListItem button>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<ListIcon />
|
<ListIcon />
|
||||||
|
@ -97,6 +97,5 @@ export default function SideBar() {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</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';
|
||||||
|
@ -12,28 +12,28 @@ 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();
|
||||||
|
@ -44,43 +44,44 @@ export default function TotalAssetsDetails({assets}) {
|
||||||
}
|
}
|
||||||
sortedAssets.sort((a, b) => {
|
sortedAssets.sort((a, b) => {
|
||||||
if (a.inUSD > b.inUSD) {
|
if (a.inUSD > b.inUSD) {
|
||||||
return -1
|
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) => {
|
const items = sortedAssets.map((a) => {
|
||||||
return (
|
return (
|
||||||
<ListItem key={a.currency} dense>
|
<ListItem key={a.currency} dense>
|
||||||
{
|
{a.currency in logoCurrencies ? (
|
||||||
(a.currency in logoCurrencies) ? (
|
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar alt={a.currency} src={`/images/${a.currency.toLowerCase()}-logo.svg`}/>
|
<Avatar
|
||||||
|
alt={a.currency}
|
||||||
|
src={`/images/${a.currency.toLowerCase()}-logo.svg`}
|
||||||
|
/>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
) : (
|
) : (
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar alt={a.currency} />
|
<Avatar alt={a.currency} />
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
)
|
)}
|
||||||
}
|
<ListItemText
|
||||||
<ListItemText primary={`${a.currency} ${a.total}`} secondary={`=~ ${Math.round(a.inUSD)} USD`}/>
|
primary={`${a.currency} ${a.total}`}
|
||||||
|
secondary={`=~ ${Math.round(a.inUSD)} USD`}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={classes.root} variant="outlined">
|
<Card className={classes.root} variant="outlined">
|
||||||
<CardContent className={classes.cardContent}>
|
<CardContent className={classes.cardContent}>
|
||||||
<List dense>
|
<List dense>{items}</List>
|
||||||
{items}
|
|
||||||
</List>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,33 +3,33 @@ 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) => ({
|
||||||
|
@ -38,7 +38,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
cardContent: {
|
cardContent: {
|
||||||
height: 350,
|
height: 350,
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function TotalAssetsPie({ assets }) {
|
export default function TotalAssetsPie({ assets }) {
|
||||||
|
@ -47,7 +47,7 @@ export default function TotalAssetsPie({ assets }) {
|
||||||
<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}
|
||||||
|
@ -81,15 +81,14 @@ export default function TotalAssetsPie({ assets }) {
|
||||||
{
|
{
|
||||||
on: 'hover',
|
on: 'hover',
|
||||||
style: {
|
style: {
|
||||||
itemTextColor: '#000'
|
itemTextColor: '#000',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
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) => ({
|
||||||
|
@ -31,13 +31,19 @@ const useStyles = makeStyles((theme) => ({
|
||||||
|
|
||||||
export default function TotalAssetSummary({ assets }) {
|
export default function TotalAssetSummary({ assets }) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return <Card className={classes.root} variant="outlined">
|
return (
|
||||||
|
<Card className={classes.root} variant="outlined">
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography className={classes.title} color="textSecondary" gutterBottom>
|
<Typography
|
||||||
|
className={classes.title}
|
||||||
|
color="textSecondary"
|
||||||
|
gutterBottom
|
||||||
|
>
|
||||||
Total Account Balance
|
Total Account Balance
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h5" component="h2">
|
<Typography variant="h5" component="h2">
|
||||||
{Math.round(aggregateAssetsBy(assets, "inBTC") * 1e8) / 1e8} <span>BTC</span>
|
{Math.round(aggregateAssetsBy(assets, 'inBTC') * 1e8) / 1e8}{' '}
|
||||||
|
<span>BTC</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography className={classes.pos} color="textSecondary">
|
<Typography className={classes.pos} color="textSecondary">
|
||||||
|
@ -45,8 +51,10 @@ export default function TotalAssetSummary({ assets }) {
|
||||||
</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}{' '}
|
||||||
|
<span>USD</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,48 @@
|
||||||
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
|
endTime = time;
|
||||||
|
|
||||||
const dateStr = toPeriodDateString(time, period)
|
const dateStr = toPeriodDateString(time, period);
|
||||||
const key = v[segment]
|
const key = v[segment];
|
||||||
|
|
||||||
keys[key] = true
|
keys[key] = true;
|
||||||
|
|
||||||
const k = key ? key : "total"
|
const k = key ? key : 'total';
|
||||||
const quoteVolume = Math.round(v.quoteVolume * 100) / 100
|
const quoteVolume = Math.round(v.quoteVolume * 100) / 100;
|
||||||
|
|
||||||
if (dateIndex[dateStr]) {
|
if (dateIndex[dateStr]) {
|
||||||
dateIndex[dateStr][k] = quoteVolume
|
dateIndex[dateStr][k] = quoteVolume;
|
||||||
} else {
|
} else {
|
||||||
dateIndex[dateStr] = {
|
dateIndex[dateStr] = {
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
|
@ -47,16 +50,16 @@ function groupData(rows, period, segment) {
|
||||||
month: time.getMonth() + 1,
|
month: time.getMonth() + 1,
|
||||||
day: time.getDate(),
|
day: time.getDate(),
|
||||||
[k]: quoteVolume,
|
[k]: quoteVolume,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
|
|
||||||
let data = []
|
let data = [];
|
||||||
while (startTime < endTime) {
|
while (startTime < endTime) {
|
||||||
const dateStr = toPeriodDateString(startTime, period)
|
const dateStr = toPeriodDateString(startTime, period);
|
||||||
const groupData = dateIndex[dateStr]
|
const groupData = dateIndex[dateStr];
|
||||||
if (groupData) {
|
if (groupData) {
|
||||||
data.push(groupData)
|
data.push(groupData);
|
||||||
} else {
|
} else {
|
||||||
data.push({
|
data.push({
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
|
@ -64,29 +67,29 @@ function groupData(rows, period, segment) {
|
||||||
month: startTime.getMonth() + 1,
|
month: startTime.getMonth() + 1,
|
||||||
day: startTime.getDate(),
|
day: startTime.getDate(),
|
||||||
total: 0,
|
total: 0,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (period) {
|
switch (period) {
|
||||||
case "day":
|
case 'day':
|
||||||
startTime.setDate(startTime.getDate() + 1)
|
startTime.setDate(startTime.getDate() + 1);
|
||||||
break
|
break;
|
||||||
case "month":
|
case 'month':
|
||||||
startTime.setMonth(startTime.getMonth() + 1)
|
startTime.setMonth(startTime.getMonth() + 1);
|
||||||
break
|
break;
|
||||||
case "year":
|
case 'year':
|
||||||
startTime.setFullYear(startTime.getFullYear() + 1)
|
startTime.setFullYear(startTime.getFullYear() + 1);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [data, Object.keys(keys)]
|
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) {
|
||||||
|
@ -97,16 +100,21 @@ export default function TradingVolumeBar(props) {
|
||||||
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 (
|
||||||
|
<ResponsiveBar
|
||||||
|
keys={keys}
|
||||||
data={data}
|
data={data}
|
||||||
indexBy={"date"}
|
indexBy={'date'}
|
||||||
margin={{ top: 50, right: 160, bottom: 100, left: 60 }}
|
margin={{ top: 50, right: 160, bottom: 100, left: 60 }}
|
||||||
padding={0.3}
|
padding={0.3}
|
||||||
valueScale={{ type: 'linear' }}
|
valueScale={{ type: 'linear' }}
|
||||||
|
@ -119,7 +127,7 @@ export default function TradingVolumeBar(props) {
|
||||||
tickRotation: -90,
|
tickRotation: -90,
|
||||||
legend: period,
|
legend: period,
|
||||||
legendPosition: 'middle',
|
legendPosition: 'middle',
|
||||||
legendOffset: 80
|
legendOffset: 80,
|
||||||
}}
|
}}
|
||||||
legends={[
|
legends={[
|
||||||
{
|
{
|
||||||
|
@ -139,14 +147,15 @@ export default function TradingVolumeBar(props) {
|
||||||
{
|
{
|
||||||
on: 'hover',
|
on: 'hover',
|
||||||
style: {
|
style: {
|
||||||
itemOpacity: 1
|
itemOpacity: 1,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
animate={true}
|
animate={true}
|
||||||
motionStiffness={90}
|
motionStiffness={90}
|
||||||
motionDamping={15}
|
motionDamping={15}
|
||||||
/>;
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
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: {
|
||||||
|
@ -15,12 +15,12 @@ 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 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);
|
||||||
|
@ -30,30 +30,35 @@ export default function TradingVolumePanel() {
|
||||||
setSegment(newValue);
|
setSegment(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Paper className={classes.paper}>
|
return (
|
||||||
|
<Paper className={classes.paper}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Typography variant="h4" gutterBottom>
|
||||||
Trading Volume
|
Trading Volume
|
||||||
</Typography>
|
</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
|
||||||
|
value={period}
|
||||||
onChange={handlePeriodChange}
|
onChange={handlePeriodChange}
|
||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
textColor="primary">
|
textColor="primary"
|
||||||
<Tab label="Day" value={"day"}/>
|
>
|
||||||
<Tab label="Month" value={"month"}/>
|
<Tab label="Day" value={'day'} />
|
||||||
<Tab label="Year" value={"year"}/>
|
<Tab label="Month" value={'month'} />
|
||||||
|
<Tab label="Year" value={'year'} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Grid container justifyContent={"flex-end"}>
|
<Grid container justifyContent={'flex-end'}>
|
||||||
<Tabs value={segment}
|
<Tabs
|
||||||
|
value={segment}
|
||||||
onChange={handleSegmentChange}
|
onChange={handleSegmentChange}
|
||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
textColor="primary">
|
textColor="primary"
|
||||||
<Tab label="By Exchange" value={"exchange"}/>
|
>
|
||||||
<Tab label="By Symbol" value={"symbol"}/>
|
<Tab label="By Exchange" value={'exchange'} />
|
||||||
|
<Tab label="By Symbol" value={'symbol'} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -62,5 +67,6 @@ export default function TradingVolumePanel() {
|
||||||
<Box className={classes.tradingVolumeBarBox}>
|
<Box className={classes.tradingVolumeBarBox}>
|
||||||
<TradingVolumeBar period={period} segment={segment} />
|
<TradingVolumeBar period={period} segment={segment} />
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>;
|
</Paper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
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';
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
container: {},
|
container: {},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function DashboardLayout({ children }) {
|
export default function DashboardLayout({ children }) {
|
||||||
|
@ -49,7 +49,11 @@ export default function DashboardLayout({children}) {
|
||||||
|
|
||||||
<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
|
||||||
|
className={classes.container}
|
||||||
|
maxWidth={false}
|
||||||
|
disableGutters={true}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</Container>
|
</Container>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
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) => ({
|
||||||
|
@ -24,20 +24,20 @@ const useStyles = makeStyles((theme) => ({
|
||||||
|
|
||||||
export default function PlainLayout(props) {
|
export default function PlainLayout(props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return <div className={classes.root}>
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
<AppBar className={classes.appBar}>
|
<AppBar className={classes.appBar}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography variant="h6" className={classes.title}>
|
<Typography variant="h6" className={classes.title}>
|
||||||
{ props && props.title ? props.title : "BBGO Setup Wizard" }
|
{props && props.title ? props.title : 'BBGO Setup Wizard'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
<div className={classes.appBarSpacer} />
|
<div className={classes.appBarSpacer} />
|
||||||
<Container>
|
<Container>{props.children}</Container>
|
||||||
{props.children}
|
|
||||||
</Container>
|
|
||||||
</main>
|
</main>
|
||||||
</div>;
|
</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;
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
|
@ -13,21 +13,21 @@ 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.
|
||||||
|
@ -38,13 +38,13 @@ export default function MyApp(props) {
|
||||||
|
|
||||||
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;
|
||||||
|
@ -55,39 +55,43 @@ export default function MyApp(props) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
syncStatusPoller = setInterval(pollSyncStatus, 1000)
|
syncStatusPoller = setInterval(pollSyncStatus, 1000);
|
||||||
} else {
|
} else {
|
||||||
// no session found, so we can not sync any data
|
// no session found, so we can not sync any data
|
||||||
setLoading(false)
|
setLoading(false);
|
||||||
setSyncing(false)
|
setSyncing(false);
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(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
|
||||||
|
name="viewport"
|
||||||
|
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||||
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
{
|
{loading ? (
|
||||||
loading ? (syncing ? (
|
syncing ? (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={syncing}
|
open={syncing}
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">{"Syncing Trades"}</DialogTitle>
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
{'Syncing Trades'}
|
||||||
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
The environment is syncing trades from the exchange sessions.
|
The environment is syncing trades from the exchange sessions.
|
||||||
|
@ -104,7 +108,7 @@ export default function MyApp(props) {
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">{"Loading"}</DialogTitle>
|
<DialogTitle id="alert-dialog-title">{'Loading'}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Loading...
|
Loading...
|
||||||
|
@ -114,10 +118,10 @@ export default function MyApp(props) {
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)) : (
|
|
||||||
<Component {...pageProps}/>
|
|
||||||
)
|
)
|
||||||
}
|
) : (
|
||||||
|
<Component {...pageProps} />
|
||||||
|
)}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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' });
|
||||||
}
|
};
|
||||||
|
|
|
@ -18,40 +18,38 @@ const useStyles = makeStyles((theme) => ({
|
||||||
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}
|
|
||||||
style={{flexGrow: 1}}
|
|
||||||
value={connectUrl}/>
|
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
</PlainLayout>
|
</PlainLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,10 @@ 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),
|
||||||
|
@ -34,35 +33,34 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
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) {
|
if (sessions.length == 0) {
|
||||||
return (
|
return (
|
||||||
|
@ -76,7 +74,7 @@ export default function Home() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("index: assets", assets)
|
console.log('index: assets', assets);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DAppProvider config={config}>
|
<DAppProvider config={config}>
|
||||||
|
@ -87,11 +85,13 @@ export default function Home() {
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<div className={classes.grid}>
|
<div className={classes.grid}>
|
||||||
<Grid container
|
<Grid
|
||||||
|
container
|
||||||
direction="row"
|
direction="row"
|
||||||
justifyContent="space-around"
|
justifyContent="space-around"
|
||||||
alignItems="flex-start"
|
alignItems="flex-start"
|
||||||
spacing={1}>
|
spacing={1}
|
||||||
|
>
|
||||||
<Grid item xs={12} md={8}>
|
<Grid item xs={12} md={8}>
|
||||||
<TotalAssetSummary assets={assets} />
|
<TotalAssetSummary assets={assets} />
|
||||||
<TotalAssetsPie assets={assets} />
|
<TotalAssetsPie assets={assets} />
|
||||||
|
@ -109,7 +109,5 @@ export default function Home() {
|
||||||
<ExchangeSessionTabPanel />
|
<ExchangeSessionTabPanel />
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
</DAppProvider>
|
</DAppProvider>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ 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 },
|
||||||
|
@ -15,9 +14,18 @@ const columns = [
|
||||||
{ 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: 'averagePrice',
|
||||||
|
headerName: 'Average Price',
|
||||||
|
type: 'number',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
|
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
|
||||||
{field: 'executedQuantity', headerName: 'Executed Quantity', type: 'number'},
|
{
|
||||||
|
field: 'executedQuantity',
|
||||||
|
headerName: 'Executed Quantity',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
{ field: 'status', headerName: 'Status' },
|
{ field: 'status', headerName: 'Status' },
|
||||||
{ field: 'isMargin', headerName: 'Margin' },
|
{ field: 'isMargin', headerName: 'Margin' },
|
||||||
{ field: 'isIsolated', headerName: 'Isolated' },
|
{ field: 'isIsolated', headerName: 'Isolated' },
|
||||||
|
@ -32,22 +40,24 @@ const useStyles = makeStyles((theme) => ({
|
||||||
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(
|
||||||
|
orders.map((o) => {
|
||||||
o.id = o.gid;
|
o.id = o.gid;
|
||||||
return o
|
return o;
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}, [])
|
);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
|
@ -69,4 +79,3 @@ export default function Orders() {
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ 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';
|
||||||
|
|
||||||
|
@ -23,52 +23,79 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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={() => {
|
||||||
|
setActiveStep(1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<AddExchangeSessionForm
|
<AddExchangeSessionForm
|
||||||
onBack={() => { setActiveStep(0) }}
|
onBack={() => {
|
||||||
onAdded={() => { setActiveStep(2) }}
|
setActiveStep(0);
|
||||||
|
}}
|
||||||
|
onAdded={() => {
|
||||||
|
setActiveStep(2);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<ReviewSessions
|
<ReviewSessions
|
||||||
onBack={() => { setActiveStep(1) }}
|
onBack={() => {
|
||||||
onNext={() => { setActiveStep(3) }}
|
setActiveStep(1);
|
||||||
|
}}
|
||||||
|
onNext={() => {
|
||||||
|
setActiveStep(3);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<ConfigureGridStrategyForm
|
<ConfigureGridStrategyForm
|
||||||
onBack={() => { setActiveStep(2) }}
|
onBack={() => {
|
||||||
onAdded={() => { setActiveStep(4) }}
|
setActiveStep(2);
|
||||||
|
}}
|
||||||
|
onAdded={() => {
|
||||||
|
setActiveStep(4);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return (
|
return (
|
||||||
<ReviewStrategies
|
<ReviewStrategies
|
||||||
onBack={() => { setActiveStep(3) }}
|
onBack={() => {
|
||||||
onNext={() => { setActiveStep(5) }}
|
setActiveStep(3);
|
||||||
|
}}
|
||||||
|
onNext={() => {
|
||||||
|
setActiveStep(5);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
return (
|
return (
|
||||||
<SaveConfigAndRestart
|
<SaveConfigAndRestart
|
||||||
onBack={() => { setActiveStep(4) }}
|
onBack={() => {
|
||||||
onRestarted={() => {
|
setActiveStep(4);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
onRestarted={() => {}}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown step');
|
throw new Error('Unknown step');
|
||||||
|
@ -103,4 +130,3 @@ export default function Setup() {
|
||||||
</PlainLayout>
|
</PlainLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,22 +27,24 @@ const useStyles = makeStyles((theme) => ({
|
||||||
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(
|
||||||
|
trades.map((o) => {
|
||||||
o.id = o.gid;
|
o.id = o.gid;
|
||||||
return o
|
return o;
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}, [])
|
);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
|
@ -64,4 +66,3 @@ export default function Trades() {
|
||||||
</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