Merge pull request #708 from c9s/refactor/format-js

format js code by prettier
This commit is contained in:
YC 2022-06-11 13:59:27 +08:00 committed by GitHub
commit c9e451791e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2394 additions and 2271 deletions

View File

@ -1,114 +1,121 @@
import axios from "axios";
import axios from 'axios';
const baseURL = process.env.NODE_ENV === "development" ? "http://localhost:8080" : ""
const baseURL =
process.env.NODE_ENV === 'development' ? 'http://localhost:8080' : '';
export function ping(cb) {
return axios.get(baseURL + '/api/ping').then(response => {
cb(response.data)
});
return axios.get(baseURL + '/api/ping').then((response) => {
cb(response.data);
});
}
export function queryOutboundIP(cb) {
return axios.get(baseURL + '/api/outbound-ip').then(response => {
cb(response.data.outboundIP)
});
return axios.get(baseURL + '/api/outbound-ip').then((response) => {
cb(response.data.outboundIP);
});
}
export function querySyncStatus(cb) {
return axios.get(baseURL + '/api/environment/syncing').then(response => {
cb(response.data.syncing)
});
return axios.get(baseURL + '/api/environment/syncing').then((response) => {
cb(response.data.syncing);
});
}
export function testDatabaseConnection(params, cb) {
return axios.post(baseURL + '/api/setup/test-db', params).then(response => {
cb(response.data)
});
return axios.post(baseURL + '/api/setup/test-db', params).then((response) => {
cb(response.data);
});
}
export function configureDatabase(params, cb) {
return axios.post(baseURL + '/api/setup/configure-db', params).then(response => {
cb(response.data)
return axios
.post(baseURL + '/api/setup/configure-db', params)
.then((response) => {
cb(response.data);
});
}
export function saveConfig(cb) {
return axios.post(baseURL + '/api/setup/save').then(response => {
cb(response.data)
});
return axios.post(baseURL + '/api/setup/save').then((response) => {
cb(response.data);
});
}
export function setupRestart(cb) {
return axios.post(baseURL + '/api/setup/restart').then(response => {
cb(response.data)
});
return axios.post(baseURL + '/api/setup/restart').then((response) => {
cb(response.data);
});
}
export function addSession(session, cb) {
return axios.post(baseURL + '/api/sessions', session).then(response => {
cb(response.data || [])
});
return axios.post(baseURL + '/api/sessions', session).then((response) => {
cb(response.data || []);
});
}
export function attachStrategyOn(session, strategyID, strategy, cb) {
return axios.post(baseURL + `/api/setup/strategy/single/${strategyID}/session/${session}`, strategy).then(response => {
cb(response.data)
return axios
.post(
baseURL + `/api/setup/strategy/single/${strategyID}/session/${session}`,
strategy
)
.then((response) => {
cb(response.data);
});
}
export function testSessionConnection(session, cb) {
return axios.post(baseURL + '/api/sessions/test', session).then(response => {
cb(response.data)
return axios
.post(baseURL + '/api/sessions/test', session)
.then((response) => {
cb(response.data);
});
}
export function queryStrategies(cb) {
return axios.get(baseURL + '/api/strategies/single').then(response => {
cb(response.data.strategies || [])
});
return axios.get(baseURL + '/api/strategies/single').then((response) => {
cb(response.data.strategies || []);
});
}
export function querySessions(cb) {
return axios.get(baseURL + '/api/sessions', {})
.then(response => {
cb(response.data.sessions || [])
});
return axios.get(baseURL + '/api/sessions', {}).then((response) => {
cb(response.data.sessions || []);
});
}
export function querySessionSymbols(sessionName, cb) {
return axios.get(baseURL + `/api/sessions/${sessionName}/symbols`, {})
.then(response => {
cb(response.data.symbols || [])
});
return axios
.get(baseURL + `/api/sessions/${sessionName}/symbols`, {})
.then((response) => {
cb(response.data.symbols || []);
});
}
export function queryTrades(params, cb) {
axios.get(baseURL + '/api/trades', {params: params})
.then(response => {
cb(response.data.trades || [])
});
axios.get(baseURL + '/api/trades', { params: params }).then((response) => {
cb(response.data.trades || []);
});
}
export function queryClosedOrders(params, cb) {
axios.get(baseURL + '/api/orders/closed', {params: params})
.then(response => {
cb(response.data.orders || [])
});
axios
.get(baseURL + '/api/orders/closed', { params: params })
.then((response) => {
cb(response.data.orders || []);
});
}
export function queryAssets(cb) {
axios.get(baseURL + '/api/assets', {})
.then(response => {
cb(response.data.assets || [])
});
axios.get(baseURL + '/api/assets', {}).then((response) => {
cb(response.data.assets || []);
});
}
export function queryTradingVolume(params, cb) {
axios.get(baseURL + '/api/trading-volume', {params: params})
.then(response => {
cb(response.data.tradingVolumes || [])
});
axios
.get(baseURL + '/api/trading-volume', { params: params })
.then((response) => {
cb(response.data.tradingVolumes || []);
});
}

View File

@ -20,297 +20,323 @@ import Alert from '@material-ui/lab/Alert';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import Visibility from '@material-ui/icons/Visibility';
import {addSession, testSessionConnection} from '../api/bbgo';
import { addSession, testSessionConnection } from '../api/bbgo';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
export default function AddExchangeSessionForm({onBack, onAdded}) {
const classes = useStyles();
const [exchangeType, setExchangeType] = React.useState('max');
const [customSessionName, setCustomSessionName] = React.useState(false);
const [sessionName, setSessionName] = React.useState(exchangeType);
export default function AddExchangeSessionForm({ onBack, onAdded }) {
const classes = useStyles();
const [exchangeType, setExchangeType] = React.useState('max');
const [customSessionName, setCustomSessionName] = React.useState(false);
const [sessionName, setSessionName] = React.useState(exchangeType);
const [testing, setTesting] = React.useState(false);
const [testResponse, setTestResponse] = React.useState(null);
const [response, setResponse] = React.useState(null);
const [testing, setTesting] = React.useState(false);
const [testResponse, setTestResponse] = React.useState(null);
const [response, setResponse] = React.useState(null);
const [apiKey, setApiKey] = React.useState('');
const [apiSecret, setApiSecret] = React.useState('');
const [apiKey, setApiKey] = React.useState('');
const [apiSecret, setApiSecret] = React.useState('');
const [showApiKey, setShowApiKey] = React.useState(false);
const [showApiSecret, setShowApiSecret] = React.useState(false);
const [showApiKey, setShowApiKey] = React.useState(false);
const [showApiSecret, setShowApiSecret] = React.useState(false);
const [isMargin, setIsMargin] = React.useState(false);
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState("");
const [isMargin, setIsMargin] = React.useState(false);
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState('');
const resetTestResponse = () => {
setTestResponse(null)
}
const resetTestResponse = () => {
setTestResponse(null);
};
const handleExchangeTypeChange = (event) => {
setExchangeType(event.target.value);
setSessionName(event.target.value);
resetTestResponse()
const handleExchangeTypeChange = (event) => {
setExchangeType(event.target.value);
setSessionName(event.target.value);
resetTestResponse();
};
const createSessionConfig = () => {
return {
name: sessionName,
exchange: exchangeType,
key: apiKey,
secret: apiSecret,
margin: isMargin,
envVarPrefix: exchangeType.toUpperCase(),
isolatedMargin: isIsolatedMargin,
isolatedMarginSymbol: isolatedMarginSymbol,
};
};
const createSessionConfig = () => {
return {
name: sessionName,
exchange: exchangeType,
key: apiKey,
secret: apiSecret,
margin: isMargin,
envVarPrefix: exchangeType.toUpperCase(),
isolatedMargin: isIsolatedMargin,
isolatedMarginSymbol: isolatedMarginSymbol,
}
}
const handleAdd = (event) => {
const payload = createSessionConfig();
addSession(payload, (response) => {
setResponse(response);
if (onAdded) {
setTimeout(onAdded, 3000);
}
}).catch((error) => {
console.error(error);
setResponse(error.response);
});
};
const handleAdd = (event) => {
const payload = createSessionConfig()
addSession(payload, (response) => {
setResponse(response)
if (onAdded) {
setTimeout(onAdded, 3000)
const handleTestConnection = (event) => {
const payload = createSessionConfig();
setTesting(true);
testSessionConnection(payload, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
}).catch((error) => {
console.error(error);
setTesting(false);
setTestResponse(error.response);
});
};
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Add Exchange Session
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl className={classes.formControl}>
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
<Select
labelId="exchange-type-select-label"
id="exchange-type-select"
value={exchangeType}
onChange={handleExchangeTypeChange}
>
<MenuItem value={'binance'}>Binance</MenuItem>
<MenuItem value={'max'}>Max</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="name"
name="name"
label="Session Name"
fullWidth
required
disabled={!customSessionName}
onChange={(event) => {
setSessionName(event.target.value);
}}
value={sessionName}
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormControlLabel
control={
<Checkbox
color="secondary"
name="custom_session_name"
onChange={(event) => {
setCustomSessionName(event.target.checked);
}}
value="1"
/>
}
}).catch((error) => {
console.error(error)
setResponse(error.response)
})
};
label="Custom exchange session name"
/>
<FormHelperText id="session-name-helper-text">
By default, the session name will be the exchange type name, e.g.{' '}
<code>binance</code> or <code>max</code>.<br />
If you're using multiple exchange sessions, you might need to custom
the session name. <br />
This is for advanced users.
</FormHelperText>
</Grid>
const handleTestConnection = (event) => {
const payload = createSessionConfig()
setTesting(true)
testSessionConnection(payload, (response) => {
console.log(response)
setTesting(false)
setTestResponse(response)
}).catch((error) => {
console.error(error)
setTesting(false)
setTestResponse(error.response)
})
};
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Add Exchange Session
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl className={classes.formControl}>
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
<Select
labelId="exchange-type-select-label"
id="exchange-type-select"
value={exchangeType}
onChange={handleExchangeTypeChange}
>
<MenuItem value={"binance"}>Binance</MenuItem>
<MenuItem value={"max"}>Max</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="name"
name="name"
label="Session Name"
fullWidth
required
disabled={!customSessionName}
onChange={(event) => {
setSessionName(event.target.value)
}}
value={sessionName}
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormControlLabel
control={<Checkbox color="secondary" name="custom_session_name"
onChange={(event) => {
setCustomSessionName(event.target.checked);
}} value="1"/>}
label="Custom exchange session name"
/>
<FormHelperText id="session-name-helper-text">
By default, the session name will be the exchange type name,
e.g. <code>binance</code> or <code>max</code>.<br/>
If you're using multiple exchange sessions, you might need to custom the session name. <br/>
This is for advanced users.
</FormHelperText>
</Grid>
<Grid item xs={12}>
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="apiKey">API Key</InputLabel>
<FilledInput
id="apiKey"
type={showApiKey ? 'text' : 'password'}
value={apiKey}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => { setShowApiKey(!showApiKey) }}
onMouseDown={(event) => { event.preventDefault() }}
edge="end"
>
{showApiKey ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
onChange={(event) => {
setApiKey(event.target.value)
resetTestResponse()
}}
/>
</FormControl>
</Grid>
<Grid item xs={12}>
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
<FilledInput
id="apiSecret"
type={showApiSecret ? 'text' : 'password'}
value={apiSecret}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => { setShowApiSecret(!showApiSecret) }}
onMouseDown={(event) => { event.preventDefault() }}
edge="end"
>
{showApiSecret ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
onChange={(event) => {
setApiSecret(event.target.value)
resetTestResponse()
}}
/>
</FormControl>
</Grid>
{exchangeType === "binance" ? (
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox color="secondary" name="isMargin" onChange={(event) => {
setIsMargin(event.target.checked);
resetTestResponse();
}} value="1"/>}
label="Use margin trading."
/>
<FormHelperText id="isMargin-helper-text">This is only available for Binance. Please use the
leverage at your own risk.</FormHelperText>
<FormControlLabel
control={<Checkbox color="secondary" name="isIsolatedMargin"
onChange={(event) => {
setIsIsolatedMargin(event.target.checked);
resetTestResponse()
}} value="1"/>}
label="Use isolated margin trading."
/>
<FormHelperText id="isIsolatedMargin-helper-text">This is only available for Binance. If this is
set, you can only trade one symbol with one session.</FormHelperText>
{isIsolatedMargin ?
<TextField
id="isolatedMarginSymbol"
name="isolatedMarginSymbol"
label="Isolated Margin Symbol"
onChange={(event) => {
setIsolatedMarginSymbol(event.target.value);
resetTestResponse()
}}
fullWidth
required
/>
: null}
</Grid>
) : null}
</Grid>
<div className={classes.buttons}>
<Button
<Grid item xs={12}>
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="apiKey">API Key</InputLabel>
<FilledInput
id="apiKey"
type={showApiKey ? 'text' : 'password'}
value={apiKey}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => {
if (onBack) {
onBack();
}
}}>
Back
</Button>
setShowApiKey(!showApiKey);
}}
onMouseDown={(event) => {
event.preventDefault();
}}
edge="end"
>
{showApiKey ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
onChange={(event) => {
setApiKey(event.target.value);
resetTestResponse();
}}
/>
</FormControl>
</Grid>
<Button
color="primary"
onClick={handleTestConnection}
disabled={testing}>
{testing ? "Testing" : "Test Connection"}
</Button>
<Grid item xs={12}>
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
<FilledInput
id="apiSecret"
type={showApiSecret ? 'text' : 'password'}
value={apiSecret}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => {
setShowApiSecret(!showApiSecret);
}}
onMouseDown={(event) => {
event.preventDefault();
}}
edge="end"
>
{showApiSecret ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
onChange={(event) => {
setApiSecret(event.target.value);
resetTestResponse();
}}
/>
</FormControl>
</Grid>
<Button
variant="contained"
color="primary"
onClick={handleAdd}
>
Add
</Button>
</div>
{exchangeType === 'binance' ? (
<Grid item xs={12}>
<FormControlLabel
control={
<Checkbox
color="secondary"
name="isMargin"
onChange={(event) => {
setIsMargin(event.target.checked);
resetTestResponse();
}}
value="1"
/>
}
label="Use margin trading."
/>
<FormHelperText id="isMargin-helper-text">
This is only available for Binance. Please use the leverage at
your own risk.
</FormHelperText>
{
testResponse ? testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
) : testResponse.success ? (
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null : null
<FormControlLabel
control={
<Checkbox
color="secondary"
name="isIsolatedMargin"
onChange={(event) => {
setIsIsolatedMargin(event.target.checked);
resetTestResponse();
}}
value="1"
/>
}
label="Use isolated margin trading."
/>
<FormHelperText id="isIsolatedMargin-helper-text">
This is only available for Binance. If this is set, you can only
trade one symbol with one session.
</FormHelperText>
{isIsolatedMargin ? (
<TextField
id="isolatedMarginSymbol"
name="isolatedMarginSymbol"
label="Isolated Margin Symbol"
onChange={(event) => {
setIsolatedMarginSymbol(event.target.value);
resetTestResponse();
}}
fullWidth
required
/>
) : null}
</Grid>
) : null}
</Grid>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}
>
Back
</Button>
{
response ? response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Exchange Session Added</Alert>
</Box>
) : null : null
}
<Button
color="primary"
onClick={handleTestConnection}
disabled={testing}
>
{testing ? 'Testing' : 'Test Connection'}
</Button>
<Button variant="contained" color="primary" onClick={handleAdd}>
Add
</Button>
</div>
</React.Fragment>
);
{testResponse ? (
testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
) : testResponse.success ? (
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null
) : null}
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Exchange Session Added</Alert>
</Box>
) : null
) : null}
</React.Fragment>
);
}

