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,9 +20,9 @@ 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: {
@ -38,11 +38,11 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
export default function AddExchangeSessionForm({onBack, onAdded}) {
export default function AddExchangeSessionForm({ onBack, onAdded }) {
const classes = useStyles();
const [exchangeType, setExchangeType] = React.useState('max');
const [customSessionName, setCustomSessionName] = React.useState(false);
@ -60,16 +60,16 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
const [isMargin, setIsMargin] = React.useState(false);
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState("");
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState('');
const resetTestResponse = () => {
setTestResponse(null)
}
setTestResponse(null);
};
const handleExchangeTypeChange = (event) => {
setExchangeType(event.target.value);
setSessionName(event.target.value);
resetTestResponse()
resetTestResponse();
};
const createSessionConfig = () => {
@ -82,34 +82,34 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
envVarPrefix: exchangeType.toUpperCase(),
isolatedMargin: isIsolatedMargin,
isolatedMarginSymbol: isolatedMarginSymbol,
}
}
};
};
const handleAdd = (event) => {
const payload = createSessionConfig()
const payload = createSessionConfig();
addSession(payload, (response) => {
setResponse(response)
setResponse(response);
if (onAdded) {
setTimeout(onAdded, 3000)
setTimeout(onAdded, 3000);
}
}).catch((error) => {
console.error(error)
setResponse(error.response)
})
console.error(error);
setResponse(error.response);
});
};
const handleTestConnection = (event) => {
const payload = createSessionConfig()
setTesting(true)
const payload = createSessionConfig();
setTesting(true);
testSessionConnection(payload, (response) => {
console.log(response)
setTesting(false)
setTestResponse(response)
console.log(response);
setTesting(false);
setTestResponse(response);
}).catch((error) => {
console.error(error)
setTesting(false)
setTestResponse(error.response)
})
console.error(error);
setTesting(false);
setTestResponse(error.response);
});
};
return (
@ -118,7 +118,6 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
Add Exchange Session
</Typography>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormControl className={classes.formControl}>
<InputLabel id="exchange-type-select-label">Exchange</InputLabel>
@ -128,8 +127,8 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
value={exchangeType}
onChange={handleExchangeTypeChange}
>
<MenuItem value={"binance"}>Binance</MenuItem>
<MenuItem value={"max"}>Max</MenuItem>
<MenuItem value={'binance'}>Binance</MenuItem>
<MenuItem value={'max'}>Max</MenuItem>
</Select>
</FormControl>
</Grid>
@ -143,7 +142,7 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
required
disabled={!customSessionName}
onChange={(event) => {
setSessionName(event.target.value)
setSessionName(event.target.value);
}}
value={sessionName}
/>
@ -151,16 +150,23 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<Grid item xs={12} sm={6}>
<FormControlLabel
control={<Checkbox color="secondary" name="custom_session_name"
control={
<Checkbox
color="secondary"
name="custom_session_name"
onChange={(event) => {
setCustomSessionName(event.target.checked);
}} value="1"/>}
}}
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/>
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>
@ -176,8 +182,12 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => { setShowApiKey(!showApiKey) }}
onMouseDown={(event) => { event.preventDefault() }}
onClick={() => {
setShowApiKey(!showApiKey);
}}
onMouseDown={(event) => {
event.preventDefault();
}}
edge="end"
>
{showApiKey ? <Visibility /> : <VisibilityOff />}
@ -185,15 +195,14 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
</InputAdornment>
}
onChange={(event) => {
setApiKey(event.target.value)
resetTestResponse()
setApiKey(event.target.value);
resetTestResponse();
}}
/>
</FormControl>
</Grid>
<Grid item xs={12}>
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="apiSecret">API Secret</InputLabel>
<FilledInput
@ -204,8 +213,12 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<InputAdornment position="end">
<IconButton
aria-label="toggle key visibility"
onClick={() => { setShowApiSecret(!showApiSecret) }}
onMouseDown={(event) => { event.preventDefault() }}
onClick={() => {
setShowApiSecret(!showApiSecret);
}}
onMouseDown={(event) => {
event.preventDefault();
}}
edge="end"
>
{showApiSecret ? <Visibility /> : <VisibilityOff />}
@ -213,49 +226,66 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
</InputAdornment>
}
onChange={(event) => {
setApiSecret(event.target.value)
resetTestResponse()
setApiSecret(event.target.value);
resetTestResponse();
}}
/>
</FormControl>
</Grid>
{exchangeType === "binance" ? (
{exchangeType === 'binance' ? (
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox color="secondary" name="isMargin" onChange={(event) => {
control={
<Checkbox
color="secondary"
name="isMargin"
onChange={(event) => {
setIsMargin(event.target.checked);
resetTestResponse();
}} value="1"/>}
}}
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>
<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"
control={
<Checkbox
color="secondary"
name="isIsolatedMargin"
onChange={(event) => {
setIsIsolatedMargin(event.target.checked);
resetTestResponse()
}} value="1"/>}
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>
<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 ?
{isIsolatedMargin ? (
<TextField
id="isolatedMarginSymbol"
name="isolatedMarginSymbol"
label="Isolated Margin Symbol"
onChange={(event) => {
setIsolatedMarginSymbol(event.target.value);
resetTestResponse()
resetTestResponse();
}}
fullWidth
required
/>
: null}
) : null}
</Grid>
) : null}
</Grid>
@ -266,28 +296,26 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
if (onBack) {
onBack();
}
}}>
}}
>
Back
</Button>
<Button
color="primary"
onClick={handleTestConnection}
disabled={testing}>
{testing ? "Testing" : "Test Connection"}
disabled={testing}
>
{testing ? 'Testing' : 'Test Connection'}
</Button>
<Button
variant="contained"
color="primary"
onClick={handleAdd}
>
<Button variant="contained" color="primary" onClick={handleAdd}>
Add
</Button>
</div>
{
testResponse ? testResponse.error ? (
{testResponse ? (
testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
@ -295,11 +323,11 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null : null
}
) : null
) : null}
{
response ? response.error ? (
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
@ -307,10 +335,8 @@ export default function AddExchangeSessionForm({onBack, onAdded}) {
<Box m={2}>
<Alert severity="success">Exchange Session Added</Alert>
</Box>
) : null : null
}
) : null
) : null}
</React.Fragment>
);
}

