mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
support exchange session test from the setup wizard
This commit is contained in:
parent
ee3c76d3fb
commit
73762d9888
|
@ -2,6 +2,16 @@ import axios from "axios";
|
|||
|
||||
const baseURL = process.env.NODE_ENV === "development" ? "http://localhost:8080" : ""
|
||||
|
||||
export function testSessionConnection(data, cb) {
|
||||
return axios.post(baseURL + '/api/sessions/test-connection', data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}).then(response => {
|
||||
cb(response.data)
|
||||
});
|
||||
}
|
||||
|
||||
export function querySessions(cb) {
|
||||
axios.get(baseURL + '/api/sessions', {})
|
||||
.then(response => {
|
||||
|
@ -10,14 +20,14 @@ export function querySessions(cb) {
|
|||
}
|
||||
|
||||
export function queryTrades(params, cb) {
|
||||
axios.get(baseURL + '/api/trades', { params: params })
|
||||
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 })
|
||||
axios.get(baseURL + '/api/orders/closed', {params: params})
|
||||
.then(response => {
|
||||
cb(response.data.orders)
|
||||
});
|
||||
|
@ -31,7 +41,7 @@ export function queryAssets(cb) {
|
|||
}
|
||||
|
||||
export function queryTradingVolume(params, cb) {
|
||||
axios.get(baseURL + '/api/trading-volume', { params: params })
|
||||
axios.get(baseURL + '/api/trading-volume', {params: params})
|
||||
.then(response => {
|
||||
cb(response.data.tradingVolumes)
|
||||
});
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import React from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
|
||||
|
@ -10,6 +13,10 @@ import Checkbox from '@material-ui/core/Checkbox';
|
|||
import Select from '@material-ui/core/Select';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
import {testSessionConnection} from '../api/bbgo';
|
||||
|
||||
import {makeStyles} from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
|
@ -18,28 +25,69 @@ const useStyles = makeStyles((theme) => ({
|
|||
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 ExchangeSessionForm() {
|
||||
|
||||
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 [apiKey, setApiKey] = React.useState('');
|
||||
const [apiSecret, setApiSecret] = React.useState('');
|
||||
|
||||
const [isMargin, setIsMargin] = React.useState(false);
|
||||
const [isIsolatedMargin, setIsIsolatedMargin] = React.useState(false);
|
||||
const [isolatedMarginSymbol, setIsolatedMarginSymbol] = React.useState("");
|
||||
|
||||
const resetTestResponse = () => {
|
||||
setTestResponse(null)
|
||||
}
|
||||
|
||||
const handleExchangeTypeChange = (event) => {
|
||||
setExchangeType(event.target.value);
|
||||
setSessionName(event.target.value);
|
||||
resetTestResponse()
|
||||
};
|
||||
|
||||
const handleIsMarginChange = (event) => {
|
||||
setIsMargin(event.target.checked);
|
||||
};
|
||||
const createSessionConfig = () => {
|
||||
return {
|
||||
name: sessionName,
|
||||
exchange: exchangeType,
|
||||
key: apiKey,
|
||||
secret: apiSecret,
|
||||
margin: isMargin,
|
||||
envVarPrefix: exchangeType.toUpperCase() + "_",
|
||||
isolatedMargin: isIsolatedMargin,
|
||||
isolatedMarginSymbol: isolatedMarginSymbol,
|
||||
}
|
||||
}
|
||||
|
||||
const handleIsIsolatedMarginChange = (event) => {
|
||||
setIsIsolatedMargin(event.target.checked);
|
||||
const handleTestConnection = (event) => {
|
||||
const payload = createSessionConfig()
|
||||
setTesting(true)
|
||||
testSessionConnection(payload, (response) => {
|
||||
console.log(response)
|
||||
setTesting(false)
|
||||
setTestResponse(response)
|
||||
}).catch((reason) => {
|
||||
console.error(reason)
|
||||
setTesting(false)
|
||||
setTestResponse(reason)
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -64,71 +112,125 @@ export default function ExchangeSessionForm() {
|
|||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={12}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
required
|
||||
id="name"
|
||||
name="name"
|
||||
label="Session Name"
|
||||
fullWidth
|
||||
autoComplete="given-name"
|
||||
required
|
||||
disabled={!customSessionName}
|
||||
onChange={(event) => {
|
||||
setSessionName(event.target.value)
|
||||
}}
|
||||
value={sessionName}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField id="state" name="state" label="State/Province/Region" fullWidth/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
required
|
||||
id="zip"
|
||||
name="zip"
|
||||
label="Zip / Postal code"
|
||||
fullWidth
|
||||
autoComplete="shipping postal-code"
|
||||
<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} sm={6}>
|
||||
<TextField
|
||||
required
|
||||
id="country"
|
||||
name="country"
|
||||
label="Country"
|
||||
fullWidth
|
||||
autoComplete="shipping country"
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField id="key" name="api_key" label="API Key"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
setApiKey(event.target.value)
|
||||
resetTestResponse()
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="secondary" name="isMargin" onChange={handleIsMarginChange}
|
||||
value="1"/>}
|
||||
label="Use margin trading. This is only available for Binance"
|
||||
<TextField id="secret" name="api_secret" label="API Secret"
|
||||
fullWidth
|
||||
required
|
||||
onChange={(event) => {
|
||||
setApiSecret(event.target.value)
|
||||
resetTestResponse()
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox color="secondary" name="isIsolatedMargin"
|
||||
onChange={handleIsIsolatedMarginChange} value="1"/>}
|
||||
label="Use isolated margin trading, if this is set, you can only trade one symbol with one session. This is only available for Binance"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{isIsolatedMargin ?
|
||||
{exchangeType === "binance" ? (
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
id="isolatedMarginSymbol"
|
||||
name="isolatedMarginSymbol"
|
||||
label="Isolated Margin Symbol"
|
||||
fullWidth
|
||||
<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}
|
||||
|
||||
|
||||
) : null}
|
||||
</Grid>
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleTestConnection}
|
||||
disabled={testing}>
|
||||
{ testing ? "Testing" : "Test Connection"}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary">
|
||||
Create
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"@material-ui/core": "^4.11.2",
|
||||
"@material-ui/data-grid": "^4.0.0-alpha.18",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.57",
|
||||
"@nivo/bar": "^0.67.0",
|
||||
"@nivo/core": "^0.67.0",
|
||||
"@nivo/pie": "^0.67.0",
|
||||
|
|
|
@ -20,7 +20,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const steps = ['Add Exchange Session', 'Review Settings', 'Test Connection'];
|
||||
const steps = ['Add Exchange Session', 'Configure Strategy', 'Restart'];
|
||||
|
||||
function getStepContent(step) {
|
||||
switch (step) {
|
||||
|
@ -35,7 +35,7 @@ function getStepContent(step) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function SetupSession() {
|
||||
export default function Setup() {
|
||||
const classes = useStyles();
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
|
|
@ -159,6 +159,17 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
|
||||
"@material-ui/lab@^4.0.0-alpha.57":
|
||||
version "4.0.0-alpha.57"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.57.tgz#e8961bcf6449e8a8dabe84f2700daacfcafbf83a"
|
||||
integrity sha512-qo/IuIQOmEKtzmRD2E4Aa6DB4A87kmY6h0uYhjUmrrgmEAgbbw9etXpWPVXuRK6AGIQCjFzV6WO2i21m1R4FCw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
"@material-ui/utils" "^4.11.2"
|
||||
clsx "^1.0.4"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.8.0 || ^17.0.0"
|
||||
|
||||
"@material-ui/styles@^4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.2.tgz#e70558be3f41719e8c0d63c7a3c9ae163fdc84cb"
|
||||
|
|
|
@ -53,10 +53,15 @@ type NotificationConfig struct {
|
|||
}
|
||||
|
||||
type Session struct {
|
||||
ExchangeName string `json:"exchange" yaml:"exchange"`
|
||||
EnvVarPrefix string `json:"envVarPrefix" yaml:"envVarPrefix"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
ExchangeName string `json:"exchange" yaml:"exchange"`
|
||||
EnvVarPrefix string `json:"envVarPrefix" yaml:"envVarPrefix"`
|
||||
|
||||
Key string `json:"key,omitempty" yaml:"key,omitempty"`
|
||||
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
|
||||
|
||||
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
|
||||
Margin bool `json:"margin,omitempty" yaml:"margin"`
|
||||
Margin bool `json:"margin,omitempty" yaml:"margin,omitempty"`
|
||||
IsolatedMargin bool `json:"isolatedMargin,omitempty" yaml:"isolatedMargin,omitempty"`
|
||||
IsolatedMarginSymbol string `json:"isolatedMarginSymbol,omitempty" yaml:"isolatedMarginSymbol,omitempty"`
|
||||
}
|
||||
|
|
|
@ -142,36 +142,52 @@ func (environ *Environment) AddExchangesByViperKeys() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func NewExchangeSessionFromConfig(name string, sessionConfig Session) (*ExchangeSession, error) {
|
||||
exchangeName, err := types.ValidExchangeName(sessionConfig.ExchangeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var exchange types.Exchange
|
||||
|
||||
if sessionConfig.Key != "" && sessionConfig.Secret != "" {
|
||||
exchange, err = cmdutil.NewExchangeStandard(exchangeName, sessionConfig.Key, sessionConfig.Secret)
|
||||
} else {
|
||||
exchange, err = cmdutil.NewExchangeWithEnvVarPrefix(exchangeName, sessionConfig.EnvVarPrefix)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// configure exchange
|
||||
if sessionConfig.Margin {
|
||||
marginExchange, ok := exchange.(types.MarginExchange)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("exchange %s does not support margin", exchangeName)
|
||||
}
|
||||
|
||||
if sessionConfig.IsolatedMargin {
|
||||
marginExchange.UseIsolatedMargin(sessionConfig.IsolatedMarginSymbol)
|
||||
} else {
|
||||
marginExchange.UseMargin()
|
||||
}
|
||||
}
|
||||
|
||||
session := NewExchangeSession(name, exchange)
|
||||
session.IsMargin = sessionConfig.Margin
|
||||
session.IsIsolatedMargin = sessionConfig.IsolatedMargin
|
||||
session.IsolatedMarginSymbol = sessionConfig.IsolatedMarginSymbol
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (environ *Environment) AddExchangesFromSessionConfig(sessions map[string]Session) error {
|
||||
for sessionName, sessionConfig := range sessions {
|
||||
exchangeName, err := types.ValidExchangeName(sessionConfig.ExchangeName)
|
||||
session, err := NewExchangeSessionFromConfig(sessionName, sessionConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exchange, err := cmdutil.NewExchangeWithEnvVarPrefix(exchangeName, sessionConfig.EnvVarPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// configure exchange
|
||||
if sessionConfig.Margin {
|
||||
marginExchange, ok := exchange.(types.MarginExchange)
|
||||
if !ok {
|
||||
return fmt.Errorf("exchange %s does not support margin", exchangeName)
|
||||
}
|
||||
|
||||
if sessionConfig.IsolatedMargin {
|
||||
marginExchange.UseIsolatedMargin(sessionConfig.IsolatedMarginSymbol)
|
||||
} else {
|
||||
marginExchange.UseMargin()
|
||||
}
|
||||
}
|
||||
|
||||
session := NewExchangeSession(sessionName, exchange)
|
||||
session.IsMargin = sessionConfig.Margin
|
||||
session.IsIsolatedMargin = sessionConfig.IsolatedMargin
|
||||
session.IsolatedMarginSymbol = sessionConfig.IsolatedMarginSymbol
|
||||
environ.AddExchangeSession(sessionName, session)
|
||||
}
|
||||
|
||||
|
@ -188,7 +204,6 @@ func (environ *Environment) Init(ctx context.Context) (err error) {
|
|||
for n := range environ.sessions {
|
||||
var session = environ.sessions[n]
|
||||
|
||||
|
||||
if err := session.Init(ctx, environ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
func RunServer(ctx context.Context, userConfig *Config, environ *Environment) error {
|
||||
func RunServer(ctx context.Context, userConfig *Config, environ *Environment, trader *Trader) error {
|
||||
r := gin.Default()
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowMethods: []string{"GET", "POST"},
|
||||
AllowWebSockets: true,
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
|
@ -134,6 +136,42 @@ func RunServer(ctx context.Context, userConfig *Config, environ *Environment) er
|
|||
return
|
||||
})
|
||||
|
||||
r.POST("/api/sessions/test-connection", func(c *gin.Context) {
|
||||
var sessionConfig Session
|
||||
if err := c.BindJSON(&sessionConfig); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
session, err := NewExchangeSessionFromConfig(sessionConfig.ExchangeName, sessionConfig)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var anyErr error
|
||||
_, openOrdersErr := session.Exchange.QueryOpenOrders(ctx, "BTCUSDT")
|
||||
if openOrdersErr != nil {
|
||||
anyErr = openOrdersErr
|
||||
}
|
||||
|
||||
_, balanceErr := session.Exchange.QueryAccountBalances(ctx)
|
||||
if balanceErr != nil {
|
||||
anyErr = balanceErr
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": anyErr == nil,
|
||||
"error": anyErr,
|
||||
"balance": balanceErr == nil,
|
||||
"openOrders": openOrdersErr == nil,
|
||||
})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions", func(c *gin.Context) {
|
||||
var sessions []*ExchangeSession
|
||||
for _, session := range environ.Sessions() {
|
||||
|
|
|
@ -11,29 +11,17 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
func NewExchangeWithEnvVarPrefix(n types.ExchangeName, varPrefix string) (types.Exchange, error) {
|
||||
if len(varPrefix) == 0 {
|
||||
varPrefix = n.String()
|
||||
func NewExchangeStandard(n types.ExchangeName, key, secret string) (types.Exchange, error) {
|
||||
if len(key) == 0 || len(secret) == 0 {
|
||||
return nil, errors.New("binance: empty key or secret")
|
||||
}
|
||||
|
||||
switch n {
|
||||
|
||||
case types.ExchangeBinance:
|
||||
key := viper.GetString(varPrefix + "-api-key")
|
||||
secret := viper.GetString(varPrefix + "-api-secret")
|
||||
if len(key) == 0 || len(secret) == 0 {
|
||||
return nil, errors.New("binance: empty key or secret")
|
||||
}
|
||||
|
||||
return binance.New(key, secret), nil
|
||||
|
||||
case types.ExchangeMax:
|
||||
key := viper.GetString(varPrefix + "-api-key")
|
||||
secret := viper.GetString(varPrefix + "-api-secret")
|
||||
if len(key) == 0 || len(secret) == 0 {
|
||||
return nil, errors.New("max: empty key or secret")
|
||||
}
|
||||
|
||||
return max.New(key, secret), nil
|
||||
|
||||
default:
|
||||
|
@ -42,6 +30,20 @@ func NewExchangeWithEnvVarPrefix(n types.ExchangeName, varPrefix string) (types.
|
|||
}
|
||||
}
|
||||
|
||||
func NewExchangeWithEnvVarPrefix(n types.ExchangeName, varPrefix string) (types.Exchange, error) {
|
||||
if len(varPrefix) == 0 {
|
||||
varPrefix = n.String()
|
||||
}
|
||||
|
||||
key := viper.GetString(varPrefix + "-api-key")
|
||||
secret := viper.GetString(varPrefix + "-api-secret")
|
||||
if len(key) == 0 || len(secret) == 0 {
|
||||
return nil, errors.New("max: empty key or secret")
|
||||
}
|
||||
|
||||
return NewExchangeStandard(n, key, secret)
|
||||
}
|
||||
|
||||
// NewExchange constructor exchange object from viper config.
|
||||
func NewExchange(n types.ExchangeName) (types.Exchange, error) {
|
||||
return NewExchangeWithEnvVarPrefix(n, "")
|
||||
|
|
|
@ -55,6 +55,33 @@ var RunCmd = &cobra.Command{
|
|||
RunE: run,
|
||||
}
|
||||
|
||||
func runSetup(basectx context.Context, userConfig *bbgo.Config, enableApiServer bool) error {
|
||||
ctx, cancelTrading := context.WithCancel(basectx)
|
||||
defer cancelTrading()
|
||||
|
||||
environ := bbgo.NewEnvironment()
|
||||
|
||||
trader := bbgo.NewTrader(environ)
|
||||
|
||||
if enableApiServer {
|
||||
go func() {
|
||||
if err := bbgo.RunServer(ctx, userConfig, environ, trader); err != nil {
|
||||
log.WithError(err).Errorf("server error")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
cancelTrading()
|
||||
|
||||
shutdownCtx, cancelShutdown := context.WithDeadline(ctx, time.Now().Add(30*time.Second))
|
||||
|
||||
log.Infof("shutting down...")
|
||||
trader.Graceful.Shutdown(shutdownCtx)
|
||||
cancelShutdown()
|
||||
return nil
|
||||
}
|
||||
|
||||
func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer bool) error {
|
||||
ctx, cancelTrading := context.WithCancel(basectx)
|
||||
defer cancelTrading()
|
||||
|
@ -227,7 +254,7 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer
|
|||
|
||||
if enableApiServer {
|
||||
go func() {
|
||||
if err := bbgo.RunServer(ctx, userConfig, environ); err != nil {
|
||||
if err := bbgo.RunServer(ctx, userConfig, environ, trader); err != nil {
|
||||
log.WithError(err).Errorf("server error")
|
||||
}
|
||||
}()
|
||||
|
@ -262,24 +289,7 @@ func run(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
configFile, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(configFile) == 0 {
|
||||
return errors.New("--config option is required")
|
||||
}
|
||||
|
||||
noCompile, err := cmd.Flags().GetBool("no-compile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
userConfig, err := bbgo.Load(configFile, false)
|
||||
setup, err := cmd.Flags().GetBool("setup")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -289,17 +299,56 @@ func run(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// for wrapper binary, we can just run the strategies
|
||||
if bbgo.IsWrapperBinary || (userConfig.Build != nil && len(userConfig.Build.Imports) == 0) || noCompile {
|
||||
userConfig, err = bbgo.Load(configFile, true)
|
||||
noCompile, err := cmd.Flags().GetBool("no-compile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configFile, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userConfig *bbgo.Config
|
||||
|
||||
if setup {
|
||||
log.Infof("running in setup mode, skip reading config file")
|
||||
enableApiServer = true
|
||||
userConfig = &bbgo.Config{
|
||||
Notifications: nil,
|
||||
Persistence: nil,
|
||||
Sessions: nil,
|
||||
ExchangeStrategies: nil,
|
||||
}
|
||||
} else {
|
||||
if len(configFile) == 0 {
|
||||
return errors.New("--config option is required")
|
||||
}
|
||||
|
||||
userConfig, err = bbgo.Load(configFile, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// for wrapper binary, we can just run the strategies
|
||||
if bbgo.IsWrapperBinary || (userConfig.Build != nil && len(userConfig.Build.Imports) == 0) || noCompile {
|
||||
if bbgo.IsWrapperBinary {
|
||||
log.Infof("running wrapper binary...")
|
||||
}
|
||||
|
||||
if setup {
|
||||
return runSetup(ctx, userConfig, enableApiServer)
|
||||
}
|
||||
|
||||
userConfig, err = bbgo.Load(configFile, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return runConfig(ctx, userConfig, enableApiServer)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user