View File

@ -13,184 +13,197 @@ import FormLabel from '@material-ui/core/FormLabel';
import Alert from '@material-ui/lab/Alert';
import {configureDatabase, testDatabaseConnection} from '../api/bbgo';
import { configureDatabase, testDatabaseConnection } from '../api/bbgo';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
export default function ConfigureDatabaseForm({onConfigured}) {
const classes = useStyles();
export default function ConfigureDatabaseForm({ onConfigured }) {
const classes = useStyles();
const [mysqlURL, setMysqlURL] = React.useState("root@tcp(127.0.0.1:3306)/bbgo")
const [mysqlURL, setMysqlURL] = React.useState(
'root@tcp(127.0.0.1:3306)/bbgo'
);
const [driver, setDriver] = React.useState("sqlite3");
const [testing, setTesting] = React.useState(false);
const [testResponse, setTestResponse] = React.useState(null);
const [configured, setConfigured] = React.useState(false);
const [driver, setDriver] = React.useState('sqlite3');
const [testing, setTesting] = React.useState(false);
const [testResponse, setTestResponse] = React.useState(null);
const [configured, setConfigured] = React.useState(false);
const getDSN = () => driver === "sqlite3" ? "file:bbgo.sqlite3" : mysqlURL
const getDSN = () => (driver === 'sqlite3' ? 'file:bbgo.sqlite3' : mysqlURL);
const resetTestResponse = () => {
setTestResponse(null)
}
const resetTestResponse = () => {
setTestResponse(null);
};
const handleConfigureDatabase = (event) => {
const dsn = getDSN()
const handleConfigureDatabase = (event) => {
const dsn = getDSN();
configureDatabase({driver, dsn}, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
if (onConfigured) {
setConfigured(true);
setTimeout(onConfigured, 3000);
}
configureDatabase({ driver, dsn }, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
if (onConfigured) {
setConfigured(true);
setTimeout(onConfigured, 3000);
}
}).catch((err) => {
console.error(err);
setTesting(false);
setTestResponse(err.response.data);
});
};
}).catch((err) => {
console.error(err);
setTesting(false);
setTestResponse(err.response.data);
})
}
const handleTestConnection = (event) => {
const dsn = getDSN();
const handleTestConnection = (event) => {
const dsn = getDSN()
setTesting(true);
testDatabaseConnection({ driver, dsn }, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
}).catch((err) => {
console.error(err);
setTesting(false);
setTestResponse(err.response.data);
});
};
setTesting(true);
testDatabaseConnection({driver, dsn}, (response) => {
console.log(response)
setTesting(false)
setTestResponse(response)
}).catch((err) => {
console.error(err)
setTesting(false)
setTestResponse(err.response.data)
})
};
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Configure Database
</Typography>
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Configure Database
</Typography>
<Typography variant="body1" gutterBottom>
If you have database installed on your machine, you can enter the DSN
string in the following field. Please note this is optional, you CAN
SKIP this step.
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={4}>
<Box m={6}>
<FormControl component="fieldset" required={true}>
<FormLabel component="legend">Database Driver</FormLabel>
<RadioGroup
aria-label="driver"
name="driver"
value={driver}
onChange={(event) => {
setDriver(event.target.value);
}}
>
<FormControlLabel
value="sqlite3"
control={<Radio />}
label="Standard (Default)"
/>
<FormControlLabel
value="mysql"
control={<Radio />}
label="MySQL"
/>
</RadioGroup>
</FormControl>
<FormHelperText></FormHelperText>
</Box>
</Grid>
{driver === 'mysql' ? (
<Grid item xs={12} sm={8}>
<TextField
id="mysql_url"
name="mysql_url"
label="MySQL Data Source Name"
fullWidth
required
defaultValue={mysqlURL}
onChange={(event) => {
setMysqlURL(event.target.value);
resetTestResponse();
}}
/>
<FormHelperText>MySQL DSN</FormHelperText>
<Typography variant="body1" gutterBottom>
If you have database installed on your machine, you can enter the DSN string in the following field.
Please note this is optional, you CAN SKIP this step.
If you have database installed on your machine, you can enter the
DSN string like the following format:
<br />
<pre>
<code>root:password@tcp(127.0.0.1:3306)/bbgo</code>
</pre>
<br />
Be sure to create your database before using it. You need to
execute the following statement to create a database:
<br />
<pre>
<code>CREATE DATABASE bbgo CHARSET utf8;</code>
</pre>
</Typography>
</Grid>
) : (
<Grid item xs={12} sm={8}>
<Box m={6}>
<Typography variant="body1" gutterBottom>
If you don't know what to choose, just pick the standard driver
(sqlite3).
<br />
For professionals, you can pick MySQL driver, BBGO works best
with MySQL, especially for larger data scale.
</Typography>
</Box>
</Grid>
)}
</Grid>
<div className={classes.buttons}>
<Button
color="primary"
onClick={handleTestConnection}
disabled={testing || configured}
>
{testing ? 'Testing' : 'Test Connection'}
</Button>
<Grid container spacing={3}>
<Grid item xs={12} sm={4}>
<Box m={6}>
<FormControl component="fieldset" required={true}>
<FormLabel component="legend">Database Driver</FormLabel>
<RadioGroup aria-label="driver" name="driver" value={driver} onChange={(event) => {
setDriver(event.target.value);
}}>
<FormControlLabel value="sqlite3" control={<Radio/>} label="Standard (Default)"/>
<FormControlLabel value="mysql" control={<Radio/>} label="MySQL"/>
</RadioGroup>
</FormControl>
<FormHelperText>
</FormHelperText>
</Box>
</Grid>
{driver === "mysql" ? (
<Grid item xs={12} sm={8}>
<TextField id="mysql_url" name="mysql_url" label="MySQL Data Source Name"
fullWidth
required
defaultValue={mysqlURL}
onChange={(event) => {
setMysqlURL(event.target.value)
resetTestResponse()
}}
/>
<FormHelperText>MySQL DSN</FormHelperText>
<Typography variant="body1" gutterBottom>
If you have database installed on your machine, you can enter the DSN string like the
following
format:
<br/>
<pre><code>root:password@tcp(127.0.0.1:3306)/bbgo</code></pre>
<br/>
Be sure to create your database before using it. You need to execute the following statement
to
create a database:
<br/>
<pre><code>CREATE DATABASE bbgo CHARSET utf8;</code></pre>
</Typography>
</Grid>
) : (
<Grid item xs={12} sm={8}>
<Box m={6}>
<Typography variant="body1" gutterBottom>
If you don't know what to choose, just pick the standard driver (sqlite3).
<br/>
For professionals, you can pick MySQL driver, BBGO works best with MySQL, especially for
larger data scale.
</Typography>
</Box>
</Grid>
)}
</Grid>
<div className={classes.buttons}>
<Button
color="primary"
onClick={handleTestConnection}
disabled={testing || configured}
>
{testing ? "Testing" : "Test Connection"}
</Button>
<Button
variant="contained"
color="primary"
disabled={testing || configured}
onClick={handleConfigureDatabase}
>
Configure
</Button>
</div>
{
testResponse ? testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
) : testResponse.success ? (
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null : null
}
</React.Fragment>
);
<Button
variant="contained"
color="primary"
disabled={testing || configured}
onClick={handleConfigureDatabase}
>
Configure
</Button>
</div>
{testResponse ? (
testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
) : testResponse.success ? (
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null
) : null}
</React.Fragment>
);
}

View File