View File

@ -13,9 +13,9 @@ 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: {
@ -31,30 +31,32 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
export default function ConfigureDatabaseForm({onConfigured}) {
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 [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)
}
setTestResponse(null);
};
const handleConfigureDatabase = (event) => {
const dsn = getDSN()
const dsn = getDSN();
configureDatabase({driver, dsn}, (response) => {
configureDatabase({ driver, dsn }, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
@ -62,27 +64,26 @@ export default function ConfigureDatabaseForm({onConfigured}) {
setConfigured(true);
setTimeout(onConfigured, 3000);
}
}).catch((err) => {
console.error(err);
setTesting(false);
setTestResponse(err.response.data);
})
}
});
};
const handleTestConnection = (event) => {
const dsn = getDSN()
const dsn = getDSN();
setTesting(true);
testDatabaseConnection({driver, dsn}, (response) => {
console.log(response)
setTesting(false)
setTestResponse(response)
testDatabaseConnection({ driver, dsn }, (response) => {
console.log(response);
setTesting(false);
setTestResponse(response);
}).catch((err) => {
console.error(err)
setTesting(false)
setTestResponse(err.response.data)
})
console.error(err);
setTesting(false);
setTestResponse(err.response.data);
});
};
return (
@ -92,79 +93,94 @@ export default function ConfigureDatabaseForm({onConfigured}) {
</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.
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) => {
<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"/>
}}
>
<FormControlLabel
value="sqlite3"
control={<Radio />}
label="Standard (Default)"
/>
<FormControlLabel
value="mysql"
control={<Radio />}
label="MySQL"
/>
</RadioGroup>
</FormControl>
<FormHelperText>
</FormHelperText>
<FormHelperText></FormHelperText>
</Box>
</Grid>
{driver === "mysql" ? (
{driver === 'mysql' ? (
<Grid item xs={12} sm={8}>
<TextField id="mysql_url" name="mysql_url" label="MySQL Data Source Name"
<TextField
id="mysql_url"
name="mysql_url"
label="MySQL Data Source Name"
fullWidth
required
defaultValue={mysqlURL}
onChange={(event) => {
setMysqlURL(event.target.value)
resetTestResponse()
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>
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.
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"}
{testing ? 'Testing' : 'Test Connection'}
</Button>
<Button
@ -177,8 +193,8 @@ export default function ConfigureDatabaseForm({onConfigured}) {
</Button>
</div>
{
testResponse ? testResponse.error ? (
{testResponse ? (
testResponse.error ? (
<Box m={2}>
<Alert severity="error">{testResponse.error}</Alert>
</Box>
@ -186,11 +202,8 @@ export default function ConfigureDatabaseForm({onConfigured}) {
<Box m={2}>
<Alert severity="success">Connection Test Succeeded</Alert>
</Box>
) : null : null
}
) : 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,32 +24,32 @@ 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)
const f = parseFloat(s);
if (!isNaN(f)) {
return f
return f;
}
}
return null
return null;
}
function parseFloatCall(s, cb) {
if (s) {
const f = parseFloat(s)
const f = parseFloat(s);
if (!isNaN(f)) {
cb(f)
cb(f);
}
}
}
function StandardNumberFormat(props) {
const {inputRef, onChange, ...other} = props;
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
@ -71,7 +75,7 @@ StandardNumberFormat.propTypes = {
};
function PriceNumberFormat(props) {
const {inputRef, onChange, ...other} = props;
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
@ -112,15 +116,14 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
export default function ConfigureGridStrategyForm({onBack, onAdded}) {
export default function ConfigureGridStrategyForm({ onBack, onAdded }) {
const classes = useStyles();
const [errors, setErrors] = React.useState({})
const [errors, setErrors] = React.useState({});
const [sessions, setSessions] = React.useState([]);
@ -144,54 +147,53 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
React.useEffect(() => {
querySessions((sessions) => {
setSessions(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":
case 'fixedQuantity':
payload.quantity = parseFloatValid(fixedQuantity);
break;
case "fixedAmount":
case 'fixedAmount':
payload.amount = parseFloatValid(fixedAmount);
break;
}
if (!selectedSessionName) {
setErrors({ session: true })
return
setErrors({ session: true });
return;
}
if (!selectedSymbol) {
setErrors({ symbol: true })
return
setErrors({ symbol: true });
return;
}
console.log(payload)
attachStrategyOn(selectedSessionName, "grid", payload, (response) => {
console.log(response)
setResponse(response)
console.log(payload);
attachStrategyOn(selectedSessionName, 'grid', payload, (response) => {
console.log(response);
setResponse(response);
if (onAdded) {
setTimeout(onAdded, 3000)
setTimeout(onAdded, 3000);
}
}).catch((err) => {
console.error(err);
setResponse(err.response.data)
}).finally(() => {
setErrors({})
})
.catch((err) => {
console.error(err);
setResponse(err.response.data);
})
.finally(() => {
setErrors({});
});
};
const handleQuantityBy = (event) => {
@ -200,14 +202,14 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
const handleSessionChange = (event) => {
const sessionName = event.target.value;
setSelectedSessionName(sessionName)
setSelectedSessionName(sessionName);
querySessionSymbols(sessionName, (symbols) => {
setActiveSessionSymbols(symbols);
}).catch((err) => {
console.error(err);
setResponse(err.response.data)
})
setResponse(err.response.data);
});
};
const sessionMenuItems = sessions.map((session, index) => {
@ -216,7 +218,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
{session.name}
</MenuItem>
);
})
});
const symbolMenuItems = activeSessionSymbols.map((symbol, index) => {
return (
@ -224,7 +226,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
{symbol}
</MenuItem>
);
})
});
return (
<React.Fragment>
@ -233,15 +235,20 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
</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.
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}>
<FormControl
required
className={classes.formControl}
error={errors.session}
>
<InputLabel id="session-select-label">Session</InputLabel>
<Select
labelId="session-select-label"
@ -258,7 +265,11 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
</Grid>
<Grid item xs={12}>
<FormControl required className={classes.formControl} error={errors.symbol}>
<FormControl
required
className={classes.formControl}
error={errors.symbol}
>
<InputLabel id="symbol-select-label">Market</InputLabel>
<Select
labelId="symbol-select-label"
@ -266,7 +277,8 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
value={selectedSymbol ? selectedSymbol : ''}
onChange={(event) => {
setSelectedSymbol(event.target.value);
}}>
}}
>
{symbolMenuItems}
</Select>
</FormControl>
@ -283,7 +295,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setUpperPrice)
parseFloatCall(event.target.value, setUpperPrice);
}}
value={upperPrice}
InputProps={{
@ -300,7 +312,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setLowerPrice)
parseFloatCall(event.target.value, setLowerPrice);
}}
value={lowerPrice}
InputProps={{
@ -317,7 +329,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setProfitSpread)
parseFloatCall(event.target.value, setProfitSpread);
}}
value={profitSpread}
InputProps={{
@ -326,19 +338,30 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
/>
</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
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" ? (
{quantityBy === 'fixedQuantity' ? (
<TextField
id="fixedQuantity"
name="order_quantity"
@ -346,7 +369,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedQuantity)
parseFloatCall(event.target.value, setFixedQuantity);
}}
value={fixedQuantity}
InputProps={{
@ -355,7 +378,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
/>
) : null}
{quantityBy === "fixedAmount" ? (
{quantityBy === 'fixedAmount' ? (
<TextField
id="orderAmount"
name="order_amount"
@ -363,7 +386,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setFixedAmount)
parseFloatCall(event.target.value, setFixedAmount);
}}
value={fixedAmount}
InputProps={{
@ -381,7 +404,7 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
fullWidth
required
onChange={(event) => {
parseFloatCall(event.target.value, setGridNumber)
parseFloatCall(event.target.value, setGridNumber);
}}
value={gridNumber}
InputProps={{
@ -397,21 +420,18 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
if (onBack) {
onBack();
}
}}>
}}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleAdd}
>
<Button variant="contained" color="primary" onClick={handleAdd}>
Add Strategy
</Button>
</div>
{
response ? response.error ? (
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
@ -419,10 +439,8 @@ export default function ConfigureGridStrategyForm({onBack, onAdded}) {
<Box m={2}>
<Alert severity="success">Strategy Added</Alert>
</Box>
) : null : null
}
) : 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,13 +9,11 @@ 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: {
@ -25,20 +23,17 @@ const useStyles = makeStyles((theme) => ({
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 { activateBrowserWallet, account } = useEthers();
const etherBalance = useEtherBalance(account);
const tokenBalance = useTokenBalance(BBG, account);
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
@ -76,8 +71,8 @@ export default function ConnectWallet() {
return (
<>
{account?
(<>
{account ? (
<>
<Button
ref={anchorRef}
id="composition-button"
@ -86,8 +81,8 @@ export default function ConnectWallet() {
aria-haspopup="true"
onClick={handleToggle}
>
<PersonIcon/>
<ListItemText primary="Profile"/>
<PersonIcon />
<ListItemText primary="Profile" />
</Button>
<Popper
open={open}
@ -113,19 +108,36 @@ export default function ConnectWallet() {
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>
<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>)}
</>
)
) : (
<div>
<button
onClick={() => activateBrowserWallet()}
className={classes.buttons}
>
Connect Wallet
</button>
</div>
)}
</>
);
}

View File

@ -1,16 +1,16 @@
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),
}
},
}));
export default function ExchangeSessionTabPanel() {
@ -21,15 +21,16 @@ export default function ExchangeSessionTabPanel() {
setTabIndex(newValue);
};
const [sessions, setSessions] = useState([])
const [sessions, setSessions] = useState([]);
useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
})
}, [])
setSessions(sessions);
});
}, []);
return <Paper className={classes.paper}>
return (
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sessions
</Typography>
@ -39,11 +40,10 @@ export default function ExchangeSessionTabPanel() {
indicatorColor="primary"
textColor="primary"
>
{
sessions.map((session) => {
return <Tab key={session.name} label={session.name}/>
})
}
{sessions.map((session) => {
return <Tab key={session.name} label={session.name} />;
})}
</Tabs>
</Paper>
);
}

