Adding support for Indicators and Extractors

This commit is contained in:
mati108@gmail.com committed 2019-01-28 10:56:00 -03:00
1 parent f942581f58
commit a8ac7385d6
12 files changed
+325 -356

No files matched your search

+10 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@advancedalgos/operations-client",
"version": "0.0.1",
"name": "@superalgos/operations-client",
"version": "0.0.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -689,6 +689,14 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-number-format": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-4.0.6.tgz",
"integrity": "sha512-WfBlqYRD0fGZpQxRy1apxsPeaqtxhzo7CnUVFKBoGDsQbc89+DQNhjG/D3IxgGYM47oKB+W/Su9ol/vVS6Wc2Q==",
"requires": {
"prop-types": "15.6.2"
}
},
"react-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@advancedalgos/operations-client",
"version": "0.0.1",
"name": "@superalgos/operations-client",
"version": "0.0.2",
"description": "Superalgos Operations client-side web app",
"main": "src/index.js",
"dependencies": {
@@ -15,6 +15,7 @@
"react": "^16.6.1",
"react-apollo": "^2.2.4",
"react-dom": "^16.6.1",
"react-number-format": "^4.0.6",
"react-router-dom": "^4.3.1"
},
"devDependencies": {
+2 -19
View File
@@ -10,29 +10,12 @@ export const GET_ALL_TEAMS_QUERY = gql`
id
name
slug
}
}
}
`
export const GET_ALL_TEAM_BOTS_QUERY = gql`
query teamBotsQuery($teamId: String!) {
teams_TeamById(teamId: $teamId) {
fb {
id
name
slug
team{
id
name
slug
}
kind
}
}
}
`
export default {
GET_ALL_TEAMS_QUERY,
GET_ALL_TEAM_BOTS_QUERY
GET_ALL_TEAMS_QUERY
};
+6 -1
View File
@@ -1,4 +1,7 @@
const types = [ 'Live', 'Competition', 'Backtest' ]
const startModes = [ 'All Months', 'One Month', 'No Time' ]
const availableMonths = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
const exchanges = [
{
@@ -7,4 +10,6 @@ const exchanges = [
}
]
export { types, exchanges }
export { types, exchanges, startModes, availableMonths }
+9
View File
@@ -72,3 +72,12 @@ export function slugify (string) {
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, '') // Trim - from end of text
}
export function getIndicatorYears () {
const localDate = new Date()
let availableYears = []
for (var i = 2015; i <= localDate.getUTCFullYear(); i++) {
availableYears[i-2015] = i
}
return availableYears
}
+280 -126
View File
@@ -9,20 +9,22 @@ import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames'
import styles from './styles'
import { types} from '../../../GraphQL/models'
import { isDefined } from '../../../utils'
import { types, startModes, availableMonths, processNames } 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
Select, FormControlLabel, Checkbox, FormHelperText
} from '@material-ui/core'
import { DateTimePicker } from 'material-ui-pickers';
import NumberFormat from 'react-number-format'
import { DateTimePicker } from 'material-ui-pickers'
import {
ChevronLeft,
ChevronRight,
} from '@material-ui/icons';
} from '@material-ui/icons'
class AddClone extends Component {
@@ -31,17 +33,23 @@ class AddClone extends Component {
let user = localStorage.getItem('user')
this.state = {
user: JSON.parse(user),
teamId:'',
botId:'',
mode:'',
selectedBot: {'id':''},
mode: '',
resumeExecution: false,
beginDatetime: DateTime.local().minus({ days: 8 }).startOf('day'),
endDatetime: DateTime.local(),
waitTime: 1,
state : '',
state: '',
stateDatetime: 0,
createDatetime: 0,
runAsTeam: false,
financialBeings:[],
// Indicator Bot
startYear: 2019,
endYear: 2019,
month: 1,
botProcessName: '',
//Error handlers
nameError: false,
@@ -49,8 +57,9 @@ class AddClone extends Component {
botError: false,
modeError: false,
isNewCloneConfirmationOpen: false,
serverResponse:'',
serverError: false
serverResponse: '',
serverError: false,
botProcessNameError: false
}
}
@@ -84,8 +93,7 @@ class AddClone extends Component {
<Typography className={classes.typography} variant='subtitle1' align='justify'>
A bot clone it's a copy of your bot running on a virtual machine.
At the moment Live clones trade by default 0.001 BTC on Poloniex.
Please select the team and bot you want to clone!
Please select from your teams and bots the one you want to clone!
</Typography>
<FormControl className={classNames(classes.form, classes.textField)} fullWidth>
@@ -94,6 +102,8 @@ class AddClone extends Component {
</InputLabel>
<Query query={teams.GET_ALL_TEAMS_QUERY} >
{({ loading, error, data }) => {
if (error) return `Error! There has been an error loading teams.`
if (loading) {
return (
<Select
@@ -106,7 +116,9 @@ class AddClone extends Component {
<MenuItem key='1' value='loading'>Loading...</MenuItem>
</Select>
)
} else if(!isDefined(data.teams_FbByTeamMember)){
}
if(!isDefined(data.teams_FbByTeamMember)){
return (
<Select
value='noteams'
@@ -118,121 +130,241 @@ class AddClone extends Component {
<MenuItem key='1' value='noteams'>You don't have any teams yet.</MenuItem>
</Select>
)
} else if (error) return `Error! There has been an error loading teams.`
} else{
this.financialBeings = data.teams_FbByTeamMember.fb
}
return (
<Select
value={this.state.teamId}
input={<Input />}
className={classes.selectEmpty}
onBlur={(e)=>this.setState({teamError:false})}
error={this.state.teamError}
onChange={e => this.setState({teamId:e.target.value})}
>
<MenuItem key={data.teams_FbByTeamMember.id}
value={data.teams_FbByTeamMember.id}>
{data.teams_FbByTeamMember.name + '/' + data.teams_FbByTeamMember.fb[0].name}
</MenuItem>
</Select>
<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_FbByTeamMember.fb.map(financialBeing => (
<MenuItem key={financialBeing.id} value={financialBeing.id}>
{data.teams_FbByTeamMember.name + '/' + financialBeing.name}
</MenuItem>
))
}
</Select>
)
}}
</Query>
<FormHelperText>{this.state.selectedBot.kind}</FormHelperText>
</FormControl>
<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
>
{types.map(option => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</TextField>
{ /* TRADER BOT */ }
{ this.state.selectedBot.kind === "TRADER1" &&
<React.Fragment>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
At the moment Live Trading Clones trade by default 0.001 BTC on Poloniex.
</Typography>
{ 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
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
>
{types.map(option => (
<MenuItem key={option} value={option}>
{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
/>
{ 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
id="waitTime"
label="Wait Time"
className={classNames(classes.form, classes.textField)}
value={this.state.waitTime}
onChange={(e)=>this.setState({waitTime:e.target.value})}
fullWidth
/>
</React.Fragment>
}
<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
/>
<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)}
/>
<TextField
id="waitTime"
label="Wait Time"
className={classNames(classes.form, classes.textField)}
value={this.state.waitTime}
onChange={(e)=>this.setState({waitTime:e.target.value})}
fullWidth
/>
</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)}
/>
<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)}
/>
<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)}
/>
</React.Fragment>
}
{ /* INDICATOR AND EXTRACTOR BOTS */ }
{ (this.state.selectedBot.kind === "TRADER"
|| this.state.selectedBot.kind === "EXTRACTOR") &&
<React.Fragment>
<TextField
label="Process Name"
className={classNames(classes.textField, classes.form)}
value={this.state.botProcessName}
onChange={(e)=> this.setState({botProcessName:e.target.value})}
onBlur={(e)=>this.setState({botProcessNameError:false})}
error={this.state.botProcessNameError}
fullWidth
/>
<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
>
{startModes.map(option => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</TextField>
{ this.state.mode === "All Months" &&
<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 === "One Month" &&
<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={option} value={index}>
{option}
</MenuItem>
))}
</TextField>
<TextField
id="endYearInput"
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 => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</TextField>
</React.Fragment>
}
</React.Fragment>
}
<div className={classes.actionButton} >
<Button
@@ -268,6 +400,17 @@ class AddClone extends Component {
);
}
setSelectedBot(botId){
if(isDefined(this.financialBeings)){
for (var i = 0; i < this.financialBeings.length; i++) {
if(this.financialBeings[i].id === botId){
this.setState({selectedBot:this.financialBeings[i]})
return
}
}
}
}
async submitForm(e){
e.preventDefault()
let error = this.validate()
@@ -289,23 +432,28 @@ class AddClone extends Component {
async createCloneOnServer(){
let variables = {
clone:{
teamId: this.state.teamId,
botId: this.state.botId,
botId: this.state.selectedBot.id,
mode: this.state.mode,
resumeExecution: this.state.resumeExecution,
runAsTeam: this.state.runAsTeam
}
}
if(this.state.mode == "Backtest"){
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
}
if(this.state.selectedBot.kind !== "TRADER"){
variables.clone.botProcessName = this.state.botProcessName
variables.clone.startYear = this.state.startYear
variables.clone.endYear = this.state.endYear
variables.clone.month = this.state.month
}
return this.props.addCloneMutation({
variables: variables
//refetchQueries: [{query: getClonesQuery}],
})
}
@@ -320,9 +468,8 @@ class AddClone extends Component {
if(!this.state.serverError)
this.setState({
teamId:'',
botId:'',
mode:'',
selectedBot: {'id':''},
mode: '',
resumeExecution: false,
beginDatetime: DateTime.local().minus({ days: 8 }).startOf('day'),
endDatetime: DateTime.local(),
@@ -331,6 +478,7 @@ class AddClone extends Component {
stateDatetime: 0,
createDatetime: 0,
runAsTeam: false,
botProcessName: '',
//Error handlers
nameError: false,
@@ -339,14 +487,15 @@ class AddClone extends Component {
modeError: false,
isNewCloneConfirmationOpen: false,
serverResponse: '',
serverError: false
serverError: false,
botProcessNameError: false
})
};
validate(){
let isError = false
if(this.state.teamId.length < 1) {
if(this.state.selectedBot.length < 1) {
isError = true
this.setState(state => ({ teamError: true }));
}
@@ -356,6 +505,11 @@ class AddClone extends Component {
this.setState(state => ({ modeError: true }));
}
if(this.state.botProcessName.length < 1) {
isError = true
this.setState(state => ({ botProcessNameError: true }));
}
return isError;
}
+1 -1
View File
@@ -65,7 +65,7 @@ class HistoryClones extends Component {
<TopBar
size='big'
title='Bot Clone History'
text="You don't have any Bot Clone yet. Once you create one you will find it here."
text="You don't have any Clone yet. Once you create one you will find it here."
backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
)
+12 -6
View File
@@ -1,11 +1,17 @@
export const LIVE = 'Live';
export const BACKTEST = 'Backtest';
export const COMPETITION = 'Competition';
export const LIVE = 'Live'
export const BACKTEST = 'Backtest'
export const COMPETITION = 'Competition'
export const ALL_MONTHS = 'All Months'
export const ONE_MONTH = 'One Month'
export const NO_TIME = 'No Time'
export const CloneModeEnum = [
LIVE,
BACKTEST,
COMPETITION
];
COMPETITION,
ALL_MONTHS,
ONE_MONTH,
NO_TIME
]
export default CloneModeEnum;
export default CloneModeEnum
-98
View File
@@ -1,98 +0,0 @@
import logger from '../config/logger'
import { toPlatformDatetime } from '../config/utils'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
// Get base Deployment config
import deploymentManifest from '../config/clone-deployment.json'
import { BACKTEST} from '../enums/CloneMode';
const platformExecutionContext = async (clone, cloneName) => {
try {
logger.info("createClone %j", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
// Make changes to base deployment config
deploymentManifest.metadata.name = cloneName
logger.debug("createClone Set basic environment configuration.")
let env = envConfig
logger.debug("createClone Configuration for team, bot and process.")
env.push({
"name": "DEV_TEAM",
"value": clone.teamId
})
env.push({
"name": "BOT",
"value": clone.botId
})
// TODO allow other types
env.push({
"name": "PROCESS",
"value": "Trading-Process"
})
// TODO allow other types
env.push({
"name": "TYPE",
"value": "Trading"
})
env.push({
"name": "RUN_AS_TEAM",
"value": clone.runAsTeam.toString()
})
env.push({
"name": "USER_LOGGED_IN",
"value": clone.userLoggedIn
})
logger.debug("createClone Configuration for the execution.")
env.push({
"name": "START_MODE",
"value": clone.mode.toLowerCase()
})
env.push({
"name": "RESUME_EXECUTION",
"value": clone.resumeExecution.toString()
})
if(clone.mode === BACKTEST){
env.push({
"name": "BEGIN_DATE_TIME",
"value": toPlatformDatetime(clone.beginDatetime)
})
env.push({
"name": "END_DATE_TIME",
"value": toPlatformDatetime(clone.endDatetime)
})
env.push({
"name": "WAIT_TIME",
"value": clone.waitTime.toString()
})
}
deploymentManifest.spec.template.spec.containers[0].env = env
// Create a new Deployment
const create = await client.apis.batch.v1.namespaces('default').jobs.post(
{ body: deploymentManifest })
logger.debug("Create: %j ", create)
// Fetch the Deployment we just created
const deployment = await client.apis.batch.v1.namespaces('default').jobs(
deploymentManifest.metadata.name).get()
logger.debug("Job: %j ", deployment)
logger.info("createClone %j succesfull", cloneName)
} catch (err) {
throw new KubernateError(err)
}
}
export default platformExecutionContext
@@ -1,98 +0,0 @@
import logger from '../config/logger'
import { toPlatformDatetime } from '../config/utils'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
// Get base Deployment config
import deploymentManifest from '../config/clone-deployment.json'
import { BACKTEST} from '../enums/CloneMode';
const platformExecutionContext = async (clone, cloneName) => {
try {
logger.info("createClone %j", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
// Make changes to base deployment config
deploymentManifest.metadata.name = cloneName
logger.debug("createClone Set basic environment configuration.")
let env = envConfig
logger.debug("createClone Configuration for team, bot and process.")
env.push({
"name": "DEV_TEAM",
"value": clone.teamId
})
env.push({
"name": "BOT",
"value": clone.botId
})
// TODO allow other types
env.push({
"name": "PROCESS",
"value": "Trading-Process"
})
// TODO allow other types
env.push({
"name": "TYPE",
"value": "Trading"
})
env.push({
"name": "RUN_AS_TEAM",
"value": clone.runAsTeam.toString()
})
env.push({
"name": "USER_LOGGED_IN",
"value": clone.userLoggedIn
})
logger.debug("createClone Configuration for the execution.")
env.push({
"name": "START_MODE",
"value": clone.mode.toLowerCase()
})
env.push({
"name": "RESUME_EXECUTION",
"value": clone.resumeExecution.toString()
})
if(clone.mode === BACKTEST){
env.push({
"name": "BEGIN_DATE_TIME",
"value": toPlatformDatetime(clone.beginDatetime)
})
env.push({
"name": "END_DATE_TIME",
"value": toPlatformDatetime(clone.endDatetime)
})
env.push({
"name": "WAIT_TIME",
"value": clone.waitTime.toString()
})
}
deploymentManifest.spec.template.spec.containers[0].env = env
// Create a new Deployment
const create = await client.apis.batch.v1.namespaces('default').jobs.post(
{ body: deploymentManifest })
logger.debug("Create: %j ", create)
// Fetch the Deployment we just created
const deployment = await client.apis.batch.v1.namespaces('default').jobs(
deploymentManifest.metadata.name).get()
logger.debug("Job: %j ", deployment)
logger.info("createClone %j succesfull", cloneName)
} catch (err) {
throw new KubernateError(err)
}
}
export default platformExecutionContext
@@ -46,7 +46,7 @@ const resolve = async(parent, { clone }, context) => {
logger.debug('addClone -> Checking existing clone %j', existingClone)
if(isDefined(existingClone) && existingClone.length > 0){
throw new CustomError('You can only create one clone per mode. Remove the'
throw new CustomError('You can only have one active clone by mode. Remove the'
+ ' existing clone of type: ' + clone.mode + ' and try again.')
}
@@ -76,8 +76,7 @@ const resolve = async(parent, { clone }, context) => {
`,
},
headers: {
authorization: context.authorization,
preshared: process.env.OPERATIONS_API_PRESHARED
authorization: context.authorization
}
})