@ -5,8 +5,12 @@ import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import {attachStrategyOn, querySessions, querySessionSymbols} from "../api/bbgo";
import { makeStyles } from '@material-ui/core/styles';
import {
attachStrategyOn,
querySessions,
querySessionSymbols,
} from '../api/bbgo';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
@ -20,409 +24,423 @@ import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import Alert from '@material-ui/lab/Alert';
import Box from "@material-ui/core/Box";
import Box from '@material-ui/core/Box';
import NumberFormat from 'react-number-format';
function parseFloatValid(s) {
if (s) {
const f = parseFloat(s)
if (!isNaN(f)) {
return f
}
if (s) {
const f = parseFloat(s);
if (!isNaN(f)) {
return f;
}
}
return null
return null;
}
function parseFloatCall(s, cb) {
if (s) {
const f = parseFloat(s)
if (!isNaN(f)) {
cb(f)
}
if (s) {
const f = parseFloat(s);
if (!isNaN(f)) {
cb(f);
}
}
}
function StandardNumberFormat(props) {
const {inputRef, onChange, ...other} = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
/>
);
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
/>
);
}
StandardNumberFormat.propTypes = {
inputRef: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
inputRef: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
};
function PriceNumberFormat(props) {
const {inputRef, onChange, ...other} = props;
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
prefix="$"
/>
);
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
prefix="$"
/>
);
}
PriceNumberFormat.propTypes = {
inputRef: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
inputRef: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
};
const useStyles = makeStyles((theme) => ({
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
export default function ConfigureGridStrategyForm({ onBack, onAdded }) {
const classes = useStyles();
export default function ConfigureGridStrategyForm({onBack, onAdded}) {
const classes = useStyles();
const [errors, setErrors] = React.useState({});
const [errors, setErrors] = React.useState({})
const [sessions, setSessions] = React.useState([]);
const [sessions, setSessions] = React.useState([]);
const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]);
const [activeSessionSymbols, setActiveSessionSymbols] = React.useState([]);
const [selectedSessionName, setSelectedSessionName] = React.useState(null);
const [selectedSessionName, setSelectedSessionName] = React.useState(null);
const [selectedSymbol, setSelectedSymbol] = React.useState('');
const [selectedSymbol, setSelectedSymbol] = React.useState('');
const [quantityBy, setQuantityBy] = React.useState('fixedAmount');
const [quantityBy, setQuantityBy] = React.useState('fixedAmount');
const [upperPrice, setUpperPrice] = React.useState(30000.0);
const [lowerPrice, setLowerPrice] = React.useState(10000.0);
const [upperPrice, setUpperPrice] = React.useState(30000.0);
const [lowerPrice, setLowerPrice] = React.useState(10000.0);
const [fixedAmount, setFixedAmount] = React.useState(100.0);
const [fixedQuantity, setFixedQuantity] = React.useState(1.234);
const [gridNumber, setGridNumber] = React.useState(20);
const [profitSpread, setProfitSpread] = React.useState(100.0);
const [fixedAmount, setFixedAmount] = React.useState(100.0);
const [fixedQuantity, setFixedQuantity] = React.useState(1.234);
const [gridNumber, setGridNumber] = React.useState(20);
const [profitSpread, setProfitSpread] = React.useState(100.0);
const [response, setResponse] = React.useState({});
const [response, setResponse] = React.useState({});
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions);
});
}, []);
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
});
}, [])
const handleAdd = (event) => {
const payload = {
symbol: selectedSymbol,
gridNumber: parseFloatValid(gridNumber),
profitSpread: parseFloatValid(profitSpread),
upperPrice: parseFloatValid(upperPrice),
lowerPrice: parseFloatValid(lowerPrice),
}
switch (quantityBy) {
case "fixedQuantity":
payload.quantity = parseFloatValid(fixedQuantity);
break;
case "fixedAmount":
payload.amount = parseFloatValid(fixedAmount);
break;
}
if (!selectedSessionName) {
setErrors({ session: true })
return
}
if (!selectedSymbol) {
setErrors({ symbol: true })
return
}
console.log(payload)
attachStrategyOn(selectedSessionName, "grid", payload, (response) => {
console.log(response)
setResponse(response)
if (onAdded) {
setTimeout(onAdded, 3000)
}
}).catch((err) => {
console.error(err);
setResponse(err.response.data)
}).finally(() => {
setErrors({})
})
const handleAdd = (event) => {
const payload = {
symbol: selectedSymbol,
gridNumber: parseFloatValid(gridNumber),
profitSpread: parseFloatValid(profitSpread),
upperPrice: parseFloatValid(upperPrice),
lowerPrice: parseFloatValid(lowerPrice),
};
switch (quantityBy) {
case 'fixedQuantity':
payload.quantity = parseFloatValid(fixedQuantity);
break;
const handleQuantityBy = (event) => {
setQuantityBy(event.target.value);
};
case 'fixedAmount':
payload.amount = parseFloatValid(fixedAmount);
break;
}
const handleSessionChange = (event) => {
const sessionName = event.target.value;
setSelectedSessionName(sessionName)
if (!selectedSessionName) {
setErrors({ session: true });
return;
}
querySessionSymbols(sessionName, (symbols) => {
setActiveSessionSymbols(symbols);
}).catch((err) => {
console.error(err);
setResponse(err.response.data)
})
};
if (!selectedSymbol) {
setErrors({ symbol: true });
return;
}
const sessionMenuItems = sessions.map((session, index) => {
return (
<MenuItem key={session.name} value={session.name}>
{session.name}
</MenuItem>
);
console.log(payload);
attachStrategyOn(selectedSessionName, 'grid', payload, (response) => {
console.log(response);
setResponse(response);
if (onAdded) {
setTimeout(onAdded, 3000);
}
})
.catch((err) => {
console.error(err);
setResponse(err.response.data);
})
.finally(() => {
setErrors({});
});
};
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
return (
<MenuItem key={symbol} value={symbol}>
{symbol}
</MenuItem>
);
})
const handleQuantityBy = (event) => {
setQuantityBy(event.target.value);
};
const handleSessionChange = (event) => {
const sessionName = event.target.value;
setSelectedSessionName(sessionName);
querySessionSymbols(sessionName, (symbols) => {
setActiveSessionSymbols(symbols);
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
});
};
const sessionMenuItems = sessions.map((session, index) => {
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Add Grid Strategy
</Typography>
<Typography variant="body1" gutterBottom>
Fixed price band grid strategy uses the fixed price band to place buy/sell orders.
This strategy places sell orders above the current price, places buy orders below the current price.
If any of the order is executed, then it will automatically place a new profit order on the reverse
side.
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl required className={classes.formControl} error={errors.session}>
<InputLabel id="session-select-label">Session</InputLabel>
<Select
labelId="session-select-label"
id="session-select"
value={selectedSessionName ? selectedSessionName : ''}
onChange={handleSessionChange}
>
{sessionMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the exchange session you want to mount this strategy.
</FormHelperText>
</Grid>
<Grid item xs={12}>
<FormControl required className={classes.formControl} error={errors.symbol}>
<InputLabel id="symbol-select-label">Market</InputLabel>
<Select
labelId="symbol-select-label"
id="symbol-select"
value={selectedSymbol ? selectedSymbol : ''}
onChange={(event) => {
setSelectedSymbol(event.target.value);
}}>
{symbolMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the market you want to run this strategy
</FormHelperText>
</Grid>
<Grid item xs={12}>
<TextField
id="upperPrice"
name="upper_price"
label="Upper Price"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setUpperPrice)
}}
value={upperPrice}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="lowerPrice"
name="lower_price"
label="Lower Price"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setLowerPrice)
}}
value={lowerPrice}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="profitSpread"
name="profit_spread"
label="Profit Spread"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setProfitSpread)
}}
value={profitSpread}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
</Grid>
<Grid item xs={12} sm={3}>
<FormControl component="fieldset">
<FormLabel component="legend">Order Quantity By</FormLabel>
<RadioGroup name="quantityBy" value={quantityBy} onChange={handleQuantityBy}>
<FormControlLabel value="fixedAmount" control={<Radio/>} label="Fixed Amount"/>
<FormControlLabel value="fixedQuantity" control={<Radio/>} label="Fixed Quantity"/>
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} sm={9}>
{quantityBy === "fixedQuantity" ? (
<TextField
id="fixedQuantity"
name="order_quantity"
label="Fixed Quantity"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedQuantity)
}}
value={fixedQuantity}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
) : null}
{quantityBy === "fixedAmount" ? (
<TextField
id="orderAmount"
name="order_amount"
label="Fixed Amount"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedAmount)
}}
value={fixedAmount}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
) : null}
</Grid>
<Grid item xs={12}>
<TextField
id="gridNumber"
name="grid_number"
label="Number of Grid"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setGridNumber)
}}
value={gridNumber}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
</Grid>
</Grid>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleAdd}
>
Add Strategy
</Button>
</div>
{
response ? response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Strategy Added</Alert>
</Box>
) : null : null
}
</React.Fragment>
<MenuItem key={session.name} value={session.name}>
{session.name}
</MenuItem>
);
});
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
return (
<MenuItem key={symbol} value={symbol}>
{symbol}
</MenuItem>
);
});
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Add Grid Strategy
</Typography>
<Typography variant="body1" gutterBottom>
Fixed price band grid strategy uses the fixed price band to place
buy/sell orders. This strategy places sell orders above the current
price, places buy orders below the current price. If any of the order is
executed, then it will automatically place a new profit order on the
reverse side.
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl
required
className={classes.formControl}
error={errors.session}
>
<InputLabel id="session-select-label">Session</InputLabel>
<Select
labelId="session-select-label"
id="session-select"
value={selectedSessionName ? selectedSessionName : ''}
onChange={handleSessionChange}
>
{sessionMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the exchange session you want to mount this strategy.
</FormHelperText>
</Grid>
<Grid item xs={12}>
<FormControl
required
className={classes.formControl}
error={errors.symbol}
>
<InputLabel id="symbol-select-label">Market</InputLabel>
<Select
labelId="symbol-select-label"
id="symbol-select"
value={selectedSymbol ? selectedSymbol : ''}
onChange={(event) => {
setSelectedSymbol(event.target.value);
}}
>
{symbolMenuItems}
</Select>
</FormControl>
<FormHelperText id="session-select-helper-text">
Select the market you want to run this strategy
</FormHelperText>
</Grid>
<Grid item xs={12}>
<TextField
id="upperPrice"
name="upper_price"
label="Upper Price"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setUpperPrice);
}}
value={upperPrice}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="lowerPrice"
name="lower_price"
label="Lower Price"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setLowerPrice);
}}
value={lowerPrice}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="profitSpread"
name="profit_spread"
label="Profit Spread"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setProfitSpread);
}}
value={profitSpread}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
</Grid>
<Grid item xs={12} sm={3}>
<FormControl component="fieldset">
<FormLabel component="legend">Order Quantity By</FormLabel>
<RadioGroup
name="quantityBy"
value={quantityBy}
onChange={handleQuantityBy}
>
<FormControlLabel
value="fixedAmount"
control={<Radio />}
label="Fixed Amount"
/>
<FormControlLabel
value="fixedQuantity"
control={<Radio />}
label="Fixed Quantity"
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} sm={9}>
{quantityBy === 'fixedQuantity' ? (
<TextField
id="fixedQuantity"
name="order_quantity"
label="Fixed Quantity"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedQuantity);
}}
value={fixedQuantity}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
) : null}
{quantityBy === 'fixedAmount' ? (
<TextField
id="orderAmount"
name="order_amount"
label="Fixed Amount"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedAmount);
}}
value={fixedAmount}
InputProps={{
inputComponent: PriceNumberFormat,
}}
/>
) : null}
</Grid>
<Grid item xs={12}>
<TextField
id="gridNumber"
name="grid_number"
label="Number of Grid"
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setGridNumber);
}}
value={gridNumber}
InputProps={{
inputComponent: StandardNumberFormat,
}}
/>
</Grid>
</Grid>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}
>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleAdd}>
Add Strategy
</Button>
</div>
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Strategy Added</Alert>
</Box>
) : null
) : null}
</React.Fragment>
);
}

View File