View File

@ -8,8 +8,8 @@ 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: {
@ -25,32 +25,32 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
export default function ReviewSessions({onBack, onNext}) {
export default function ReviewSessions({ onBack, onNext }) {
const classes = useStyles();
const [sessions, setSessions] = React.useState([]);
React.useEffect(() => {
querySessions((sessions) => {
setSessions(sessions)
setSessions(sessions);
});
}, [])
}, []);
const items = sessions.map((session, i) => {
console.log(session)
console.log(session);
return (
<ListItem key={session.name}>
<ListItemIcon>
<PowerIcon/>
<PowerIcon />
</ListItemIcon>
<ListItemText primary={session.name} secondary={session.exchange}/>
<ListItemText primary={session.name} secondary={session.exchange} />
</ListItem>
);
})
});
return (
<React.Fragment>
@ -58,9 +58,7 @@ export default function ReviewSessions({onBack, onNext}) {
Review Sessions
</Typography>
<List component="nav">
{items}
</List>
<List component="nav">{items}</List>
<div className={classes.buttons}>
<Button
@ -68,7 +66,8 @@ export default function ReviewSessions({onBack, onNext}) {
if (onBack) {
onBack();
}
}}>
}}
>
Back
</Button>
@ -79,7 +78,8 @@ export default function ReviewSessions({onBack, onNext}) {
if (onNext) {
onNext();
}
}}>
}}
>
Next
</Button>
</div>

