Sync with latest version of clone executor

This commit is contained in:
Matias committed 2019-04-01 12:09:16 +02:00
1 parent 3dd3e6dbd6
commit 9a2d671a97
14 files changed
+500 -452

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@superalgos/operations-client",
"version": "0.0.25",
"version": "0.0.27",
"description": "Superalgos Operations client-side web app",
"main": "src/index.js",
"dependencies": {
-36
View File
@@ -1,36 +0,0 @@
import gql from 'graphql-tag'
export const clone = gql`
fragment clone on operations_Clone {
id
authId
teamId
botId
mode
resumeExecution
beginDatetime
endDatetime
waitTime
state
stateDatetime
createDatetime
lastLogs
runAsTeam
summaryDate
buyAverage
sellAverage
marketRate
combinedProfitsA
combinedProfitsB
assetA
assetB
botType
teamName
botName
botAvatar
teamAvatar
processName
keyId
botSlug
}
`
+35 -4
View File
@@ -1,5 +1,36 @@
import { clone } from './clone'
import gql from 'graphql-tag'
export {
clone
}
export const clone = gql`
fragment clone on operations_Clone {
id
authId
teamId
botId
mode
resumeExecution
beginDatetime
endDatetime
waitTime
state
stateDatetime
createDatetime
lastLogs
runAsTeam
summaryDate
buyAverage
sellAverage
marketRate
combinedProfitsA
combinedProfitsB
assetA
assetB
botType
teamName
botName
botAvatar
teamAvatar
processName
keyId
botSlug
}
`
+27 -13
View File
@@ -29,22 +29,36 @@ const tradingStartModesList = {
const indicatorStartModes = {
allMonths: 'All Months',
oneMonth: 'One Month',
noTime: 'No Time'
noTime: 'Head of the Market'
}
const processNames = {
const sensorProcessNames = {
Live: "Live-Trades",
HoleFixing: "Hole-Fixing"
}
const indicatorProcessNames = {
Daily: 'Multi-Period-Daily',
Market: 'Multi-Period-Market',
Min: "One-Min-Daily-Candles-Volumes",
Live: "Poloniex-Live-Trades",
HoleFixing: "Poloniex-Hole-Fixing"
Market: 'Multi-Period-Market'
}
const exchanges = [
{
id: '1',
name: 'Poloniex'
}
]
const tradingProcessNames = {
TradingProcess: "Trading-Process"
}
export { tradingStartModes, tradingStartModesList, exchanges, indicatorStartModes, availableMonths, processNames, availableTimePeriods }
const botTypes = {
Trading: "Trading",
Indicator: "Indicator",
Sensor: "Sensor"
}
const exchanges = {
Coss: "Coss",
Poloniex: "Poloniex",
Binance: "Binance"
}
export {
tradingStartModes, tradingStartModesList, exchanges, indicatorStartModes, availableMonths,
availableTimePeriods, sensorProcessNames, indicatorProcessNames, tradingProcessNames, botTypes
}
+387 -381
View File
@@ -6,24 +6,28 @@ import TopBar from '../../BannerTopBar'
import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames'
import styles from './styles'
import { tradingStartModes, indicatorStartModes, availableMonths, processNames, availableTimePeriods } from '../../../GraphQL/models'
import { isDefined, getIndicatorYears } from '../../../utils'
import {
MenuItem, Button, TextField, FormControl, InputLabel, Input, Typography,
Paper, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions,
Select, FormControlLabel, Checkbox, FormHelperText
tradingStartModes, indicatorStartModes, availableMonths, availableTimePeriods,
sensorProcessNames, indicatorProcessNames, tradingProcessNames, botTypes, exchanges
} from '../../../GraphQL/models'
import {
MenuItem, Button, TextField, FormControl, InputLabel, Input, Typography,
Paper, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions,
Select, FormControlLabel, Checkbox, FormHelperText
} from '@material-ui/core'
import { DateTimePicker } from 'material-ui-pickers'
import { ChevronLeft, ChevronRight } from '@material-ui/icons'
class AddClone extends Component {
constructor(props){
constructor(props) {
super(props)
let user = localStorage.getItem('user')
this.state = {
user: JSON.parse(user),
selectedBot: {'id':''},
selectedBot: { 'id': '' },
mode: '',
resumeExecution: true,
beginDatetime: DateTime.local().minus({ days: 8 }).startOf('day'),
@@ -36,12 +40,13 @@ class AddClone extends Component {
teams: [],
teamId: '',
keyId: '',
exchangeName: exchanges.Coss,
processName: '',
// Indicator Bot
startYear: 2019,
endYear: 2019,
month: 1,
processName: '',
//TradingBot
timePeriod: '01-hs',
@@ -61,7 +66,7 @@ class AddClone extends Component {
render() {
const { classes } = this.props
if( !isDefined(this.state.user) ) {
if (!isDefined(this.state.user)) {
return (
<TopBar
size='big'
@@ -71,18 +76,18 @@ class AddClone extends Component {
/>
)
} else return (
<React.Fragment>
<TopBar
size='medium'
title='Create a bot clone'
text='Create a bot clone here.'
backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
<React.Fragment>
<TopBar
size='medium'
title='Create a bot clone'
text='Create a bot clone here.'
backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
<div className='container'>
<div className='container'>
<Paper className={classNames('container', classes.root)}>
<form noValidate autoComplete="off" onSubmit={this.submitForm.bind(this)}>
<form noValidate autoComplete="off" onSubmit={this.submitForm.bind(this)}>
<Typography className={classes.typography} variant='h5' gutterBottom>
Create a Bot Clone
</Typography>
@@ -114,31 +119,31 @@ class AddClone extends Component {
)
}
if(!isDefined(data.teams_TeamsByOwner)){
return (
<Select
value='noteams'
input={<Input />}
displayEmpty
className={classes.selectEmpty}
disabled
>
<MenuItem key='1' value='noteams'>You don't have any teams yet.</MenuItem>
</Select>
)
} else{
if (!isDefined(data.teams_TeamsByOwner)) {
return (
<Select
value='noteams'
input={<Input />}
displayEmpty
className={classes.selectEmpty}
disabled
>
<MenuItem key='1' value='noteams'>You don't have any teams yet.</MenuItem>
</Select>
)
} else {
this.teams = data.teams_TeamsByOwner
}
return (
<Select
value={this.state.selectedBot.id}
displayEmpty
className={classes.selectEmpty}
onBlur={(e)=>this.setState({botError:false})}
error={this.state.botError}
onChange={e => this.setSelectedBot(e.target.value)}
>
<Select
value={this.state.selectedBot.id}
displayEmpty
className={classes.selectEmpty}
onBlur={(e) => this.setState({ botError: false })}
error={this.state.botError}
onChange={e => this.setSelectedBot(e.target.value)}
>
{
data.teams_TeamsByOwner.map(team => (
team.fb.map(financialBeing => (
@@ -148,347 +153,356 @@ class AddClone extends Component {
))
))
}
</Select>
</Select>
)
}}
</Query>
<FormHelperText>{this.state.selectedBot.kind}</FormHelperText>
</FormControl>
{ this.state.selectedBot.kind === "Trading" &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Available Running Modes are Backtest and Live.
{this.state.selectedBot.kind === botTypes.Trading &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Available Running Modes are Backtest and Live.
<ul>
<li> Backtest mode does NOT put real orders at the exchange;
it simulates the order execution at the specified price.
<li> Backtest mode does NOT put real orders at the exchange;
it simulates the order execution at the specified price.
</li>
<li>
Live mode DOES put real orders at the exchange.
<li>
Live mode DOES put real orders at the exchange.
</li>
</ul>
</Typography>
</ul>
</Typography>
<TextField
select
label="Running Mode"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.mode}
onChange={(e)=> this.setState({mode:e.target.value})}
onBlur={(e)=>this.setState({modeError:false})}
error={this.state.modeError}
fullWidth
>
{Object.keys(tradingStartModes).map(option => (
<MenuItem key={option} value={option}>
{tradingStartModes[option]}
</MenuItem>
))}
</TextField>
{ this.state.mode == "backtest" &&
<React.Fragment>
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.beginDatetime}
label='Backtest Start Date'
onChange={newVal => this.setState({ beginDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
<TextField label="Running Mode"
select
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.mode}
onChange={(e) => this.setState({ mode: e.target.value })}
onBlur={(e) => this.setState({ modeError: false })}
error={this.state.modeError}
fullWidth
>
{Object.keys(tradingStartModes).map(option => (
<MenuItem key={option} value={option}>
{tradingStartModes[option]}
</MenuItem>
))}
</TextField>
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.endDatetime}
label='Backtest End Date'
onChange={newVal => this.setState({ endDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
</React.Fragment>
}
{this.state.mode == "backtest" &&
<React.Fragment>
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.beginDatetime}
label='Backtest Start Date'
onChange={newVal => this.setState({ beginDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
{ this.state.mode == "live" &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Select one of your available Exchange API Keys to be used by this clone.
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.endDatetime}
label='Backtest End Date'
onChange={newVal => this.setState({ endDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
</React.Fragment>
}
{this.state.mode == "live" &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Select one of your available Exchange API Keys to be used by this clone.
</Typography>
<Query
query={keys.GET_ALL_KEYS}
>
{({ loading, error, data }) => {
if (loading) return (
<TextField
select
label="Exchange API Key"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.keyId}
onChange={(e)=> this.setState({keyId:e.target.value})}
onBlur={(e)=>this.setState({keyIdError:false})}
error={this.state.keyIdError}
fullWidth
>
<MenuItem key='1' value='Loading keys...'>
'Loading keys...'
<Query
query={keys.GET_ALL_KEYS}
>
{({ loading, error, data }) => {
if (loading) return (
<TextField
select
label="Exchange API Key"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.keyId}
onChange={(e) => this.setState({ keyId: e.target.value })}
onBlur={(e) => this.setState({ keyIdError: false })}
error={this.state.keyIdError}
fullWidth
>
<MenuItem key='1' value='Loading keys...'>
'Loading keys...'
</MenuItem>
</TextField>
);
if (error) return `Error! ${error.message}`;
const list = data.keyVault_AvailableKeys.map((key) => (
<MenuItem key={key.id} value={key.id}>
{key.key}
</MenuItem>
));
return (
<TextField
select
label="Exchange API Key"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.keyId}
onChange={(e)=> this.setState({keyId:e.target.value})}
onBlur={(e)=>this.setState({keyIdError:false})}
error={this.state.keyIdError}
fullWidth
>
{ list }
</TextField>
);
}}
</Query>
</TextField>
);
if (error) return `Error! ${error.message}`;
const list = data.keyVault_AvailableKeys.map((key) => (
<MenuItem key={key.id} value={key.id}>
{key.key}
</MenuItem>
));
return (
<TextField
select
label="Exchange API Key"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.keyId}
onChange={(e) => this.setState({ keyId: e.target.value })}
onBlur={(e) => this.setState({ keyIdError: false })}
error={this.state.keyIdError}
fullWidth
>
{list}
</TextField>
);
}}
</Query>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Because we are still in early alpha-testing phase,
Live Trading Clones are limited to an initial investment
of 0.001 BTC. If you choose to run this bot or any modified
version of the code, you are doing it at your own risk.
<Typography className={classes.typography} variant='subtitle1' align='justify'>
Because we are still in early alpha-testing phase,
Live Trading Clones are limited to an initial investment
of 0.001 BTC. If you choose to run this bot or any modified
version of the code, you are doing it at your own risk.
</Typography>
</React.Fragment>
}
</React.Fragment>
}
<Typography className={classes.typography} variant='subtitle1' align='justify'>
The Time Period in which the bot will operate.
<Typography className={classes.typography} variant='subtitle1' align='justify'>
The Time Period in which the bot will operate.
</Typography>
<TextField
select
label="Time Period"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.timePeriod}
onChange={(e)=> this.setState({timePeriod:e.target.value})}
fullWidth
>
{availableTimePeriods.map((option, index) => (
<TextField label="Time Period"
select
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.timePeriod}
onChange={(e) => this.setState({ timePeriod: e.target.value })}
fullWidth
>
{availableTimePeriods.map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{this.state.selectedBot.kind === botTypes.Indicator &&
<React.Fragment>
<TextField label="Process Name"
select
className={classNames(classes.textField, classes.form)}
value={this.state.processName}
onChange={(e) => this.setState({ processName: e.target.value })}
onBlur={(e) => this.setState({ processNameError: false })}
error={this.state.processNameError}
fullWidth
>
{Object.keys(indicatorProcessNames).map((option, index) => (
<MenuItem key={index} value={indicatorProcessNames[option]}>
{indicatorProcessNames[option]}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{this.state.selectedBot.kind === botTypes.Sensor &&
<React.Fragment>
<TextField label="Process Name"
select
className={classNames(classes.textField, classes.form)}
value={this.state.processName}
onChange={(e) => this.setState({ processName: e.target.value })}
onBlur={(e) => this.setState({ processNameError: false })}
error={this.state.processNameError}
fullWidth
>
{Object.keys(sensorProcessNames).map((option, index) => (
<MenuItem key={index} value={sensorProcessNames[option]}>
{sensorProcessNames[option]}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{(this.state.selectedBot.kind === botTypes.Indicator
|| this.state.selectedBot.kind === botTypes.Sensor) &&
<React.Fragment>
<TextField label="Running Mode"
select
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.mode}
onChange={(e) => this.setState({ mode: e.target.value })}
onBlur={(e) => this.setState({ modeError: false })}
error={this.state.modeError}
fullWidth
>
{Object.keys(indicatorStartModes).map(option => (
<MenuItem key={option} value={option}>
{indicatorStartModes[option]}
</MenuItem>
))}
</TextField>
{this.state.mode === "allMonths" &&
<React.Fragment>
<TextField
id="beginYearInput"
select
label="Start Year"
className={classNames(classes.textField, classes.form)}
value={this.state.startYear}
onChange={(e) => this.setState({ startYear: e.target.value })}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={option} value={index}>
{option}
</MenuItem>
))}
</TextField>
<TextField
id="endYearInput"
select
label="End Year"
className={classNames(classes.textField, classes.form)}
value={this.state.endYear}
onChange={(e) => this.setState({ endYear: e.target.value })}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={option} value={index}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{this.state.mode === "oneMonth" &&
<React.Fragment>
<TextField
id="beginYearInput"
select
label="Year"
className={classNames(classes.textField, classes.form)}
value={this.state.startYear}
onChange={(e) => this.setState({ startYear: e.target.value })}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
{ false &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
If Run as Team is selected, the clone will be taken from the
team respository instead of your own team member folder.
Competitions runs from the team respository.
</Typography>
<FormControlLabel
control={
<Checkbox
checked={this.state.runAsTeam}
onChange={(e)=>this.setState({runAsTeam:e.target.checked })}
value="runAsTeam"
color="primary"
/>
}
label="Run as Team"
className={classNames(classes.form, classes.textField)}
/>
</React.Fragment>
}
</React.Fragment>
}
{ (this.state.selectedBot.kind === "Indicator"
|| this.state.selectedBot.kind === "Sensor") &&
<React.Fragment>
<TextField
select
label="Process Name"
className={classNames(classes.textField, classes.form)}
value={this.state.processName}
onChange={(e)=> this.setState({processName:e.target.value})}
onBlur={(e)=>this.setState({processNameError:false})}
error={this.state.processNameError}
fullWidth
>
{Object.keys(processNames).map(option => (
<MenuItem key={option} value={processNames[option]}>
{processNames[option]}
</MenuItem>
))}
</TextField>
<TextField
select
label="Running Mode"
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.mode}
onChange={(e)=> this.setState({mode:e.target.value})}
onBlur={(e)=>this.setState({modeError:false})}
error={this.state.modeError}
fullWidth
>
{Object.keys(indicatorStartModes).map(option => (
<MenuItem key={option} value={option}>
{indicatorStartModes[option]}
</MenuItem>
))}
</TextField>
{ this.state.mode === "allMonths" &&
<React.Fragment>
<TextField
id="beginYearInput"
select
label="Start Year"
className={classNames(classes.textField, classes.form)}
value={this.state.startYear}
onChange={(e)=> this.setState({startYear:e.target.value})}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={option} value={index}>
{option}
</MenuItem>
))}
</TextField>
<TextField
id="endYearInput"
select
label="End Year"
className={classNames(classes.textField, classes.form)}
value={this.state.endYear}
onChange={(e)=> this.setState({endYear:e.target.value})}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={option} value={index}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{ this.state.mode === "oneMonth" &&
<React.Fragment>
<TextField
id="beginYearInput"
select
label="Year"
className={classNames(classes.textField, classes.form)}
value={this.state.startYear}
onChange={(e)=> this.setState({startYear:e.target.value})}
fullWidth
>
{getIndicatorYears().map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
<TextField
id="endMonthInput"
select
label="Month"
className={classNames(classes.textField, classes.form)}
value={this.state.month}
onChange={(e)=> this.setState({month:e.target.value})}
fullWidth
>
{availableMonths.map((option, index) => (
<MenuItem key={index} value={index+1}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
{ this.state.mode === "noTime" &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
The Resume Execution option let's you pick up the context of
the last execution and continue from there.
</Typography>
<FormControlLabel
control={
<Checkbox
checked={this.state.resumeExecution}
onChange={(e)=>this.setState({resumeExecution:e.target.checked })}
value="resumeExecution"
color="primary"
/>
}
label="Resume Execution"
className={classNames(classes.form, classes.textField)}
/>
{ !this.state.resumeExecution &&
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.beginDatetime}
label='Start Date'
onChange={newVal => this.setState({ beginDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
}
</React.Fragment>
}
</React.Fragment>
<TextField
id="endMonthInput"
select
label="Month"
className={classNames(classes.textField, classes.form)}
value={this.state.month}
onChange={(e) => this.setState({ month: e.target.value })}
fullWidth
>
{availableMonths.map((option, index) => (
<MenuItem key={index} value={index + 1}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
</React.Fragment>
}
<Typography className={classes.typography} variant='subtitle1' align='justify'>
You will be able to see the clone information under Active Clones.
Whenever you decide to stop the clone you can delete it. On History Clones will find all deleted clones.
{(this.state.mode === "noTime" && this.state.selectedBot.kind !== botTypes.Sensor ) &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
The Resume Execution option let's you pick up the context of
the last execution and continue from there.
</Typography>
<FormControlLabel
control={
<Checkbox
checked={this.state.resumeExecution}
onChange={(e) => this.setState({ resumeExecution: e.target.checked })}
value="resumeExecution"
color="primary"
/>
}
label="Resume Execution"
className={classNames(classes.form, classes.textField)}
/>
{!this.state.resumeExecution &&
<DateTimePicker
autoOk
disableFuture
format="DD' at 'HH:mm"
ampm={false}
showTabs={false}
leftArrowIcon={<ChevronLeft />}
rightArrowIcon={<ChevronRight />}
value={this.state.beginDatetime}
label='Start Date'
onChange={newVal => this.setState({ beginDatetime: newVal })}
className={classNames(classes.form, classes.textField)}
fullWidth
/>
}
</React.Fragment>
}
<TextField label="Exchange"
select
className={classNames(classes.margin, classes.textField, classes.form)}
value={this.state.exchangeName}
onChange={(e) => this.setState({ exchangeName: e.target.value })}
fullWidth
>
{Object.keys(exchanges).map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
You will be able to see the clone information under Active Clones.
Whenever you decide to stop the clone you can delete it. On History Clones will find all deleted clones.
</Typography>
<div className={classes.actionButton} >
<Button
type="submit"
variant='contained' color='secondary'>
Clone Bot!
<Button
type="submit"
variant='contained' color='secondary'>
Clone Bot!
</Button>
</div>
</div>
</form>
@@ -511,19 +525,19 @@ class AddClone extends Component {
</Button>
</DialogActions>
</Dialog>
</Paper>
</Paper>
</div>
</React.Fragment>
);
}
setSelectedBot(botId){
if(isDefined(this.teams)){
setSelectedBot(botId) {
if (isDefined(this.teams)) {
for (var j = 0; j < this.teams.length; j++) {
for (var i = 0; i < this.teams[j].fb.length; i++) {
if(this.teams[j].fb[i].id === botId){
this.setState({selectedBot:this.teams[j].fb[i]})
this.setState({teamId:this.teams[j].id})
if (this.teams[j].fb[i].id === botId) {
this.setState({ selectedBot: this.teams[j].fb[i] })
this.setState({ teamId: this.teams[j].id })
return
}
}
@@ -531,16 +545,16 @@ class AddClone extends Component {
}
}
async submitForm(e){
async submitForm(e) {
e.preventDefault()
let error = this.validate()
if (!error){
if (!error) {
let serverResponse = await this.createCloneOnServer()
error = serverResponse.errors || !isDefined(serverResponse.data.operations_AddClone)
if(error){
if (error) {
this.state.serverResponse = serverResponse.errors[0].message || serverResponse.errors[0]
this.state.serverError = true
}else{
} else {
this.state.serverResponse = "The new clone was sucessfully created."
this.state.serverError = false
}
@@ -549,24 +563,25 @@ class AddClone extends Component {
}
}
async createCloneOnServer(){
async createCloneOnServer() {
let variables = {
clone:{
clone: {
teamId: this.state.teamId,
botId: this.state.selectedBot.id,
mode: this.state.mode,
resumeExecution: this.state.resumeExecution,
botType: this.getBotTypeFromKind(this.state.selectedBot.kind)
botType: this.state.selectedBot.kind,
exchangeName: this.state.exchangeName
}
}
if(this.state.selectedBot.kind === "Trading"){
if (this.state.selectedBot.kind === "Trading") {
variables.clone.runAsTeam = this.state.runAsTeam
variables.clone.processName = 'Trading-Process'
variables.clone.timePeriod = this.state.timePeriod
if(this.state.mode === "backtest"){
variables.clone.beginDatetime = this.state.beginDatetime.valueOf() / 1000|0
variables.clone.endDatetime = this.state.endDatetime.valueOf() / 1000|0
if (this.state.mode === "backtest") {
variables.clone.beginDatetime = this.state.beginDatetime.valueOf() / 1000 | 0
variables.clone.endDatetime = this.state.endDatetime.valueOf() / 1000 | 0
variables.clone.waitTime = this.state.waitTime
} else {
variables.clone.keyId = this.state.keyId
@@ -577,7 +592,7 @@ class AddClone extends Component {
variables.clone.endYear = this.state.endYear
variables.clone.month = this.state.month
variables.clone.runAsTeam = true
variables.clone.beginDatetime = this.state.beginDatetime.valueOf() / 1000|0
variables.clone.beginDatetime = this.state.beginDatetime.valueOf() / 1000 | 0
}
return this.props.addCloneMutation({
@@ -585,15 +600,6 @@ class AddClone extends Component {
})
}
getBotTypeFromKind(kind){
if(kind === 'Trading')
return 'Trading'
else if(kind === 'Indicator')
return 'Indicator'
else if(kind === 'Sensor')
return 'Extraction'
}
handleNewCloneConfirmationOpen = () => {
this.setState({ isNewCloneConfirmationOpen: true })
};
@@ -603,15 +609,15 @@ class AddClone extends Component {
isNewCloneConfirmationOpen: false
})
if(!this.state.serverError)
if (!this.state.serverError)
this.setState({
selectedBot: {'id':''},
selectedBot: { 'id': '' },
mode: '',
resumeExecution: true,
beginDatetime: DateTime.local().minus({ days: 8 }).startOf('day'),
endDatetime: DateTime.local(),
waitTime: 1,
state : '',
state: '',
stateDatetime: 0,
createDatetime: 0,
runAsTeam: false,
@@ -633,21 +639,21 @@ class AddClone extends Component {
})
};
validate(){
validate() {
let isError = false
if(this.state.selectedBot.length < 1) {
if (this.state.selectedBot.length < 1) {
isError = true
this.setState(state => ({ selectedBotError: true }));
}
if(this.state.mode.length < 1) {
if (this.state.mode.length < 1) {
isError = true
this.setState(state => ({ modeError: true }));
}
if(this.state.selectedBot.kind !== "Trading"
&& this.state.processName.length < 1 ) {
if (this.state.selectedBot.kind !== "Trading"
&& this.state.processName.length < 1) {
isError = true
this.setState(state => ({ processNameError: true }));
}
@@ -658,6 +664,6 @@ class AddClone extends Component {
}
export default compose(
graphql(clones.OPERATIONS_ADD_CLONE, { name:'addCloneMutation' }),
graphql(clones.OPERATIONS_ADD_CLONE, { name: 'addCloneMutation' }),
withStyles(styles)
)(AddClone)
-2
View File
@@ -3,8 +3,6 @@
# dependencies
/node_modules
/src/config/clone-deployment.json
# testing
/coverage
+7 -7
View File
@@ -4,10 +4,10 @@ The Superalgos Operations Server manages the clone life cycle inside the **Super
The main function includes storing the metadata information of the clones and handling the interaction with the orchestration software which is [Kubernetes](https://Kubernetes.io).
# Execution sequence
The operations module (or Clones) integrates with other system modules to enable secure and encapsulated execution. On the clone creation process the encapsulation is achieved by deploying a **Cloud** Image on a **Kubernates** container.
The operations module (or Clones) integrates with other system modules to enable secure and encapsulated execution. On the clone creation process the encapsulation is achieved by deploying a **Clone Executor** Image on a **Kubernates** container.
- [Keys](https://github.com/Superalgos/KeyVaultModule): Manages the signature of the messages sent to the external exchanges.
- [Teams](https://github.com/Superalgos/TeamsModule): Provide team and bot authorization.
- [Cloud](https://github.com/Superalgos/AACloud): Receive the execution parameters and runs an independent instance of the platform.
- [Clone Executor](https://github.com/Superalgos/CloneExecutor): Receive the execution parameters and runs an independent instance of the platform.
```mermaid
sequenceDiagram
@@ -19,13 +19,13 @@ Clones ->> Keys: authorizeCloneToUseKey
Keys ->> Clones: geCloneTeamAndBot
Keys ->> Teams: getTeamMembers
Keys -->> Clones: authorizeCloneToUseKey
Clones ->> Cloud: createClone
Cloud ->> Keys: signTransaction
Cloud ->> Clones: updateExection
Clones ->> Clone Executor: createClone
Clone Executor ->> Keys: signTransaction
Clone Executor ->> Clones: updateExection
Note left of Cloud: signTransaction and <br/> updateExection are <br/> called n number <br/> of times
Note left of Clone Executor: signTransaction and <br/> updateExection are <br/> called n number <br/> of times
User ->> Clones: getCloneExecutionInformation
Cloud ->> Clones: cloneExecutionComplete
Clone Executor ->> Clones: cloneExecutionComplete
User ->> Clones: deleteClone
Clones ->> Keys: releaseKey
```
+31
View File
@@ -0,0 +1,31 @@
{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": {
"labels": {
"app": "clone-executor"
},
"name": "clone-executor"
},
"spec": {
"template": {
"spec": {
"restartPolicy": "OnFailure",
"containers": [
{
"name": "clone-executor",
"image": "superalgos/clone-executor:v1.0",
"command": ["node", "Run.js"],
"env": [
]
}
]
},
"metadata": {
"labels": {
"app": "clone-executor"
}
}
}
}
}
+2 -2
View File
@@ -1,11 +1,11 @@
export const Trading = 'Trading'
export const Indicator = 'Indicator'
export const Extraction = 'Extraction'
export const Sensor = 'Sensor'
export const BotTypesEnum = [
Trading,
Indicator,
Extraction
Sensor
]
export default BotTypesEnum
+7 -3
View File
@@ -4,7 +4,7 @@ import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
import deploymentManifest from '../config/clone-deployment.json'
import { BACKTEST, COMPETITION, LIVE, NO_TIME } from '../enums/CloneMode'
import { Trading, Indicator, Extraction } from '../enums/BotTypes'
import { Trading, Indicator, Sensor } from '../enums/BotTypes'
const createClone = async (clone) => {
try {
@@ -80,6 +80,10 @@ const createClone = async (clone) => {
'name': 'PROCESS',
'value': clone.processName
})
env.push({
'name': 'EXCHANGE_NAME',
'value': clone.exchangeName
})
logger.debug('createClone Trading Configuration.')
if (clone.botType === Trading) {
@@ -130,7 +134,7 @@ const createClone = async (clone) => {
'value': clone.keyId
})
}
} else if (clone.botType === Indicator || clone.botType === Extraction) {
} else if (clone.botType === Indicator || clone.botType === Sensor) {
if (clone.mode === NO_TIME) {
if (!clone.resumeExecution) {
env.push({
@@ -159,7 +163,7 @@ const createClone = async (clone) => {
}
deploymentManifest.spec.template.spec.containers[0].env = env
logger.debug('ENV %s: ', JSON.stringify(env))
await client.apis.batch.v1.namespaces('default').jobs.post(
{ body: deploymentManifest })
+1
View File
@@ -10,6 +10,7 @@ const cloneDetails = (selectedBot, clone) => {
clone.teamSlug = selectedBot.team.slug
clone.teamAvatar = selectedBot.team.profile.avatar
clone.userLoggedIn = selectedBot.team.members[0].member.alias
clone.botType = selectedBot.kind
}
return clone
}
@@ -31,7 +31,6 @@ const resolve = async(parent, { botType }, context) => {
let bot = allUserBots[j].node
if(clones[i].botId === bot.id){
let clone = cloneDetails(bot, clones[i])
clone.botType = bot.kind
cloneResponse.push(clone)
break
}
-1
View File
@@ -34,7 +34,6 @@ const resolve = async (parent, { botType, queryLogs }, context) => {
let bot = allUserBots[j].node
if(clones[i].botId === bot.id){
let clone = cloneDetails(bot, clones[i])
clone.botType = bot.kind
cloneResponse.push(clone)
break
}
+2 -1
View File
@@ -25,7 +25,8 @@ const Type = new GraphQLInputObjectType({
processName: { type: new GraphQLNonNull(GraphQLString) },
botType: { type: new GraphQLNonNull(GraphQLString) },
keyId: { type: GraphQLString },
timePeriod: { type: GraphQLString }
timePeriod: { type: GraphQLString },
exchangeName: { type: GraphQLString }
})
})