@ -1,6 +1,6 @@
import React from "react";
import React from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
@ -9,123 +9,135 @@ import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import ListItemText from "@material-ui/core/ListItemText";
import PersonIcon from "@material-ui/icons/Person";
import { useEtherBalance, useTokenBalance, useEthers } from '@usedapp/core'
import { formatEther } from '@ethersproject/units'
import ListItemText from '@material-ui/core/ListItemText';
import PersonIcon from '@material-ui/icons/Person';
import { useEtherBalance, useTokenBalance, useEthers } from '@usedapp/core';
import { formatEther } from '@ethersproject/units';
const useStyles = makeStyles((theme) => ({
buttons: {
margin: theme.spacing(1),
padding: theme.spacing(1),
},
profile: {
margin: theme.spacing(1),
padding: theme.spacing(1),
}
buttons: {
margin: theme.spacing(1),
padding: theme.spacing(1),
},
profile: {
margin: theme.spacing(1),
padding: theme.spacing(1),
},
}));
const BBG = '0x3Afe98235d680e8d7A52e1458a59D60f45F935C0'
const BBG = '0x3Afe98235d680e8d7A52e1458a59D60f45F935C0';
export default function ConnectWallet() {
const classes = useStyles();
const { activateBrowserWallet, account } = useEthers();
const etherBalance = useEtherBalance(account);
const tokenBalance = useTokenBalance(BBG, account);
const classes = useStyles();
const { activateBrowserWallet, account } = useEthers()
const etherBalance = useEtherBalance(account)
const tokenBalance = useTokenBalance(BBG, account)
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
function handleListKeyDown(event) {
if (event.key === 'Tab') {
event.preventDefault();
setOpen(false);
} else if (event.key === 'Escape') {
setOpen(false);
}
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
// return focus to the button when we transitioned from !open -> open
const prevOpen = React.useRef(open);
React.useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current.focus();
}
prevOpen.current = open;
}, [open]);
return (
<>
{account?
(<>
<Button
ref={anchorRef}
id="composition-button"
aria-controls={open ? 'composition-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<PersonIcon/>
<ListItemText primary="Profile"/>
</Button>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
placement="bottom-start"
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom-start' ? 'left top' : 'left bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList
autoFocusItem={open}
id="composition-menu"
aria-labelledby="composition-button"
onKeyDown={handleListKeyDown}
>
<MenuItem onClick={handleClose}>{account && <p>Account: {account}</p>}</MenuItem>
<MenuItem onClick={handleClose}>{etherBalance && <a>ETH Balance: {formatEther(etherBalance)}</a>}</MenuItem>
<MenuItem onClick={handleClose}>{tokenBalance && <a>BBG Balance: {formatEther(tokenBalance)}</a>}</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>):(<div>
<button onClick={() => activateBrowserWallet()} className={classes.buttons}>Connect Wallet</button>
</div>)}
</>
)
setOpen(false);
};
function handleListKeyDown(event) {
if (event.key === 'Tab') {
event.preventDefault();
setOpen(false);
} else if (event.key === 'Escape') {
setOpen(false);
}
}
// return focus to the button when we transitioned from !open -> open
const prevOpen = React.useRef(open);
React.useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current.focus();
}
prevOpen.current = open;
}, [open]);
return (
<>
{account ? (
<>
<Button
ref={anchorRef}
id="composition-button"
aria-controls={open ? 'composition-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<PersonIcon />
<ListItemText primary="Profile" />
</Button>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
placement="bottom-start"
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom-start' ? 'left top' : 'left bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList
autoFocusItem={open}
id="composition-menu"
aria-labelledby="composition-button"
onKeyDown={handleListKeyDown}
>
<MenuItem onClick={handleClose}>
{account && <p>Account: {account}</p>}
</MenuItem>
<MenuItem onClick={handleClose}>
{etherBalance && (
<a>ETH Balance: {formatEther(etherBalance)}</a>
)}
</MenuItem>
<MenuItem onClick={handleClose}>
{tokenBalance && (
<a>BBG Balance: {formatEther(tokenBalance)}</a>
)}
</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
) : (
<div>
<button
onClick={() => activateBrowserWallet()}
className={classes.buttons}
>
Connect Wallet
</button>
</div>
)}
</>
);
}

View File

@ -1,49 +1,49 @@
import Paper from "@material-ui/core/Paper";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import React, {useEffect, useState} from "react";
import {querySessions} from '../api/bbgo'
import Typography from "@material-ui/core/Typography";
import {makeStyles} from "@material-ui/core/styles";
import Paper from '@material-ui/core/Paper';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import React, { useEffect, useState } from 'react';
import { querySessions } from '../api/bbgo';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
}
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
}));
export default function ExchangeSessionTabPanel() {
const classes = useStyles();
const classes = useStyles();
const [tabIndex, setTabIndex] = React.useState(0);
const handleTabClick = (event, newValue) => {
setTabIndex(newValue);
};
const [tabIndex, setTabIndex] = React.useState(0);
const handleTabClick = (event, newValue) => {
setTabIndex(newValue);
};
const [sessions, setSessions] = useState([])
const [sessions, setSessions] = useState([]);
useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
})
}, [])
useEffect(() => {
querySessions((sessions) => {
setSessions(sessions);
});
}, []);
return <Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sessions
</Typography>
<Tabs
value={tabIndex}
onChange={handleTabClick}
indicatorColor="primary"
textColor="primary"
>
{
sessions.map((session) => {
return <Tab key={session.name} label={session.name}/>
})
}
</Tabs>
return (
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sessions
</Typography>
<Tabs
value={tabIndex}
onChange={handleTabClick}
indicatorColor="primary"
textColor="primary"
>
{sessions.map((session) => {
return <Tab key={session.name} label={session.name} />;
})}
</Tabs>
</Paper>
);
}

View File

@ -8,81 +8,81 @@ import ListItemText from '@material-ui/core/ListItemText';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import PowerIcon from '@material-ui/icons/Power';
import {makeStyles} from '@material-ui/core/styles';
import {querySessions} from "../api/bbgo";
import { makeStyles } from '@material-ui/core/styles';
import { querySessions } from '../api/bbgo';
const useStyles = makeStyles((theme) => ({
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
export default function ReviewSessions({onBack, onNext}) {
const classes = useStyles();
export default function ReviewSessions({ onBack, onNext }) {
const classes = useStyles();
const [sessions, setSessions] = React.useState([]);
const [sessions, setSessions] = React.useState([]);
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
});
}, [])
const items = sessions.map((session, i) => {
console.log(session)
return (
<ListItem key={session.name}>
<ListItemIcon>
<PowerIcon/>
</ListItemIcon>
<ListItemText primary={session.name} secondary={session.exchange}/>
</ListItem>
);
})
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions);
});
}, []);
const items = sessions.map((session, i) => {
console.log(session);
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Review Sessions
</Typography>
<List component="nav">
{items}
</List>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
if (onNext) {
onNext();
}
}}>
Next
</Button>
</div>
</React.Fragment>
<ListItem key={session.name}>
<ListItemIcon>
<PowerIcon />
</ListItemIcon>
<ListItemText primary={session.name} secondary={session.exchange} />
</ListItem>
);
});
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Review Sessions
</Typography>
<List component="nav">{items}</List>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
if (onNext) {
onNext();
}
}}
>
Next
</Button>
</div>
</React.Fragment>
);
}

View File

@ -15,144 +15,143 @@ import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import {makeStyles} from '@material-ui/core/styles';
import {queryStrategies} from "../api/bbgo";
import { makeStyles } from '@material-ui/core/styles';
import { queryStrategies } from '../api/bbgo';
const useStyles = makeStyles((theme) => ({
strategyCard: {
margin: theme.spacing(1),
},
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
strategyCard: {
margin: theme.spacing(1),
},
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
function configToTable(config) {
const rows = Object.getOwnPropertyNames(config).map((k) => {
return {
key: k,
val: config[k],
}
})
const rows = Object.getOwnPropertyNames(config).map((k) => {
return {
key: k,
val: config[k],
};
});
return (
<TableContainer>
<Table aria-label="strategy attributes">
<TableHead>
<TableRow>
<TableCell>Field</TableCell>
<TableCell align="right">Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.key}>
<TableCell component="th" scope="row">
{row.key}
</TableCell>
<TableCell align="right">{row.val}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
return (
<TableContainer>
<Table aria-label="strategy attributes">
<TableHead>
<TableRow>
<TableCell>Field</TableCell>
<TableCell align="right">Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.key}>
<TableCell component="th" scope="row">
{row.key}
</TableCell>
<TableCell align="right">{row.val}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
export default function ReviewStrategies({onBack, onNext}) {
const classes = useStyles();
export default function ReviewStrategies({ onBack, onNext }) {
const classes = useStyles();
const [strategies, setStrategies] = React.useState([]);
const [strategies, setStrategies] = React.useState([]);
React.useEffect(() => {
queryStrategies((strategies) => {
setStrategies(strategies || [])
}).catch((err) => {
console.error(err);
});
}, [])
React.useEffect(() => {
queryStrategies((strategies) => {
setStrategies(strategies || []);
}).catch((err) => {
console.error(err);
});
}, []);
const items = strategies.map((o, i) => {
const mounts = o.on || [];
delete o.on
const items = strategies.map((o, i) => {
const mounts = o.on || [];
delete o.on;
const config = o[o.strategy]
const config = o[o.strategy];
const titleComps = [o.strategy.toUpperCase()]
if (config.symbol) {
titleComps.push(config.symbol)
}
const titleComps = [o.strategy.toUpperCase()];
if (config.symbol) {
titleComps.push(config.symbol);
}
const title = titleComps.join(" ")
return (
<Card key={i} className={classes.strategyCard}>
<CardHeader
avatar={
<Avatar aria-label="strategy">G</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon/>
</IconButton>
}
title={title}
subheader={`Exchange ${mounts.map((m) => m.toUpperCase())}`}
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
Strategy will be executed on session {mounts.join(',')} with the following configuration:
</Typography>
{configToTable(config)}
</CardContent>
</Card>
);
})
const title = titleComps.join(' ');
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Review Strategies
</Typography>
<Card key={i} className={classes.strategyCard}>
<CardHeader
avatar={<Avatar aria-label="strategy">G</Avatar>}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={title}
subheader={`Exchange ${mounts.map((m) => m.toUpperCase())}`}
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
Strategy will be executed on session {mounts.join(',')} with the
following configuration:
</Typography>
<List component="nav">
{items}
</List>
<div className={classes.buttons}>
<Button onClick={() => {
if (onBack) {
onBack()
}
}}>
Add New Strategy
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
if (onNext) {
onNext();
}
}}>
Next
</Button>
</div>
</React.Fragment>
{configToTable(config)}
</CardContent>
</Card>
);
});
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Review Strategies
</Typography>
<List component="nav">{items}</List>
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}
>
Add New Strategy
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
if (onNext) {
onNext();
}
}}
>
Next
</Button>
</div>
</React.Fragment>
);
}

View File