View File

@ -15,8 +15,8 @@ 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: {
@ -35,7 +35,7 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
@ -44,8 +44,8 @@ function configToTable(config) {
return {
key: k,
val: config[k],
}
})
};
});
return (
<TableContainer>
@ -71,41 +71,39 @@ function configToTable(config) {
);
}
export default function ReviewStrategies({onBack, onNext}) {
export default function ReviewStrategies({ onBack, onNext }) {
const classes = useStyles();
const [strategies, setStrategies] = React.useState([]);
React.useEffect(() => {
queryStrategies((strategies) => {
setStrategies(strategies || [])
setStrategies(strategies || []);
}).catch((err) => {
console.error(err);
});
}, [])
}, []);
const items = strategies.map((o, i) => {
const mounts = o.on || [];
delete o.on
delete o.on;
const config = o[o.strategy]
const config = o[o.strategy];
const titleComps = [o.strategy.toUpperCase()]
const titleComps = [o.strategy.toUpperCase()];
if (config.symbol) {
titleComps.push(config.symbol)
titleComps.push(config.symbol);
}
const title = titleComps.join(" ")
const title = titleComps.join(' ');
return (
<Card key={i} className={classes.strategyCard}>
<CardHeader
avatar={
<Avatar aria-label="strategy">G</Avatar>
}
avatar={<Avatar aria-label="strategy">G</Avatar>}
action={
<IconButton aria-label="settings">
<MoreVertIcon/>
<MoreVertIcon />
</IconButton>
}
title={title}
@ -113,15 +111,15 @@ export default function ReviewStrategies({onBack, onNext}) {
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
Strategy will be executed on session {mounts.join(',')} with the following configuration:
Strategy will be executed on session {mounts.join(',')} with the
following configuration:
</Typography>
{configToTable(config)}
</CardContent>
</Card>
);
})
});
return (
<React.Fragment>
@ -129,16 +127,16 @@ export default function ReviewStrategies({onBack, onNext}) {
Review Strategies
</Typography>
<List component="nav">
{items}
</List>
<List component="nav">{items}</List>
<div className={classes.buttons}>
<Button onClick={() => {
<Button
onClick={() => {
if (onBack) {
onBack()
onBack();
}
}}>
}}
>
Add New Strategy
</Button>
@ -149,7 +147,8 @@ export default function ReviewStrategies({onBack, onNext}) {
if (onNext) {
onNext();
}
}}>
}}
>
Next
</Button>
</div>

