diff --git a/client/package.json b/client/package.json index 5b7406958..a1fdc2e36 100644 --- a/client/package.json +++ b/client/package.json @@ -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": { diff --git a/client/src/GraphQL/Fragments/clone.js b/client/src/GraphQL/Fragments/clone.js deleted file mode 100644 index 3fc772000..000000000 --- a/client/src/GraphQL/Fragments/clone.js +++ /dev/null @@ -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 - } -` diff --git a/client/src/GraphQL/Fragments/index.js b/client/src/GraphQL/Fragments/index.js index d916b776d..3fc772000 100644 --- a/client/src/GraphQL/Fragments/index.js +++ b/client/src/GraphQL/Fragments/index.js @@ -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 + } +` diff --git a/client/src/GraphQL/models.js b/client/src/GraphQL/models.js index 0ce6667fc..4704c7b65 100644 --- a/client/src/GraphQL/models.js +++ b/client/src/GraphQL/models.js @@ -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 +} diff --git a/client/src/views/Clone/Add/index.js b/client/src/views/Clone/Add/index.js index 08ec9b9dc..99aa9ae77 100644 --- a/client/src/views/Clone/Add/index.js +++ b/client/src/views/Clone/Add/index.js @@ -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 ( ) } else return ( - - + + - + - + Create a Bot Clone @@ -114,31 +119,31 @@ class AddClone extends Component { ) } - if(!isDefined(data.teams_TeamsByOwner)){ - return ( - } - displayEmpty - className={classes.selectEmpty} - disabled - > - You don't have any teams yet. - - ) - } else{ + if (!isDefined(data.teams_TeamsByOwner)) { + return ( + } + displayEmpty + className={classes.selectEmpty} + disabled + > + You don't have any teams yet. + + ) + } else { this.teams = data.teams_TeamsByOwner } return ( - this.setState({botError:false})} - error={this.state.botError} - onChange={e => this.setSelectedBot(e.target.value)} - > + 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 { )) )) } - + ) }} {this.state.selectedBot.kind} - { this.state.selectedBot.kind === "Trading" && - - - Available Running Modes are Backtest and Live. + {this.state.selectedBot.kind === botTypes.Trading && + + + Available Running Modes are Backtest and Live. - Backtest mode does NOT put real orders at the exchange; - it simulates the order execution at the specified price. - + Backtest mode does NOT put real orders at the exchange; + it simulates the order execution at the specified price. - - Live mode DOES put real orders at the exchange. + + Live mode DOES put real orders at the exchange. - - + + - this.setState({mode:e.target.value})} - onBlur={(e)=>this.setState({modeError:false})} - error={this.state.modeError} - fullWidth - > - {Object.keys(tradingStartModes).map(option => ( - - {tradingStartModes[option]} - - ))} - - { this.state.mode == "backtest" && - - } - rightArrowIcon={} - value={this.state.beginDatetime} - label='Backtest Start Date' - onChange={newVal => this.setState({ beginDatetime: newVal })} - className={classNames(classes.form, classes.textField)} - fullWidth - /> + this.setState({ mode: e.target.value })} + onBlur={(e) => this.setState({ modeError: false })} + error={this.state.modeError} + fullWidth + > + {Object.keys(tradingStartModes).map(option => ( + + {tradingStartModes[option]} + + ))} + - } - rightArrowIcon={} - value={this.state.endDatetime} - label='Backtest End Date' - onChange={newVal => this.setState({ endDatetime: newVal })} - className={classNames(classes.form, classes.textField)} - fullWidth - /> - - } + {this.state.mode == "backtest" && + + } + rightArrowIcon={} + 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" && - - - Select one of your available Exchange API Keys to be used by this clone. + } + rightArrowIcon={} + value={this.state.endDatetime} + label='Backtest End Date' + onChange={newVal => this.setState({ endDatetime: newVal })} + className={classNames(classes.form, classes.textField)} + fullWidth + /> + + } + + {this.state.mode == "live" && + + + Select one of your available Exchange API Keys to be used by this clone. - - {({ loading, error, data }) => { - if (loading) return ( - this.setState({keyId:e.target.value})} - onBlur={(e)=>this.setState({keyIdError:false})} - error={this.state.keyIdError} - fullWidth - > - - 'Loading keys...' + + {({ loading, error, data }) => { + if (loading) return ( + this.setState({ keyId: e.target.value })} + onBlur={(e) => this.setState({ keyIdError: false })} + error={this.state.keyIdError} + fullWidth + > + + 'Loading keys...' - - ); - if (error) return `Error! ${error.message}`; - const list = data.keyVault_AvailableKeys.map((key) => ( - - {key.key} - - )); - return ( - this.setState({keyId:e.target.value})} - onBlur={(e)=>this.setState({keyIdError:false})} - error={this.state.keyIdError} - fullWidth - > - { list } - - ); - }} - + + ); + if (error) return `Error! ${error.message}`; + const list = data.keyVault_AvailableKeys.map((key) => ( + + {key.key} + + )); + return ( + this.setState({ keyId: e.target.value })} + onBlur={(e) => this.setState({ keyIdError: false })} + error={this.state.keyIdError} + fullWidth + > + {list} + + ); + }} + - - 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. + + 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. - - } + + } - - - - The Time Period in which the bot will operate. + + The Time Period in which the bot will operate. - this.setState({timePeriod:e.target.value})} - fullWidth - > - {availableTimePeriods.map((option, index) => ( + this.setState({ timePeriod: e.target.value })} + fullWidth + > + {availableTimePeriods.map((option, index) => ( + + {option} + + ))} + + + + } + + {this.state.selectedBot.kind === botTypes.Indicator && + + this.setState({ processName: e.target.value })} + onBlur={(e) => this.setState({ processNameError: false })} + error={this.state.processNameError} + fullWidth + > + {Object.keys(indicatorProcessNames).map((option, index) => ( + + {indicatorProcessNames[option]} + + ))} + + + } + + {this.state.selectedBot.kind === botTypes.Sensor && + + this.setState({ processName: e.target.value })} + onBlur={(e) => this.setState({ processNameError: false })} + error={this.state.processNameError} + fullWidth + > + {Object.keys(sensorProcessNames).map((option, index) => ( + + {sensorProcessNames[option]} + + ))} + + + } + + {(this.state.selectedBot.kind === botTypes.Indicator + || this.state.selectedBot.kind === botTypes.Sensor) && + + this.setState({ mode: e.target.value })} + onBlur={(e) => this.setState({ modeError: false })} + error={this.state.modeError} + fullWidth + > + {Object.keys(indicatorStartModes).map(option => ( + + {indicatorStartModes[option]} + + ))} + + + {this.state.mode === "allMonths" && + + this.setState({ startYear: e.target.value })} + fullWidth + > + {getIndicatorYears().map((option, index) => ( + + {option} + + ))} + + + this.setState({ endYear: e.target.value })} + fullWidth + > + {getIndicatorYears().map((option, index) => ( + + {option} + + ))} + + + } + + {this.state.mode === "oneMonth" && + + this.setState({ startYear: e.target.value })} + fullWidth + > + {getIndicatorYears().map((option, index) => ( {option} ))} - - - { false && - - - 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. - - - this.setState({runAsTeam:e.target.checked })} - value="runAsTeam" - color="primary" - /> - } - label="Run as Team" - className={classNames(classes.form, classes.textField)} - /> - - } - - - } - - { (this.state.selectedBot.kind === "Indicator" - || this.state.selectedBot.kind === "Sensor") && - - this.setState({processName:e.target.value})} - onBlur={(e)=>this.setState({processNameError:false})} - error={this.state.processNameError} - fullWidth - > - {Object.keys(processNames).map(option => ( - - {processNames[option]} - - ))} - - this.setState({mode:e.target.value})} - onBlur={(e)=>this.setState({modeError:false})} - error={this.state.modeError} - fullWidth - > - {Object.keys(indicatorStartModes).map(option => ( - - {indicatorStartModes[option]} - - ))} - - - { this.state.mode === "allMonths" && - - this.setState({startYear:e.target.value})} - fullWidth - > - {getIndicatorYears().map((option, index) => ( - - {option} - - ))} - - - this.setState({endYear:e.target.value})} - fullWidth - > - {getIndicatorYears().map((option, index) => ( - - {option} - - ))} - - - } - - { this.state.mode === "oneMonth" && - - this.setState({startYear:e.target.value})} - fullWidth - > - {getIndicatorYears().map((option, index) => ( - - {option} - - ))} - - - this.setState({month:e.target.value})} - fullWidth - > - {availableMonths.map((option, index) => ( - - {option} - - ))} - - - } - - { this.state.mode === "noTime" && - - - The Resume Execution option let's you pick up the context of - the last execution and continue from there. - - this.setState({resumeExecution:e.target.checked })} - value="resumeExecution" - color="primary" - /> - } - label="Resume Execution" - className={classNames(classes.form, classes.textField)} - /> - { !this.state.resumeExecution && - } - rightArrowIcon={} - value={this.state.beginDatetime} - label='Start Date' - onChange={newVal => this.setState({ beginDatetime: newVal })} - className={classNames(classes.form, classes.textField)} - fullWidth - /> - } - - - } - + this.setState({ month: e.target.value })} + fullWidth + > + {availableMonths.map((option, index) => ( + + {option} + + ))} + + + } + } - - 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 ) && + + + The Resume Execution option let's you pick up the context of + the last execution and continue from there. + + this.setState({ resumeExecution: e.target.checked })} + value="resumeExecution" + color="primary" + /> + } + label="Resume Execution" + className={classNames(classes.form, classes.textField)} + /> + {!this.state.resumeExecution && + } + rightArrowIcon={} + value={this.state.beginDatetime} + label='Start Date' + onChange={newVal => this.setState({ beginDatetime: newVal })} + className={classNames(classes.form, classes.textField)} + fullWidth + /> + } + + + } + + this.setState({ exchangeName: e.target.value })} + fullWidth + > + {Object.keys(exchanges).map((option, index) => ( + + {option} + + ))} + + + + 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. - - Clone Bot! + + Clone Bot! - + @@ -511,19 +525,19 @@ class AddClone extends Component { - + ); } - 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) diff --git a/server/.gitignore b/server/.gitignore index f53991e89..cd5f56af4 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -3,8 +3,6 @@ # dependencies /node_modules -/src/config/clone-deployment.json - # testing /coverage diff --git a/server/README.md b/server/README.md index 6d5616a45..ef43b765f 100644 --- a/server/README.md +++ b/server/README.md @@ -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 updateExection are called n number of times +Note left of Clone Executor: signTransaction and updateExection are called n number of times User ->> Clones: getCloneExecutionInformation -Cloud ->> Clones: cloneExecutionComplete +Clone Executor ->> Clones: cloneExecutionComplete User ->> Clones: deleteClone Clones ->> Keys: releaseKey ``` diff --git a/server/src/config/clone-deployment.json b/server/src/config/clone-deployment.json new file mode 100644 index 000000000..e350cc3c9 --- /dev/null +++ b/server/src/config/clone-deployment.json @@ -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" + } + } + } + } +} diff --git a/server/src/enums/BotTypes.js b/server/src/enums/BotTypes.js index 02145b032..d6993d277 100644 --- a/server/src/enums/BotTypes.js +++ b/server/src/enums/BotTypes.js @@ -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 diff --git a/server/src/kubernetes/createClone.js b/server/src/kubernetes/createClone.js index ccab8aa6d..7a748540c 100644 --- a/server/src/kubernetes/createClone.js +++ b/server/src/kubernetes/createClone.js @@ -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 }) diff --git a/server/src/schema/cloneDetails.js b/server/src/schema/cloneDetails.js index 1d8acc9f6..d0dff70b3 100644 --- a/server/src/schema/cloneDetails.js +++ b/server/src/schema/cloneDetails.js @@ -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 } diff --git a/server/src/schema/queries/HistoryClones.js b/server/src/schema/queries/HistoryClones.js index 9cd860499..f9a3ee731 100644 --- a/server/src/schema/queries/HistoryClones.js +++ b/server/src/schema/queries/HistoryClones.js @@ -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 } diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js index afb70d263..60f5e4e1a 100644 --- a/server/src/schema/queries/ListClones.js +++ b/server/src/schema/queries/ListClones.js @@ -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 } diff --git a/server/src/schema/types/input/Clone.js b/server/src/schema/types/input/Clone.js index 3b106dafa..d4ad654b4 100644 --- a/server/src/schema/types/input/Clone.js +++ b/server/src/schema/types/input/Clone.js @@ -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 } }) })