@ -1,107 +1,105 @@
import React from 'react';
import {useRouter} from 'next/router';
import { useRouter } from 'next/router';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import {ping, saveConfig, setupRestart} from "../api/bbgo";
import Box from "@material-ui/core/Box";
import Alert from "@material-ui/lab/Alert";
import { ping, saveConfig, setupRestart } from '../api/bbgo';
import Box from '@material-ui/core/Box';
import Alert from '@material-ui/lab/Alert';
const useStyles = makeStyles((theme) => ({
strategyCard: {
margin: theme.spacing(1),
},
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
strategyCard: {
margin: theme.spacing(1),
},
formControl: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
minWidth: 120,
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
},
},
}));
export default function SaveConfigAndRestart({onBack, onRestarted}) {
const classes = useStyles();
export default function SaveConfigAndRestart({ onBack, onRestarted }) {
const classes = useStyles();
const {push} = useRouter();
const [response, setResponse] = React.useState({});
const { push } = useRouter();
const [response, setResponse] = React.useState({});
const handleRestart = () => {
saveConfig((resp) => {
setResponse(resp);
const handleRestart = () => {
saveConfig((resp) => {
setResponse(resp);
setupRestart((resp) => {
let t
t = setInterval(() => {
ping(() => {
clearInterval(t)
push("/");
})
}, 1000);
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
})
setupRestart((resp) => {
let t;
t = setInterval(() => {
ping(() => {
clearInterval(t);
push('/');
});
}, 1000);
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
});
// call restart here
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
});
};
// call restart here
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
});
};
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Save Config and Restart
</Typography>
return (
<React.Fragment>
<Typography variant="h6" gutterBottom>
Save Config and Restart
</Typography>
<Typography variant="body1" gutterBottom>
Click "Save and Restart" to save the configurations to the config file <code>bbgo.yaml</code>,
and save the exchange session credentials to the dotenv file <code>.env.local</code>.
</Typography>
<Typography variant="body1" gutterBottom>
Click "Save and Restart" to save the configurations to the config file{' '}
<code>bbgo.yaml</code>, and save the exchange session credentials to the
dotenv file <code>.env.local</code>.
</Typography>
<div className={classes.buttons}>
<Button onClick={() => {
if (onBack) {
onBack()
}
}}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleRestart}>
Save and Restart
</Button>
</div>
{
response ? response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Config Saved</Alert>
</Box>
) : null : null
<div className={classes.buttons}>
<Button
onClick={() => {
if (onBack) {
onBack();
}
}}
>
Back
</Button>
</React.Fragment>
);
<Button variant="contained" color="primary" onClick={handleRestart}>
Save and Restart
</Button>
</div>
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
) : response.success ? (
<Box m={2}>
<Alert severity="success">Config Saved</Alert>
</Box>
) : null
) : null}
</React.Fragment>
);
}

View File

@ -1,102 +1,101 @@
import Drawer from "@material-ui/core/Drawer";
import Divider from "@material-ui/core/Divider";
import List from "@material-ui/core/List";
import Link from "next/link";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import DashboardIcon from "@material-ui/icons/Dashboard";
import ListItemText from "@material-ui/core/ListItemText";
import ListIcon from "@material-ui/icons/List";
import TrendingUpIcon from "@material-ui/icons/TrendingUp";
import React from "react";
import {makeStyles} from "@material-ui/core/styles";
import Drawer from '@material-ui/core/Drawer';
import Divider from '@material-ui/core/Divider';
import List from '@material-ui/core/List';
import Link from 'next/link';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import DashboardIcon from '@material-ui/icons/Dashboard';
import ListItemText from '@material-ui/core/ListItemText';
import ListIcon from '@material-ui/icons/List';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
display: 'flex',
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBarSpacer: theme.mixins.toolbar,
drawerPaper: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
position: 'relative',
whiteSpace: 'nowrap',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawer: {
width: drawerWidth,
root: {
flexGrow: 1,
display: 'flex',
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBarSpacer: theme.mixins.toolbar,
drawerPaper: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
position: 'relative',
whiteSpace: 'nowrap',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawer: {
width: drawerWidth,
},
}));
export default function SideBar() {
const classes = useStyles();
const classes = useStyles();
return <Drawer
variant="permanent"
className={classes.drawer}
PaperProps={{
className: classes.drawerPaper,
}}
anchor={"left"}
open={true}>
return (
<Drawer
variant="permanent"
className={classes.drawer}
PaperProps={{
className: classes.drawerPaper,
}}
anchor={'left'}
open={true}
>
<div className={classes.appBarSpacer} />
<div className={classes.appBarSpacer}/>
<List>
<Link href={"/"}>
<ListItem button>
<ListItemIcon>
<DashboardIcon/>
</ListItemIcon>
<ListItemText primary="Dashboard"/>
</ListItem>
</Link>
</List>
<Divider/>
<List>
<Link href={"/orders"}>
<ListItem button>
<ListItemIcon>
<ListIcon/>
</ListItemIcon>
<ListItemText primary="Orders"/>
</ListItem>
</Link>
<Link href={"/trades"}>
<ListItem button>
<ListItemIcon>
<ListIcon/>
</ListItemIcon>
<ListItemText primary="Trades"/>
</ListItem>
</Link>
<ListItem button>
<ListItemIcon>
<TrendingUpIcon/>
</ListItemIcon>
<ListItemText primary="Strategies"/>
</ListItem>
</List>
<List>
<Link href={'/'}>
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
</Link>
</List>
<Divider />
<List>
<Link href={'/orders'}>
<ListItem button>
<ListItemIcon>
<ListIcon />
</ListItemIcon>
<ListItemText primary="Orders" />
</ListItem>
</Link>
<Link href={'/trades'}>
<ListItem button>
<ListItemIcon>
<ListIcon />
</ListItemIcon>
<ListItemText primary="Trades" />
</ListItem>
</Link>
<ListItem button>
<ListItemIcon>
<TrendingUpIcon />
</ListItemIcon>
<ListItemText primary="Strategies" />
</ListItem>
</List>
</Drawer>
);
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import CardContent from "@material-ui/core/CardContent";
import Card from "@material-ui/core/Card";
import {makeStyles} from "@material-ui/core/styles";
import CardContent from '@material-ui/core/CardContent';
import Card from '@material-ui/core/Card';
import { makeStyles } from '@material-ui/core/styles';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
@ -9,78 +9,79 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
},
cardContent: {}
root: {
margin: theme.spacing(1),
},
cardContent: {},
}));
const logoCurrencies = {
"BTC": true,
"ETH": true,
"BCH": true,
"LTC": true,
"USDT": true,
"BNB": true,
"COMP": true,
"XRP": true,
"LINK": true,
"DOT": true,
"SXP": true,
"DAI": true,
"MAX": true,
"TWD": true,
"SNT": true,
"YFI": true,
"GRT": true,
}
BTC: true,
ETH: true,
BCH: true,
LTC: true,
USDT: true,
BNB: true,
COMP: true,
XRP: true,
LINK: true,
DOT: true,
SXP: true,
DAI: true,
MAX: true,
TWD: true,
SNT: true,
YFI: true,
GRT: true,
};
export default function TotalAssetsDetails({assets}) {
const classes = useStyles();
export default function TotalAssetsDetails({ assets }) {
const classes = useStyles();
const sortedAssets = [];
for (let k in assets) {
sortedAssets.push(assets[k]);
const sortedAssets = [];
for (let k in assets) {
sortedAssets.push(assets[k]);
}
sortedAssets.sort((a, b) => {
if (a.inUSD > b.inUSD) {
return -1;
}
sortedAssets.sort((a, b) => {
if (a.inUSD > b.inUSD) {
return -1
}
if (a.inUSD < b.inUSD) {
return 1
}
if (a.inUSD < b.inUSD) {
return 1;
}
return 0;
})
const items = sortedAssets.map((a) => {
return (
<ListItem key={a.currency} dense>
{
(a.currency in logoCurrencies) ? (
<ListItemAvatar>
<Avatar alt={a.currency} src={`/images/${a.currency.toLowerCase()}-logo.svg`}/>
</ListItemAvatar>
) : (
<ListItemAvatar>
<Avatar alt={a.currency}/>
</ListItemAvatar>
)
}
<ListItemText primary={`${a.currency} ${a.total}`} secondary={`=~ ${Math.round(a.inUSD)} USD`}/>
</ListItem>
)
})
return 0;
});
const items = sortedAssets.map((a) => {
return (
<Card className={classes.root} variant="outlined">
<CardContent className={classes.cardContent}>
<List dense>
{items}
</List>
</CardContent>
</Card>
<ListItem key={a.currency} dense>
{a.currency in logoCurrencies ? (
<ListItemAvatar>
<Avatar
alt={a.currency}
src={`/images/${a.currency.toLowerCase()}-logo.svg`}
/>
</ListItemAvatar>
) : (
<ListItemAvatar>
<Avatar alt={a.currency} />
</ListItemAvatar>
)}
<ListItemText
primary={`${a.currency} ${a.total}`}
secondary={`=~ ${Math.round(a.inUSD)} USD`}
/>
</ListItem>
);
});
return (
<Card className={classes.root} variant="outlined">
<CardContent className={classes.cardContent}>
<List dense>{items}</List>
</CardContent>
</Card>
);
}

View File

@ -1,95 +1,94 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import {ResponsivePie} from '@nivo/pie';
import {queryAssets} from '../api/bbgo';
import {currencyColor} from '../src/utils';
import CardContent from "@material-ui/core/CardContent";
import Card from "@material-ui/core/Card";
import {makeStyles} from "@material-ui/core/styles";
import { ResponsivePie } from '@nivo/pie';
import { queryAssets } from '../api/bbgo';
import { currencyColor } from '../src/utils';
import CardContent from '@material-ui/core/CardContent';
import Card from '@material-ui/core/Card';
import { makeStyles } from '@material-ui/core/styles';
function reduceAssetsBy(assets, field, minimum) {
let as = []
let as = [];
let others = {id: "others", labels: "others", value: 0.0}
for (let key in assets) {
if (assets[key]) {
let a = assets[key]
let value = a[field]
let others = { id: 'others', labels: 'others', value: 0.0 };
for (let key in assets) {
if (assets[key]) {
let a = assets[key];
let value = a[field];
if (value < minimum) {
others.value += value
} else {
as.push({
id: a.currency,
label: a.currency,
color: currencyColor(a.currency),
value: Math.round(value, 1),
})
}
}
if (value < minimum) {
others.value += value;
} else {
as.push({
id: a.currency,
label: a.currency,
color: currencyColor(a.currency),
value: Math.round(value, 1),
});
}
}
}
return as
return as;
}
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
},
cardContent: {
height: 350,
}
root: {
margin: theme.spacing(1),
},
cardContent: {
height: 350,
},
}));
export default function TotalAssetsPie({ assets }) {
const classes = useStyles();
return (
<Card className={classes.root} variant="outlined">
<CardContent className={classes.cardContent}>
<ResponsivePie
data={reduceAssetsBy(assets, "inUSD", 2)}
margin={{top: 20, right: 80, bottom: 10, left: 0}}
padding={0.1}
innerRadius={0.8}
padAngle={1.0}
valueFormat=" >-$f"
colors={{datum: 'data.color'}}
// colors={{scheme: 'nivo'}}
cornerRadius={0.1}
borderWidth={1}
borderColor={{from: 'color', modifiers: [['darker', 0.2]]}}
radialLabelsSkipAngle={10}
radialLabelsTextColor="#333333"
radialLabelsLinkColor={{from: 'color'}}
sliceLabelsSkipAngle={30}
sliceLabelsTextColor="#fff"
legends={[
{
anchor: 'right',
direction: 'column',
justify: false,
translateX: 70,
translateY: 0,
itemsSpacing: 5,
itemWidth: 80,
itemHeight: 24,
itemTextColor: '#999',
itemOpacity: 1,
symbolSize: 18,
symbolShape: 'circle',
effects: [
{
on: 'hover',
style: {
itemTextColor: '#000'
}
}
]
}
]}
/>
</CardContent>
</Card>
);
const classes = useStyles();
return (
<Card className={classes.root} variant="outlined">
<CardContent className={classes.cardContent}>
<ResponsivePie
data={reduceAssetsBy(assets, 'inUSD', 2)}
margin={{ top: 20, right: 80, bottom: 10, left: 0 }}
padding={0.1}
innerRadius={0.8}
padAngle={1.0}
valueFormat=" >-$f"
colors={{ datum: 'data.color' }}
// colors={{scheme: 'nivo'}}
cornerRadius={0.1}
borderWidth={1}
borderColor={{ from: 'color', modifiers: [['darker', 0.2]] }}
radialLabelsSkipAngle={10}
radialLabelsTextColor="#333333"
radialLabelsLinkColor={{ from: 'color' }}
sliceLabelsSkipAngle={30}
sliceLabelsTextColor="#fff"
legends={[
{
anchor: 'right',
direction: 'column',
justify: false,
translateX: 70,
translateY: 0,
itemsSpacing: 5,
itemWidth: 80,
itemHeight: 24,
itemTextColor: '#999',
itemOpacity: 1,
symbolSize: 18,
symbolShape: 'circle',
effects: [
{
on: 'hover',
style: {
itemTextColor: '#000',
},
},
],
},
]}
/>
</CardContent>
</Card>
);
}