View File

@ -1,15 +1,14 @@
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: {
@ -28,14 +27,14 @@ const useStyles = makeStyles((theme) => ({
paddingBottom: theme.spacing(2),
'& > *': {
marginLeft: theme.spacing(1),
}
},
},
}));
export default function SaveConfigAndRestart({onBack, onRestarted}) {
export default function SaveConfigAndRestart({ onBack, onRestarted }) {
const classes = useStyles();
const {push} = useRouter();
const { push } = useRouter();
const [response, setResponse] = React.useState({});
const handleRestart = () => {
@ -43,17 +42,17 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
setResponse(resp);
setupRestart((resp) => {
let t
let t;
t = setInterval(() => {
ping(() => {
clearInterval(t)
push("/");
})
clearInterval(t);
push('/');
});
}, 1000);
}).catch((err) => {
console.error(err);
setResponse(err.response.data);
})
});
// call restart here
}).catch((err) => {
@ -69,29 +68,29 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
</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>.
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={() => {
<Button
onClick={() => {
if (onBack) {
onBack()
onBack();
}
}}>
}}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleRestart}>
<Button variant="contained" color="primary" onClick={handleRestart}>
Save and Restart
</Button>
</div>
{
response ? response.error ? (
{response ? (
response.error ? (
<Box m={2}>
<Alert severity="error">{response.error}</Alert>
</Box>
@ -99,9 +98,8 @@ export default function SaveConfigAndRestart({onBack, onRestarted}) {
<Box m={2}>
<Alert severity="success">Config Saved</Alert>
</Box>
) : null : null
}
) : null
) : null}
</React.Fragment>
);
}

View File

@ -1,15 +1,15 @@
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;
@ -46,57 +46,56 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function SideBar() {
const classes = useStyles();
return <Drawer
return (
<Drawer
variant="permanent"
className={classes.drawer}
PaperProps={{
className: classes.drawerPaper,
}}
anchor={"left"}
open={true}>
<div className={classes.appBarSpacer}/>
anchor={'left'}
open={true}
>
<div className={classes.appBarSpacer} />
<List>
<Link href={"/"}>
<Link href={'/'}>
<ListItem button>
<ListItemIcon>
<DashboardIcon/>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard"/>
<ListItemText primary="Dashboard" />
</ListItem>
</Link>
</List>
<Divider/>
<Divider />
<List>
<Link href={"/orders"}>
<Link href={'/orders'}>
<ListItem button>
<ListItemIcon>
<ListIcon/>
<ListIcon />
</ListItemIcon>
<ListItemText primary="Orders"/>
<ListItemText primary="Orders" />
</ListItem>
</Link>
<Link href={"/trades"}>
<Link href={'/trades'}>
<ListItem button>
<ListItemIcon>
<ListIcon/>
<ListIcon />
</ListItemIcon>
<ListItemText primary="Trades"/>
<ListItemText primary="Trades" />
</ListItem>
</Link>
<ListItem button>
<ListItemIcon>
<TrendingUpIcon/>
<TrendingUpIcon />
</ListItemIcon>
<ListItemText primary="Strategies"/>
<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';
@ -12,30 +12,30 @@ const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(1),
},
cardContent: {}
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}) {
export default function TotalAssetsDetails({ assets }) {
const classes = useStyles();
const sortedAssets = [];
@ -44,43 +44,44 @@ export default function TotalAssetsDetails({assets}) {
}
sortedAssets.sort((a, b) => {
if (a.inUSD > b.inUSD) {
return -1
return -1;
}
if (a.inUSD < b.inUSD) {
return 1
return 1;
}
return 0;
})
});
const items = sortedAssets.map((a) => {
return (
<ListItem key={a.currency} dense>
{
(a.currency in logoCurrencies) ? (
{a.currency in logoCurrencies ? (
<ListItemAvatar>
<Avatar alt={a.currency} src={`/images/${a.currency.toLowerCase()}-logo.svg`}/>
<Avatar
alt={a.currency}
src={`/images/${a.currency.toLowerCase()}-logo.svg`}
/>
</ListItemAvatar>
) : (
<ListItemAvatar>
<Avatar alt={a.currency}/>
<Avatar alt={a.currency} />
</ListItemAvatar>
)
}
<ListItemText primary={`${a.currency} ${a.total}`} secondary={`=~ ${Math.round(a.inUSD)} USD`}/>
)}
<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>
<List dense>{items}</List>
</CardContent>
</Card>
);
}

View File

@ -1,35 +1,35 @@
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}
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 a = assets[key];
let value = a[field];
if (value < minimum) {
others.value += value
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) => ({
@ -38,7 +38,7 @@ const useStyles = makeStyles((theme) => ({
},
cardContent: {
height: 350,
}
},
}));
export default function TotalAssetsPie({ assets }) {
@ -47,20 +47,20 @@ export default function TotalAssetsPie({ assets }) {
<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}}
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={{ datum: 'data.color' }}
// colors={{scheme: 'nivo'}}
cornerRadius={0.1}
borderWidth={1}
borderColor={{from: 'color', modifiers: [['darker', 0.2]]}}
borderColor={{ from: 'color', modifiers: [['darker', 0.2]] }}
radialLabelsSkipAngle={10}
radialLabelsTextColor="#333333"
radialLabelsLinkColor={{from: 'color'}}
radialLabelsLinkColor={{ from: 'color' }}
sliceLabelsSkipAngle={30}
sliceLabelsTextColor="#fff"
legends={[
@ -81,15 +81,14 @@ export default function TotalAssetsPie({ assets }) {
{
on: 'hover',
style: {
itemTextColor: '#000'
}
}
]
}
itemTextColor: '#000',
},
},
],
},
]}
/>
</CardContent>
</Card>
);
}

View File

@ -1,20 +1,20 @@
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
let total = 0.0;
for (let key in assets) {
if (assets[key]) {
let a = assets[key]
let value = a[field]
total += value
let a = assets[key];
let value = a[field];
total += value;
}
}
return total
return total;
}
const useStyles = makeStyles((theme) => ({
@ -31,13 +31,19 @@ const useStyles = makeStyles((theme) => ({
export default function TotalAssetSummary({ assets }) {
const classes = useStyles();
return <Card className={classes.root} variant="outlined">
return (
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography className={classes.title} color="textSecondary" gutterBottom>
<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>
{Math.round(aggregateAssetsBy(assets, 'inBTC') * 1e8) / 1e8}{' '}
<span>BTC</span>
</Typography>
<Typography className={classes.pos} color="textSecondary">
@ -45,8 +51,10 @@ export default function TotalAssetSummary({ assets }) {
</Typography>
<Typography variant="h5" component="h3">
{Math.round(aggregateAssetsBy(assets, "inUSD") * 100) / 100} <span>USD</span>
{Math.round(aggregateAssetsBy(assets, 'inUSD') * 100) / 100}{' '}
<span>USD</span>
</Typography>
</CardContent>
</Card>
);
}

View File

@ -1,45 +1,48 @@
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()
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)
const time = new Date(v.time);
if (!startTime) {
startTime = time
startTime = time;
}
endTime = time
endTime = time;
const dateStr = toPeriodDateString(time, period)
const key = v[segment]
const dateStr = toPeriodDateString(time, period);
const key = v[segment];
keys[key] = true
keys[key] = true;
const k = key ? key : "total"
const quoteVolume = Math.round(v.quoteVolume * 100) / 100
const k = key ? key : 'total';
const quoteVolume = Math.round(v.quoteVolume * 100) / 100;
if (dateIndex[dateStr]) {
dateIndex[dateStr][k] = quoteVolume
dateIndex[dateStr][k] = quoteVolume;
} else {
dateIndex[dateStr] = {
date: dateStr,
@ -47,16 +50,16 @@ function groupData(rows, period, segment) {
month: time.getMonth() + 1,
day: time.getDate(),
[k]: quoteVolume,
};
}
}
})
});
let data = []
let data = [];
while (startTime < endTime) {
const dateStr = toPeriodDateString(startTime, period)
const groupData = dateIndex[dateStr]
const dateStr = toPeriodDateString(startTime, period);
const groupData = dateIndex[dateStr];
if (groupData) {
data.push(groupData)
data.push(groupData);
} else {
data.push({
date: dateStr,
@ -64,29 +67,29 @@ function groupData(rows, period, segment) {
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
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)]
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) {
@ -97,29 +100,34 @@ export default function TradingVolumeBar(props) {
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}
return (
<ResponsiveBar
keys={keys}
data={data}
indexBy={"date"}
margin={{top: 50, right: 160, bottom: 100, left: 60}}
indexBy={'date'}
margin={{ top: 50, right: 160, bottom: 100, left: 60 }}
padding={0.3}
valueScale={{type: 'linear'}}
indexScale={{type: 'band', round: true}}
valueScale={{ type: 'linear' }}
indexScale={{ type: 'band', round: true }}
labelSkipWidth={30}
labelSkipHeight={20}
enableGridY={true}
colors={{scheme: 'paired'}}
colors={{ scheme: 'paired' }}
axisBottom={{
tickRotation: -90,
legend: period,
legendPosition: 'middle',
legendOffset: 80
legendOffset: 80,
}}
legends={[
{
@ -139,14 +147,15 @@ export default function TradingVolumeBar(props) {
{
on: 'hover',
style: {
itemOpacity: 1
}
}
]
}
itemOpacity: 1,
},
},
],
},
]}
animate={true}
motionStiffness={90}
motionDamping={15}
/>;
/>
);
}

View File