View File

@ -1,52 +1,60 @@
import {useEffect, useState} from "react";
import { useEffect, useState } from 'react';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
function aggregateAssetsBy(assets, field) {
let total = 0.0
for (let key in assets) {
if (assets[key]) {
let a = assets[key]
let value = a[field]
total += value
}
let total = 0.0;
for (let key in assets) {
if (assets[key]) {
let a = assets[key];
let value = a[field];
total += value;
}
}
return total
return total;
}
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
},
title: {
fontSize: 14,
},
pos: {
marginTop: 12,
},
root: {
margin: theme.spacing(1),
},
title: {
fontSize: 14,
},
pos: {
marginTop: 12,
},
}));
export default function TotalAssetSummary({ assets }) {
const classes = useStyles();
return <Card className={classes.root} variant="outlined">
<CardContent>
<Typography className={classes.title} color="textSecondary" gutterBottom>
Total Account Balance
</Typography>
<Typography variant="h5" component="h2">
{Math.round(aggregateAssetsBy(assets, "inBTC") * 1e8) / 1e8} <span>BTC</span>
</Typography>
const classes = useStyles();
return (
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Total Account Balance
</Typography>
<Typography variant="h5" component="h2">
{Math.round(aggregateAssetsBy(assets, 'inBTC') * 1e8) / 1e8}{' '}
<span>BTC</span>
</Typography>
<Typography className={classes.pos} color="textSecondary">
Estimated Value
</Typography>
<Typography className={classes.pos} color="textSecondary">
Estimated Value
</Typography>
<Typography variant="h5" component="h3">
{Math.round(aggregateAssetsBy(assets, "inUSD") * 100) / 100} <span>USD</span>
</Typography>
</CardContent>
<Typography variant="h5" component="h3">
{Math.round(aggregateAssetsBy(assets, 'inUSD') * 100) / 100}{' '}
<span>USD</span>
</Typography>
</CardContent>
</Card>
);
}

View File

@ -1,152 +1,161 @@
import {ResponsiveBar} from '@nivo/bar';
import {queryTradingVolume} from '../api/bbgo';
import {useEffect, useState} from "react";
import { ResponsiveBar } from '@nivo/bar';
import { queryTradingVolume } from '../api/bbgo';
import { useEffect, useState } from 'react';
function toPeriodDateString(time, period) {
switch (period) {
case "day":
return time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate()
case "month":
return time.getFullYear() + "-" + (time.getMonth() + 1)
case "year":
return time.getFullYear()
switch (period) {
case 'day':
return (
time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate()
);
case 'month':
return time.getFullYear() + '-' + (time.getMonth() + 1);
case 'year':
return time.getFullYear();
}
}
return time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate()
return (
time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate()
);
}
function groupData(rows, period, segment) {
let dateIndex = {}
let startTime = null
let endTime = null
let keys = {}
let dateIndex = {};
let startTime = null;
let endTime = null;
let keys = {};
rows.forEach((v) => {
const time = new Date(v.time)
if (!startTime) {
startTime = time
}
endTime = time
const dateStr = toPeriodDateString(time, period)
const key = v[segment]
keys[key] = true
const k = key ? key : "total"
const quoteVolume = Math.round(v.quoteVolume * 100) / 100
if (dateIndex[dateStr]) {
dateIndex[dateStr][k] = quoteVolume
} else {
dateIndex[dateStr] = {
date: dateStr,
year: time.getFullYear(),
month: time.getMonth() + 1,
day: time.getDate(),
[k]: quoteVolume,
}
}
})
let data = []
while (startTime < endTime) {
const dateStr = toPeriodDateString(startTime, period)
const groupData = dateIndex[dateStr]
if (groupData) {
data.push(groupData)
} else {
data.push({
date: dateStr,
year: startTime.getFullYear(),
month: startTime.getMonth() + 1,
day: startTime.getDate(),
total: 0,
})
}
switch (period) {
case "day":
startTime.setDate(startTime.getDate() + 1)
break
case "month":
startTime.setMonth(startTime.getMonth() + 1)
break
case "year":
startTime.setFullYear(startTime.getFullYear() + 1)
break
}
rows.forEach((v) => {
const time = new Date(v.time);
if (!startTime) {
startTime = time;
}
return [data, Object.keys(keys)]
endTime = time;
const dateStr = toPeriodDateString(time, period);
const key = v[segment];
keys[key] = true;
const k = key ? key : 'total';
const quoteVolume = Math.round(v.quoteVolume * 100) / 100;
if (dateIndex[dateStr]) {
dateIndex[dateStr][k] = quoteVolume;
} else {
dateIndex[dateStr] = {
date: dateStr,
year: time.getFullYear(),
month: time.getMonth() + 1,
day: time.getDate(),
[k]: quoteVolume,
};
}
});
let data = [];
while (startTime < endTime) {
const dateStr = toPeriodDateString(startTime, period);
const groupData = dateIndex[dateStr];
if (groupData) {
data.push(groupData);
} else {
data.push({
date: dateStr,
year: startTime.getFullYear(),
month: startTime.getMonth() + 1,
day: startTime.getDate(),
total: 0,
});
}
switch (period) {
case 'day':
startTime.setDate(startTime.getDate() + 1);
break;
case 'month':
startTime.setMonth(startTime.getMonth() + 1);
break;
case 'year':
startTime.setFullYear(startTime.getFullYear() + 1);
break;
}
}
return [data, Object.keys(keys)];
}
export default function TradingVolumeBar(props) {
const [tradingVolumes, setTradingVolumes] = useState([])
const [period, setPeriod] = useState(props.period)
const [segment, setSegment] = useState(props.segment)
const [tradingVolumes, setTradingVolumes] = useState([]);
const [period, setPeriod] = useState(props.period);
const [segment, setSegment] = useState(props.segment);
useEffect(() => {
if (props.period !== period) {
setPeriod(props.period);
}
useEffect(() => {
if (props.period !== period) {
setPeriod(props.period);
}
if (props.segment !== segment) {
setSegment(props.segment);
}
if (props.segment !== segment) {
setSegment(props.segment);
}
queryTradingVolume({period: props.period, segment: props.segment }, (tradingVolumes) => {
setTradingVolumes(tradingVolumes)
})
}, [props.period, props.segment])
queryTradingVolume(
{ period: props.period, segment: props.segment },
(tradingVolumes) => {
setTradingVolumes(tradingVolumes);
}
);
}, [props.period, props.segment]);
const [data, keys] = groupData(tradingVolumes, period, segment)
const [data, keys] = groupData(tradingVolumes, period, segment);
return <ResponsiveBar keys={keys}
data={data}
indexBy={"date"}
margin={{top: 50, right: 160, bottom: 100, left: 60}}
padding={0.3}
valueScale={{type: 'linear'}}
indexScale={{type: 'band', round: true}}
labelSkipWidth={30}
labelSkipHeight={20}
enableGridY={true}
colors={{scheme: 'paired'}}
axisBottom={{
tickRotation: -90,
legend: period,
legendPosition: 'middle',
legendOffset: 80
}}
legends={[
{
dataFrom: 'keys',
anchor: 'right',
direction: 'column',
justify: false,
translateX: 120,
translateY: 0,
itemsSpacing: 2,
itemWidth: 100,
itemHeight: 20,
itemDirection: 'left-to-right',
itemOpacity: 0.85,
symbolSize: 20,
effects: [
{
on: 'hover',
style: {
itemOpacity: 1
}
}
]
}
]}
animate={true}
motionStiffness={90}
motionDamping={15}
/>;
return (
<ResponsiveBar
keys={keys}
data={data}
indexBy={'date'}
margin={{ top: 50, right: 160, bottom: 100, left: 60 }}
padding={0.3}
valueScale={{ type: 'linear' }}
indexScale={{ type: 'band', round: true }}
labelSkipWidth={30}
labelSkipHeight={20}
enableGridY={true}
colors={{ scheme: 'paired' }}
axisBottom={{
tickRotation: -90,
legend: period,
legendPosition: 'middle',
legendOffset: 80,
}}
legends={[
{
dataFrom: 'keys',
anchor: 'right',
direction: 'column',
justify: false,
translateX: 120,
translateY: 0,
itemsSpacing: 2,
itemWidth: 100,
itemHeight: 20,
itemDirection: 'left-to-right',
itemOpacity: 0.85,
symbolSize: 20,
effects: [
{
on: 'hover',
style: {
itemOpacity: 1,
},
},
],
},
]}
animate={true}
motionStiffness={90}
motionDamping={15}
/>
);
}

View File

@ -1,66 +1,72 @@
import Paper from "@material-ui/core/Paper";
import Box from "@material-ui/core/Box";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import React from "react";
import TradingVolumeBar from "./TradingVolumeBar";
import {makeStyles} from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Paper from '@material-ui/core/Paper';
import Box from '@material-ui/core/Box';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import React from 'react';
import TradingVolumeBar from './TradingVolumeBar';
import { makeStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
const useStyles = makeStyles((theme) => ({
tradingVolumeBarBox: {
height: 400,
},
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
}
tradingVolumeBarBox: {
height: 400,
},
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
}));
export default function TradingVolumePanel() {
const [period, setPeriod] = React.useState("day");
const [segment, setSegment] = React.useState("exchange");
const classes = useStyles();
const handlePeriodChange = (event, newValue) => {
setPeriod(newValue);
};
const [period, setPeriod] = React.useState('day');
const [segment, setSegment] = React.useState('exchange');
const classes = useStyles();
const handlePeriodChange = (event, newValue) => {
setPeriod(newValue);
};
const handleSegmentChange = (event, newValue) => {
setSegment(newValue);
};
const handleSegmentChange = (event, newValue) => {
setSegment(newValue);
};
return <Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Trading Volume
</Typography>
return (
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Trading Volume
</Typography>
<Grid container spacing={0}>
<Grid item xs={12} md={6}>
<Tabs value={period}
onChange={handlePeriodChange}
indicatorColor="primary"
textColor="primary">
<Tab label="Day" value={"day"}/>
<Tab label="Month" value={"month"}/>
<Tab label="Year" value={"year"}/>
</Tabs>
</Grid>
<Grid item xs={12} md={6}>
<Grid container justifyContent={"flex-end"}>
<Tabs value={segment}
onChange={handleSegmentChange}
indicatorColor="primary"
textColor="primary">
<Tab label="By Exchange" value={"exchange"}/>
<Tab label="By Symbol" value={"symbol"}/>
</Tabs>
</Grid>
</Grid>
<Grid container spacing={0}>
<Grid item xs={12} md={6}>
<Tabs
value={period}
onChange={handlePeriodChange}
indicatorColor="primary"
textColor="primary"
>
<Tab label="Day" value={'day'} />
<Tab label="Month" value={'month'} />
<Tab label="Year" value={'year'} />
</Tabs>
</Grid>
<Grid item xs={12} md={6}>
<Grid container justifyContent={'flex-end'}>
<Tabs
value={segment}
onChange={handleSegmentChange}
indicatorColor="primary"
textColor="primary"
>
<Tab label="By Exchange" value={'exchange'} />
<Tab label="By Symbol" value={'symbol'} />
</Tabs>
</Grid>
</Grid>
</Grid>
<Box className={classes.tradingVolumeBarBox}>
<TradingVolumeBar period={period} segment={segment}/>
</Box>
</Paper>;
<Box className={classes.tradingVolumeBarBox}>
<TradingVolumeBar period={period} segment={segment} />
</Box>
</Paper>
);
}

View File

@ -1,58 +1,62 @@
import React from "react";
import React from 'react';
import {makeStyles} from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import SideBar from "../components/SideBar";
import SideBar from '../components/SideBar';
import ConnectWallet from '../components/ConnectWallet';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
display: 'flex',
},
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
appBarSpacer: theme.mixins.toolbar,
container: { },
toolbar:{
justifyContent: 'space-between',
}
root: {
flexGrow: 1,
display: 'flex',
},
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
appBarSpacer: theme.mixins.toolbar,
container: {},
toolbar: {
justifyContent: 'space-between',
},
}));
export default function DashboardLayout({children}) {
const classes = useStyles();
export default function DashboardLayout({ children }) {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<Typography variant="h6" className={classes.title}>
BBGO
</Typography>
{/* <Button color="inherit">Login</Button> */}
<ConnectWallet />
</Toolbar>
</AppBar>
return (
<div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<Typography variant="h6" className={classes.title}>
BBGO
</Typography>
{/* <Button color="inherit">Login</Button> */}
<ConnectWallet />
</Toolbar>
</AppBar>
<SideBar/>
<SideBar />
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Container className={classes.container} maxWidth={false} disableGutters={true}>
{children}
</Container>
</main>
</div>
);
<main className={classes.content}>
<div className={classes.appBarSpacer} />
<Container
className={classes.container}
maxWidth={false}
disableGutters={true}
>
{children}
</Container>
</main>
</div>
);
}