@ -1,12 +1,12 @@
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: {
@ -15,12 +15,12 @@ const useStyles = makeStyles((theme) => ({
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 [period, setPeriod] = React.useState('day');
const [segment, setSegment] = React.useState('exchange');
const classes = useStyles();
const handlePeriodChange = (event, newValue) => {
setPeriod(newValue);
@ -30,37 +30,43 @@ export default function TradingVolumePanel() {
setSegment(newValue);
};
return <Paper className={classes.paper}>
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}
<Tabs
value={period}
onChange={handlePeriodChange}
indicatorColor="primary"
textColor="primary">
<Tab label="Day" value={"day"}/>
<Tab label="Month" value={"month"}/>
<Tab label="Year" value={"year"}/>
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}
<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"}/>
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}/>
<TradingVolumeBar period={period} segment={segment} />
</Box>
</Paper>;
</Paper>
);
}

View File

@ -1,12 +1,12 @@
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';
@ -24,13 +24,13 @@ const useStyles = makeStyles((theme) => ({
zIndex: theme.zIndex.drawer + 1,
},
appBarSpacer: theme.mixins.toolbar,
container: { },
toolbar:{
container: {},
toolbar: {
justifyContent: 'space-between',
}
},
}));
export default function DashboardLayout({children}) {
export default function DashboardLayout({ children }) {
const classes = useStyles();
return (
@ -45,11 +45,15 @@ export default function DashboardLayout({children}) {
</Toolbar>
</AppBar>
<SideBar/>
<SideBar />
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Container className={classes.container} maxWidth={false} disableGutters={true}>
<div className={classes.appBarSpacer} />
<Container
className={classes.container}
maxWidth={false}
disableGutters={true}
>
{children}
</Container>
</main>

View File

@ -1,9 +1,9 @@
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) => ({
@ -24,20 +24,20 @@ const useStyles = makeStyles((theme) => ({
export default function PlainLayout(props) {
const classes = useStyles();
return <div className={classes.root}>
return (
<div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar>
<Typography variant="h6" className={classes.title}>
{ props && props.title ? props.title : "BBGO Setup Wizard" }
{props && props.title ? props.title : 'BBGO Setup Wizard'}
</Typography>
</Toolbar>
</AppBar>
<main className={classes.content}>
<div className={classes.appBarSpacer}/>
<Container>
{props.children}
</Container>
<div className={classes.appBarSpacer} />
<Container>{props.children}</Container>
</main>
</div>;
</div>
);
}

View File

@ -1,8 +1,7 @@
const withTM = require('next-transpile-modules')
([
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,21 +13,21 @@ 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.
@ -38,13 +38,13 @@ export default function MyApp(props) {
querySessions((sessions) => {
if (sessions.length > 0) {
setSyncing(true)
setSyncing(true);
const pollSyncStatus = () => {
querySyncStatus((status) => {
switch (status) {
case SyncNotStarted:
break
break;
case Syncing:
setSyncing(true);
break;
@ -55,46 +55,50 @@ export default function MyApp(props) {
break;
}
}).catch((err) => {
console.error(err)
})
}
console.error(err);
});
};
syncStatusPoller = setInterval(pollSyncStatus, 1000)
syncStatusPoller = setInterval(pollSyncStatus, 1000);
} else {
// no session found, so we can not sync any data
setLoading(false)
setSyncing(false)
setLoading(false);
setSyncing(false);
}
}).catch((err) => {
console.error(err)
})
console.error(err);
});
}, []);
return (
<React.Fragment>
<Head>
<title>BBGO</title>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline/>
{
loading ? (syncing ? (
<CssBaseline />
{loading ? (
syncing ? (
<Dialog
open={syncing}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Syncing Trades"}</DialogTitle>
<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/>
<LinearProgress />
</Box>
</DialogContent>
</Dialog>
@ -104,20 +108,20 @@ export default function MyApp(props) {
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Loading"}</DialogTitle>
<DialogTitle id="alert-dialog-title">{'Loading'}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Loading...
</DialogContentText>
<Box m={2}>
<LinearProgress/>
<LinearProgress />
</Box>
</DialogContent>
</Dialog>
)) : (
<Component {...pageProps}/>
)
}
) : (
<Component {...pageProps} />
)}
</ThemeProvider>
</React.Fragment>
);

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,11 +1,11 @@
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: {
@ -18,40 +18,38 @@ const useStyles = makeStyles((theme) => ({
alignItems: 'center',
alignContent: 'center',
height: 320,
}
},
}));
function fetchConnectUrl(cb) {
return queryOutboundIP((outboundIP) => {
cb(window.location.protocol + "//" + outboundIP + ":" + window.location.port)
})
cb(
window.location.protocol + '//' + outboundIP + ':' + window.location.port
);
});
}
export default function Connect() {
const classes = useStyles();
const [connectUrl, setConnectUrl] = useState([])
const [connectUrl, setConnectUrl] = useState([]);
useEffect(() => {
fetchConnectUrl(function (url) {
setConnectUrl(url)
})
}, [])
setConnectUrl(url);
});
}, []);
return (
<PlainLayout title={"Connect"}>
<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}/>
<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,11 +16,10 @@ 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),
@ -34,35 +33,34 @@ const useStyles = makeStyles((theme) => ({
},
}));
const config: Config = {
readOnlyChainId: ChainId.Mainnet,
readOnlyUrls: {
[ChainId.Mainnet]: 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
[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 [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)
setSessions(sessions);
queryAssets(setAssets);
} else {
router.push("/setup");
router.push('/setup');
}
}).catch((err) => {
console.error(err);
})
}, [router])
});
}, [router]);
if (sessions.length == 0) {
return (
@ -76,7 +74,7 @@ export default function Home() {
);
}
console.log("index: assets", assets)
console.log('index: assets', assets);
return (
<DAppProvider config={config}>
@ -87,29 +85,29 @@ export default function Home() {
</Typography>
<div className={classes.grid}>
<Grid container
<Grid
container
direction="row"
justifyContent="space-around"
alignItems="flex-start"
spacing={1}>
spacing={1}
>
<Grid item xs={12} md={8}>
<TotalAssetSummary assets={assets}/>
<TotalAssetsPie assets={assets}/>
<TotalAssetSummary assets={assets} />
<TotalAssetsPie assets={assets} />
</Grid>
<Grid item xs={12} md={4}>
<TotalAssetDetails assets={assets}/>
<TotalAssetDetails assets={assets} />
</Grid>
</Grid>
</div>
</Paper>
<TradingVolumePanel/>
<TradingVolumePanel />
<ExchangeSessionTabPanel/>
<ExchangeSessionTabPanel />
</DashboardLayout>
</DAppProvider>
);
}

View File

@ -1,27 +1,35 @@
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) => ({
@ -32,22 +40,24 @@ const useStyles = makeStyles((theme) => ({
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
}
},
}));
export default function Orders() {
const classes = useStyles();
const [orders, setOrders] = useState([])
const [orders, setOrders] = useState([]);
useEffect(() => {
queryClosedOrders({}, (orders) => {
setOrders(orders.map((o) => {
setOrders(
orders.map((o) => {
o.id = o.gid;
return o
}))
return o;
})
}, [])
);
});
}, []);
return (
<DashboardLayout>
@ -56,7 +66,7 @@ export default function Orders() {
Orders
</Typography>
<div className={classes.dataGridContainer}>
<div style={{flexGrow: 1}}>
<div style={{ flexGrow: 1 }}>
<DataGrid
rows={orders}
columns={columns}
@ -69,4 +79,3 @@ export default function Orders() {
</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,12 +8,12 @@ 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';
@ -23,52 +23,79 @@ const useStyles = makeStyles((theme) => ({
},
}));
const steps = ['Configure Database', 'Add Exchange Session', 'Review Sessions', 'Configure Strategy', 'Review Strategies', 'Save Config and Restart'];
const steps = [
'Configure Database',
'Add Exchange Session',
'Review Sessions',
'Configure Strategy',
'Review Strategies',
'Save Config and Restart',
];
function getStepContent(step, setActiveStep) {
switch (step) {
case 0:
return <ConfigureDatabaseForm onConfigured={() => {
setActiveStep(1)
}}/>;
return (
<ConfigureDatabaseForm
onConfigured={() => {
setActiveStep(1);
}}
/>
);
case 1:
return (
<AddExchangeSessionForm
onBack={() => { setActiveStep(0) }}
onAdded={() => { setActiveStep(2) }}
onBack={() => {
setActiveStep(0);
}}
onAdded={() => {
setActiveStep(2);
}}
/>
);
case 2:
return (
<ReviewSessions
onBack={() => { setActiveStep(1) }}
onNext={() => { setActiveStep(3) }}
onBack={() => {
setActiveStep(1);
}}
onNext={() => {
setActiveStep(3);
}}
/>
);
case 3:
return (
<ConfigureGridStrategyForm
onBack={() => { setActiveStep(2) }}
onAdded={() => { setActiveStep(4) }}
onBack={() => {
setActiveStep(2);
}}
onAdded={() => {
setActiveStep(4);
}}
/>
);
case 4:
return (
<ReviewStrategies
onBack={() => { setActiveStep(3) }}
onNext={() => { setActiveStep(5) }}
onBack={() => {
setActiveStep(3);
}}
onNext={() => {
setActiveStep(5);
}}
/>
);
case 5:
return (
<SaveConfigAndRestart
onBack={() => { setActiveStep(4) }}
onRestarted={() => {
onBack={() => {
setActiveStep(4);
}}
onRestarted={() => {}}
/>
)
);
default:
throw new Error('Unknown step');
@ -103,4 +130,3 @@ export default function Setup() {
</PlainLayout>
);
}

View File

@ -1,22 +1,22 @@
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) => ({
@ -27,22 +27,24 @@ const useStyles = makeStyles((theme) => ({
dataGridContainer: {
display: 'flex',
height: 'calc(100vh - 64px - 120px)',
}
},
}));
export default function Trades() {
const classes = useStyles();
const [trades, setTrades] = useState([])
const [trades, setTrades] = useState([]);
useEffect(() => {
queryTrades({}, (trades) => {
setTrades(trades.map((o) => {
setTrades(
trades.map((o) => {
o.id = o.gid;
return o
}))
return o;
})
}, [])
);
});
}, []);
return (
<DashboardLayout>
@ -64,4 +66,3 @@ export default function Trades() {
</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"
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"]
}