View File

@ -1,43 +1,43 @@
import React from "react";
import React from 'react';
import {makeStyles} from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
const useStyles = makeStyles((theme) => ({
root: {
// flexGrow: 1,
display: 'flex',
},
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
appBarSpacer: theme.mixins.toolbar,
root: {
// flexGrow: 1,
display: 'flex',
},
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
appBarSpacer: theme.mixins.toolbar,
}));
export default function PlainLayout(props) {
const classes = useStyles();
return <div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar>
<Typography variant="h6" className={classes.title}>
{ props && props.title ? props.title : "BBGO Setup Wizard" }
</Typography>
</Toolbar>
</AppBar>
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar>
<Typography variant="h6" className={classes.title}>
{props && props.title ? props.title : 'BBGO Setup Wizard'}
</Typography>
</Toolbar>
</AppBar>
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Container>
{props.children}
</Container>
</main>
</div>;
<main className={classes.content}>
<div className={classes.appBarSpacer} />
<Container>{props.children}</Container>
</main>
</div>
);
}

View File

@ -1,8 +1,7 @@
const withTM = require('next-transpile-modules')
([
'@react-spring/three',
'@react-spring/web',
])
const withTM = require('next-transpile-modules')([
'@react-spring/three',
'@react-spring/web',
]);
module.exports = withTM({
// disable webpack 5 to make it compatible with the following rules
@ -11,7 +10,7 @@ module.exports = withTM({
config.module.rules.push({
test: /react-spring/,
sideEffects: true,
})
return config
});
return config;
},
})
});

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';
import {ThemeProvider} from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/core/styles';
import Dialog from '@material-ui/core/Dialog';
import DialogContent from '@material-ui/core/DialogContent';
@ -13,117 +13,121 @@ import Box from '@material-ui/core/Box';
import CssBaseline from '@material-ui/core/CssBaseline';
import theme from '../src/theme';
import '../styles/globals.css'
import {querySessions, querySyncStatus} from "../api/bbgo";
import '../styles/globals.css';
import { querySessions, querySyncStatus } from '../api/bbgo';
const SyncNotStarted = 0
const Syncing = 1
const SyncDone = 2
const SyncNotStarted = 0;
const Syncing = 1;
const SyncDone = 2;
// session is configured, check if we're syncing data
let syncStatusPoller = null
let syncStatusPoller = null;
export default function MyApp(props) {
const {Component, pageProps} = props;
const { Component, pageProps } = props;
const [loading, setLoading] = React.useState(true)
const [syncing, setSyncing] = React.useState(false)
const [loading, setLoading] = React.useState(true);
const [syncing, setSyncing] = React.useState(false);
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
querySessions((sessions) => {
if (sessions.length > 0) {
setSyncing(true)
querySessions((sessions) => {
if (sessions.length > 0) {
setSyncing(true);
const pollSyncStatus = () => {
querySyncStatus((status) => {
switch (status) {
case SyncNotStarted:
break
case Syncing:
setSyncing(true);
break;
case SyncDone:
clearInterval(syncStatusPoller);
setLoading(false);
setSyncing(false);
break;
}
}).catch((err) => {
console.error(err)
})
}
syncStatusPoller = setInterval(pollSyncStatus, 1000)
} else {
// no session found, so we can not sync any data
setLoading(false)
setSyncing(false)
const pollSyncStatus = () => {
querySyncStatus((status) => {
switch (status) {
case SyncNotStarted:
break;
case Syncing:
setSyncing(true);
break;
case SyncDone:
clearInterval(syncStatusPoller);
setLoading(false);
setSyncing(false);
break;
}
}).catch((err) => {
console.error(err)
})
}).catch((err) => {
console.error(err);
});
};
}, []);
syncStatusPoller = setInterval(pollSyncStatus, 1000);
} else {
// no session found, so we can not sync any data
setLoading(false);
setSyncing(false);
}
}).catch((err) => {
console.error(err);
});
}, []);
return (
<React.Fragment>
<Head>
<title>BBGO</title>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline/>
{
loading ? (syncing ? (
<Dialog
open={syncing}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Syncing Trades"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The environment is syncing trades from the exchange sessions.
Please wait a moment...
</DialogContentText>
<Box m={2}>
<LinearProgress/>
</Box>
</DialogContent>
</Dialog>
) : (
<Dialog
open={loading}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Loading"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Loading...
</DialogContentText>
<Box m={2}>
<LinearProgress/>
</Box>
</DialogContent>
</Dialog>
)) : (
<Component {...pageProps}/>
)
}
</ThemeProvider>
</React.Fragment>
);
return (
<React.Fragment>
<Head>
<title>BBGO</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
{loading ? (
syncing ? (
<Dialog
open={syncing}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{'Syncing Trades'}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The environment is syncing trades from the exchange sessions.
Please wait a moment...
</DialogContentText>
<Box m={2}>
<LinearProgress />
</Box>
</DialogContent>
</Dialog>
) : (
<Dialog
open={loading}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{'Loading'}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Loading...
</DialogContentText>
<Box m={2}>
<LinearProgress />
</Box>
</DialogContent>
</Dialog>
)
) : (
<Component {...pageProps} />
)}
</ThemeProvider>
</React.Fragment>
);
}
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
};

View File

@ -1,8 +1,6 @@
/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import Document, {
Html, Head, Main, NextScript,
} from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@material-ui/styles';
import theme from '../src/theme';
@ -66,6 +64,9 @@ MyDocument.getInitialProps = async (ctx) => {
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};

View File

@ -1,6 +1,6 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default (req, res) => {
res.statusCode = 200
res.json({ name: 'John Doe' })
}
res.statusCode = 200;
res.json({ name: 'John Doe' });
};

View File

@ -1,57 +1,55 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import PlainLayout from '../../layouts/PlainLayout';
import {QRCodeSVG} from 'qrcode.react';
import {queryOutboundIP} from '../../api/bbgo';
import { QRCodeSVG } from 'qrcode.react';
import { queryOutboundIP } from '../../api/bbgo';
const useStyles = makeStyles((theme) => ({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
textAlign: 'center',
alignItems: 'center',
alignContent: 'center',
height: 320,
}
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
textAlign: 'center',
alignItems: 'center',
alignContent: 'center',
height: 320,
},
}));
function fetchConnectUrl(cb) {
return queryOutboundIP((outboundIP) => {
cb(window.location.protocol + "//" + outboundIP + ":" + window.location.port)
})
return queryOutboundIP((outboundIP) => {
cb(
window.location.protocol + '//' + outboundIP + ':' + window.location.port
);
});
}
export default function Connect() {
const classes = useStyles();
const classes = useStyles();
const [connectUrl, setConnectUrl] = useState([])
const [connectUrl, setConnectUrl] = useState([]);
useEffect(() => {
fetchConnectUrl(function (url) {
setConnectUrl(url)
})
}, [])
useEffect(() => {
fetchConnectUrl(function (url) {
setConnectUrl(url);
});
}, []);
return (
<PlainLayout title={"Connect"}>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sign In Using QR Codes
</Typography>
<div className={classes.dataGridContainer}>
<QRCodeSVG
size={160}
style={{flexGrow: 1}}
value={connectUrl}/>
</div>
</Paper>
</PlainLayout>
);
return (
<PlainLayout title={'Connect'}>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sign In Using QR Codes
</Typography>
<div className={classes.dataGridContainer}>
<QRCodeSVG size={160} style={{ flexGrow: 1 }} value={connectUrl} />
</div>
</Paper>
</PlainLayout>
);
}

View File

@ -1,7 +1,7 @@
import React, {useState} from 'react';
import {useRouter} from 'next/router';
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Grid from '@material-ui/core/Grid';
@ -16,100 +16,98 @@ import ExchangeSessionTabPanel from '../components/ExchangeSessionTabPanel';
import DashboardLayout from '../layouts/DashboardLayout';
import {queryAssets, querySessions} from "../api/bbgo";
import { queryAssets, querySessions } from '../api/bbgo';
import { ChainId, Config, DAppProvider } from '@usedapp/core';
const useStyles = makeStyles((theme) => ({
totalAssetsSummary: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
grid: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
totalAssetsSummary: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
grid: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
}));
const config: Config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
},
}
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]:
'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
},
};
// props are pageProps passed from _app.tsx
export default function Home() {
const classes = useStyles();
const router = useRouter();
const classes = useStyles();
const router = useRouter();
const [assets, setAssets] = useState({})
const [sessions, setSessions] = React.useState([])
const [assets, setAssets] = useState({});
const [sessions, setSessions] = React.useState([]);
React.useEffect(() => {
querySessions((sessions) => {
if (sessions && sessions.length > 0) {
setSessions(sessions)
queryAssets(setAssets)
} else {
router.push("/setup");
}
}).catch((err) => {
console.error(err);
})
}, [router])
if (sessions.length == 0) {
return (
<DashboardLayout>
<Box m={4}>
<Typography variant="h4" gutterBottom>
Loading
</Typography>
</Box>
</DashboardLayout>
);
}
console.log("index: assets", assets)
React.useEffect(() => {
querySessions((sessions) => {
if (sessions && sessions.length > 0) {
setSessions(sessions);
queryAssets(setAssets);
} else {
router.push('/setup');
}
}).catch((err) => {
console.error(err);
});
}, [router]);
if (sessions.length == 0) {
return (
<DAppProvider config={config}>
<DashboardLayout>
<Paper className={classes.totalAssetsSummary}>
<Typography variant="h4" gutterBottom>
Total Assets
</Typography>
<div className={classes.grid}>
<Grid container
direction="row"
justifyContent="space-around"
alignItems="flex-start"
spacing={1}>
<Grid item xs={12} md={8}>
<TotalAssetSummary assets={assets}/>
<TotalAssetsPie assets={assets}/>
</Grid>
<Grid item xs={12} md={4}>
<TotalAssetDetails assets={assets}/>
</Grid>
</Grid>
</div>
</Paper>
<TradingVolumePanel/>
<ExchangeSessionTabPanel/>
</DashboardLayout>
</DAppProvider>
<DashboardLayout>
<Box m={4}>
<Typography variant="h4" gutterBottom>
Loading
</Typography>
</Box>
</DashboardLayout>
);
}
}
console.log('index: assets', assets);
return (
<DAppProvider config={config}>
<DashboardLayout>
<Paper className={classes.totalAssetsSummary}>
<Typography variant="h4" gutterBottom>
Total Assets
</Typography>
<div className={classes.grid}>
<Grid
container
direction="row"
justifyContent="space-around"
alignItems="flex-start"
spacing={1}
>
<Grid item xs={12} md={8}>
<TotalAssetSummary assets={assets} />
<TotalAssetsPie assets={assets} />
</Grid>
<Grid item xs={12} md={4}>
<TotalAssetDetails assets={assets} />
</Grid>
</Grid>
</div>
</Paper>
<TradingVolumePanel />
<ExchangeSessionTabPanel />
</DashboardLayout>
</DAppProvider>
);
}

View File

@ -1,72 +1,81 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import {queryClosedOrders} from '../api/bbgo';
import {DataGrid} from '@material-ui/data-grid';
import { queryClosedOrders } from '../api/bbgo';
import { DataGrid } from '@material-ui/data-grid';
import DashboardLayout from '../layouts/DashboardLayout';
const columns = [
{field: 'gid', headerName: 'GID', width: 80, type: 'number'},
{field: 'clientOrderID', headerName: 'Client Order ID', width: 130},
{field: 'exchange', headerName: 'Exchange'},
{field: 'symbol', headerName: 'Symbol'},
{field: 'orderType', headerName: 'Type'},
{field: 'side', headerName: 'Side', width: 90},
{field: 'averagePrice', headerName: 'Average Price', type: 'number', width: 120},
{field: 'quantity', headerName: 'Quantity', type: 'number'},
{field: 'executedQuantity', headerName: 'Executed Quantity', type: 'number'},
{field: 'status', headerName: 'Status'},
{field: 'isMargin', headerName: 'Margin'},
{field: 'isIsolated', headerName: 'Isolated'},
{field: 'creationTime', headerName: 'Create Time', width: 200},
{ field: 'gid', headerName: 'GID', width: 80, type: 'number' },
{ field: 'clientOrderID', headerName: 'Client Order ID', width: 130 },
{ field: 'exchange', headerName: 'Exchange' },
{ field: 'symbol', headerName: 'Symbol' },
{ field: 'orderType', headerName: 'Type' },
{ field: 'side', headerName: 'Side', width: 90 },
{
field: 'averagePrice',
headerName: 'Average Price',
type: 'number',
width: 120,
},
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
{
field: 'executedQuantity',
headerName: 'Executed Quantity',
type: 'number',
},
{ field: 'status', headerName: 'Status' },
{ field: 'isMargin', headerName: 'Margin' },
{ field: 'isIsolated', headerName: 'Isolated' },
{ field: 'creationTime', headerName: 'Create Time', width: 200 },
];
const useStyles = makeStyles((theme) => ({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
}
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
},
}));
export default function Orders() {
const classes = useStyles();
const classes = useStyles();
const [orders, setOrders] = useState([])
const [orders, setOrders] = useState([]);
useEffect(() => {
queryClosedOrders({}, (orders) => {
setOrders(orders.map((o) => {
o.id = o.gid;
return o
}))
useEffect(() => {
queryClosedOrders({}, (orders) => {
setOrders(
orders.map((o) => {
o.id = o.gid;
return o;
})
}, [])
);
});
}, []);
return (
<DashboardLayout>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Orders
</Typography>
<div className={classes.dataGridContainer}>
<div style={{flexGrow: 1}}>
<DataGrid
rows={orders}
columns={columns}
pageSize={50}
autoPageSize={true}
/>
</div>
</div>
</Paper>
</DashboardLayout>
);
return (
<DashboardLayout>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Orders
</Typography>
<div className={classes.dataGridContainer}>
<div style={{ flexGrow: 1 }}>
<DataGrid
rows={orders}
columns={columns}
pageSize={50}
autoPageSize={true}
/>
</div>
</div>
</Paper>
</DashboardLayout>
);
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Paper from '@material-ui/core/Paper';
@ -8,99 +8,125 @@ import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import ConfigureDatabaseForm from "../../components/ConfigureDatabaseForm";
import AddExchangeSessionForm from "../../components/AddExchangeSessionForm";
import ReviewSessions from "../../components/ReviewSessions";
import ConfigureGridStrategyForm from "../../components/ConfigureGridStrategyForm";
import ReviewStrategies from "../../components/ReviewStrategies";
import SaveConfigAndRestart from "../../components/SaveConfigAndRestart";
import ConfigureDatabaseForm from '../../components/ConfigureDatabaseForm';
import AddExchangeSessionForm from '../../components/AddExchangeSessionForm';
import ReviewSessions from '../../components/ReviewSessions';
import ConfigureGridStrategyForm from '../../components/ConfigureGridStrategyForm';
import ReviewStrategies from '../../components/ReviewStrategies';
import SaveConfigAndRestart from '../../components/SaveConfigAndRestart';
import PlainLayout from '../../layouts/PlainLayout';
const useStyles = makeStyles((theme) => ({
paper: {
padding: theme.spacing(2),
},
paper: {
padding: theme.spacing(2),
},
}));
const steps = ['Configure Database', 'Add Exchange Session', 'Review Sessions', 'Configure Strategy', 'Review Strategies', 'Save Config and Restart'];
const steps = [
'Configure Database',
'Add Exchange Session',
'Review Sessions',
'Configure Strategy',
'Review Strategies',
'Save Config and Restart',
];
function getStepContent(step, setActiveStep) {
switch (step) {
case 0:
return <ConfigureDatabaseForm onConfigured={() => {
setActiveStep(1)
}}/>;
case 1:
return (
<AddExchangeSessionForm
onBack={() => { setActiveStep(0) }}
onAdded={() => { setActiveStep(2) }}
/>
);
case 2:
return (
<ReviewSessions
onBack={() => { setActiveStep(1) }}
onNext={() => { setActiveStep(3) }}
/>
);
case 3:
return (
<ConfigureGridStrategyForm
onBack={() => { setActiveStep(2) }}
onAdded={() => { setActiveStep(4) }}
/>
);
case 4:
return (
<ReviewStrategies
onBack={() => { setActiveStep(3) }}
onNext={() => { setActiveStep(5) }}
/>
);
switch (step) {
case 0:
return (
<ConfigureDatabaseForm
onConfigured={() => {
setActiveStep(1);
}}
/>
);
case 1:
return (
<AddExchangeSessionForm
onBack={() => {
setActiveStep(0);
}}
onAdded={() => {
setActiveStep(2);
}}
/>
);
case 2:
return (
<ReviewSessions
onBack={() => {
setActiveStep(1);
}}
onNext={() => {
setActiveStep(3);
}}
/>
);
case 3:
return (
<ConfigureGridStrategyForm
onBack={() => {
setActiveStep(2);
}}
onAdded={() => {
setActiveStep(4);
}}
/>
);
case 4:
return (
<ReviewStrategies
onBack={() => {
setActiveStep(3);
}}
onNext={() => {
setActiveStep(5);
}}
/>
);
case 5:
return (
<SaveConfigAndRestart
onBack={() => { setActiveStep(4) }}
onRestarted={() => {
case 5:
return (
<SaveConfigAndRestart
onBack={() => {
setActiveStep(4);
}}
onRestarted={() => {}}
/>
);
}}
/>
)
default:
throw new Error('Unknown step');
}
default:
throw new Error('Unknown step');
}
}
export default function Setup() {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
return (
<PlainLayout>
<Box m={4}>
<Paper className={classes.paper}>
<Typography variant="h4" component="h2" gutterBottom>
Setup Session
</Typography>
return (
<PlainLayout>
<Box m={4}>
<Paper className={classes.paper}>
<Typography variant="h4" component="h2" gutterBottom>
Setup Session
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
<React.Fragment>
{getStepContent(activeStep, setActiveStep)}
</React.Fragment>
</Paper>
</Box>
</PlainLayout>
);
<React.Fragment>
{getStepContent(activeStep, setActiveStep)}
</React.Fragment>
</Paper>
</Box>
</PlainLayout>
);
}

View File

@ -1,67 +1,68 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import {queryTrades} from '../api/bbgo';
import {DataGrid} from '@material-ui/data-grid';
import { queryTrades } from '../api/bbgo';
import { DataGrid } from '@material-ui/data-grid';
import DashboardLayout from '../layouts/DashboardLayout';
const columns = [
{field: 'gid', headerName: 'GID', width: 80, type: 'number'},
{field: 'exchange', headerName: 'Exchange'},
{field: 'symbol', headerName: 'Symbol'},
{field: 'side', headerName: 'Side', width: 90},
{field: 'price', headerName: 'Price', type: 'number', width: 120},
{field: 'quantity', headerName: 'Quantity', type: 'number'},
{field: 'isMargin', headerName: 'Margin'},
{field: 'isIsolated', headerName: 'Isolated'},
{field: 'tradedAt', headerName: 'Trade Time', width: 200},
{ field: 'gid', headerName: 'GID', width: 80, type: 'number' },
{ field: 'exchange', headerName: 'Exchange' },
{ field: 'symbol', headerName: 'Symbol' },
{ field: 'side', headerName: 'Side', width: 90 },
{ field: 'price', headerName: 'Price', type: 'number', width: 120 },
{ field: 'quantity', headerName: 'Quantity', type: 'number' },
{ field: 'isMargin', headerName: 'Margin' },
{ field: 'isIsolated', headerName: 'Isolated' },
{ field: 'tradedAt', headerName: 'Trade Time', width: 200 },
];
const useStyles = makeStyles((theme) => ({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
}
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
},
}));
export default function Trades() {
const classes = useStyles();
const classes = useStyles();
const [trades, setTrades] = useState([])
const [trades, setTrades] = useState([]);
useEffect(() => {
queryTrades({}, (trades) => {
setTrades(trades.map((o) => {
o.id = o.gid;
return o
}))
useEffect(() => {
queryTrades({}, (trades) => {
setTrades(
trades.map((o) => {
o.id = o.gid;
return o;
})
}, [])
);
});
}, []);
return (
<DashboardLayout>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Trades
</Typography>
<div className={classes.dataGridContainer}>
<div style={{ flexGrow: 1 }}>
<DataGrid
rows={trades}
columns={columns}
showToolbar={true}
autoPageSize={true}
/>
</div>
</div>
</Paper>
</DashboardLayout>
);
return (
<DashboardLayout>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Trades
</Typography>
<div className={classes.dataGridContainer}>
<div style={{ flexGrow: 1 }}>
<DataGrid
rows={trades}
columns={columns}
showToolbar={true}
autoPageSize={true}
/>
</div>
</div>
</Paper>
</DashboardLayout>
);
}

View File

@ -6,13 +6,13 @@ module.exports = {
'postcss-preset-env',
{
autoprefixer: {
flexbox: 'no-2009'
flexbox: 'no-2009',
},
stage: 3,
features: {
'custom-properties': false
}
}
]
]
}
'custom-properties': false,
},
},
],
],
};

View File

@ -1,28 +1,26 @@
export function currencyColor(currency) {
switch (currency) {
case "BTC":
return "#f69c3d"
case "ETH":
return "#497493"
case "MCO":
return "#032144"
case "OMG":
return "#2159ec"
case "LTC":
return "#949494"
case "USDT":
return "#2ea07b"
case "SAND":
return "#2E9AD0"
case "XRP":
return "#00AAE4"
case "BCH":
return "#8DC351"
case "MAX":
return "#2D4692"
case "TWD":
return "#4A7DED"
}
switch (currency) {
case 'BTC':
return '#f69c3d';
case 'ETH':
return '#497493';
case 'MCO':
return '#032144';
case 'OMG':
return '#2159ec';
case 'LTC':
return '#949494';
case 'USDT':
return '#2ea07b';
case 'SAND':
return '#2E9AD0';
case 'XRP':
return '#00AAE4';
case 'BCH':
return '#8DC351';
case 'MAX':
return '#2D4692';
case 'TWD':
return '#4A7DED';
}
}

View File

@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
@ -18,12 +14,6 @@
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}