diff --git a/Animation.js b/Animation.js index 95bf8a85d..c5a53041d 100644 --- a/Animation.js +++ b/Animation.js @@ -79,7 +79,16 @@ function newAnimation () { /* We request the next frame to be drawn, and stablishing a loop */ + /* Use this code for full animation speed. */ animationLoopHandle = window.requestAnimationFrame(animationLoop) + + /* Use this code for max 10 frames per second animation speed, if the app is consumming too much of your CPU. */ + /* + setTimeout(nextLoop, 100) + function nextLoop () { + animationLoopHandle = window.requestAnimationFrame(animationLoop) + } + */ } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] animationLoop -> err = ' + err.stack) } } diff --git a/ChartsSpace/ViewPort.js b/ChartsSpace/ViewPort.js index 358f3a1e6..40f5154f9 100644 --- a/ChartsSpace/ViewPort.js +++ b/ChartsSpace/ViewPort.js @@ -204,6 +204,10 @@ function newViewPort () { function applyZoom (amount) { // console.log("applyZoom amount: " + amount); + /* We adjust the sensitivity for Mac Users */ + let isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0 + if (isMac) { amount = amount / 5 } + if (amount > 0) { if (thisObject.zoomTargetLevel > -5) { amount = amount * 2 diff --git a/CockpitSpace/GraphQLRestartSimulation.js b/CockpitSpace/GraphQLRestartSimulation.js deleted file mode 100644 index fc42a3a66..000000000 --- a/CockpitSpace/GraphQLRestartSimulation.js +++ /dev/null @@ -1,39 +0,0 @@ - -async function graphQlRestartSimulation (simulationParams) { - try { - const accessToken = window.localStorage.getItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY) - - if (accessToken === null) { - throw new Error('Can not restart the simulation because the user is not logged in. ') - } - - const graphQLServer = await axios({ - url: window.canvasApp.graphQL.masterAppApiUrl, - method: 'post', - data: { - query: ` - mutation operations_RunSimulation( - $simulation: operations_SimulationInput) - { - operations_RunSimulation(simulation: $simulation) - } - `, - variables: { - simulation: simulationParams - } - }, - headers: { - authorization: 'Bearer ' + accessToken - } - }) - - if (graphQLServer.data.errors) { - console.log(graphQLServer.data.errors) - throw new Error(graphQLServer.data.errors[0].message) - } - } catch (err) { - console.log(err.stack) - throw new Error('There has been an error restarting the simulation. Error: ' + err.stack) - } -} - diff --git a/CockpitSpace/RestartSimulation.js b/CockpitSpace/RestartSimulation.js index 451c4f4ac..98e7eb14e 100644 --- a/CockpitSpace/RestartSimulation.js +++ b/CockpitSpace/RestartSimulation.js @@ -8,7 +8,7 @@ function newRestartSimulation () { visible: true, container: undefined, status: undefined, - restart: restart, + startOrStop: startOrStop, physics: physics, draw: draw, getContainer: getContainer, @@ -35,6 +35,9 @@ function newRestartSimulation () { let executionFocusExists = false let idleLabel = '' + let refreshCounter = 0 + let isRunning = false + let eventSubscriptionId return thisObject @@ -43,6 +46,8 @@ function newRestartSimulation () { thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfMouseNotOverEventSubscriptionId) + systemEventHandler.stopListening('Jason-Multi-Period', eventSubscriptionId) + thisObject.container.finalize() thisObject.container = undefined } @@ -56,10 +61,28 @@ function newRestartSimulation () { selfMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) thisObject.status = 'Ready' + + systemEventHandler.createEventHandler('Cockpit-Restart-Button') + + /* + We will start listening to the event that is triggered when the simulation process finishes processing one day in the case + of daily files or the whole market in the case of market files. + */ + eventSubscriptionId = systemEventHandler.listenToEvent('Jason-Multi-Period', 'Status Report Updated', undefined, undefined, undefined, onEvent) + + function onEvent (message) { + refreshCounter++ + + if (refreshCounter === 1) { + refreshCounter = 0 + turnOffProductCards() + turnOnProductCards() + } + } } function getContainer (point, purpose) { - if (thisObject.visible !== true || thisObject.status !== 'Ready' || executionFocusExists === false) { return } + if (thisObject.visible !== true || (thisObject.status !== 'Ready' && thisObject.status !== 'Calculating') || executionFocusExists === false) { return } if (thisObject.container.frame.isThisPointHere(point, true) === true) { return thisObject.container @@ -81,16 +104,19 @@ function newRestartSimulation () { } function onMouseClick (event) { - restart() + startOrStop() } - async function restart () { + async function startOrStop () { try { - thisObject.status = 'Saving' - let result = await canvas.strategySpace.strategizerGateway.saveToStrategyzer(getSimulationParams()) - if (result === true) { - if (window.canvasApp.executingAt === 'Local') { - if (idleLabel === 'RESTART LIVE TRADING') { + if (isRunning === false) { + thisObject.status = 'Saving' + let result = await canvas.strategySpace.strategizerGateway.saveToStrategyzer(getSimulationParams()) + if (result === false) { + thisObject.status = 'Error' + counterTillNextState = 500 + } else { + if (idleLabel === 'START LIVE TRADING') { callServer('', 'ResetLogsAndData', onSaved) function onSaved (err) { if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { @@ -99,24 +125,26 @@ function newRestartSimulation () { logger.write('[ERROR] Restart Simulation -> Can not delete Logs and Simulation data. err = ' + err.messsage) } } - } - callServer('', 'RestartCloneExecutor', onSaved) - function onSaved (err) { - if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { - logger.write('[INFO] Restart Simulation -> Clone Executor Restarted') - } else { - logger.write('[ERROR] Restart Simulation -> Can not restart Clone Executor. err = ' + err.messsage) + let event = { + definition: getDefinition() } + systemEventHandler.raiseEvent('Cockpit-Restart-Button', 'Live-Trading Started', event) + } else { + let event = { + definition: getDefinition() + } + systemEventHandler.raiseEvent('Cockpit-Restart-Button', 'Backstesting Started', event) } - } else { - thisObject.status = 'Restarting' - await graphQlRestartSimulation(simulationParams) + + thisObject.status = 'Calculating' + isRunning = true } - thisObject.status = 'Calculating' - counterTillNextState = 500 } else { - thisObject.status = 'Error' - counterTillNextState = 500 + /* isRunning === true */ + systemEventHandler.raiseEvent('Cockpit-Restart-Button', 'Stop Requested') + isRunning = false + thisObject.status = 'Stopping' + counterTillNextState = 200 } } catch (err) { thisObject.status = 'Error' @@ -124,6 +152,12 @@ function newRestartSimulation () { } } + function getDefinition () { + let definitionNode = canvas.strategySpace.workspace.getProtocolDefinitionNode() + definitionNode.simulationParams = getSimulationParams() + return JSON.stringify(definitionNode) + } + function turnOffProductCards () { let productCardsToTurnOff = ['Trading-Simulation', 'Simulation-Conditions', 'Simulation-Strategies', 'Simulation-Trades', 'Live Trading History'] for (let i = 0; i < canvas.panelsSpace.panels.length; i++) { @@ -162,27 +196,11 @@ function newRestartSimulation () { case 'Saving': break - case 'Restarting': + case 'Calculating': break - case 'Calculating': - thisObject.status = 'Refreshing' - counterTillNextState = 150 - break - case 'Refreshing': - thisObject.status = 'Reviewing' - turnOffProductCards() - turnOnProductCards() - counterTillNextState = 150 - break - case 'Reviewing': - thisObject.status = '2nd Refresh' - counterTillNextState = 250 - break - case '2nd Refresh': + case 'Stopping': thisObject.status = 'Ready' - turnOffProductCards() - turnOnProductCards() break case 'Error': thisObject.status = 'Ready' @@ -196,7 +214,7 @@ function newRestartSimulation () { } function labelPhysics () { - idleLabel = 'RESTART SIMULATION' + idleLabel = 'START BACKTESTING' if (canvas.strategySpace.workspace.definition) { let definition = canvas.strategySpace.workspace.definition if (definition.personalData) { @@ -206,7 +224,7 @@ function newRestartSimulation () { if (exchangeAccount.keys) { if (exchangeAccount.keys.length > 0) { let key = exchangeAccount.keys[0] - idleLabel = 'RESTART LIVE TRADING' + idleLabel = 'START LIVE TRADING' } } } @@ -256,19 +274,10 @@ function newRestartSimulation () { case 'Saving': params.backgroundColor = UI_COLOR.GREY break - case 'Restarting': - params.backgroundColor = UI_COLOR.GREY - break - case 'Refreshing': - params.backgroundColor = UI_COLOR.GOLDEN_ORANGE - break case 'Calculating': params.backgroundColor = UI_COLOR.TITANIUM_YELLOW break - case 'Reviewing': - params.backgroundColor = UI_COLOR.GREY - break - case '2nd Refresh': + case 'Stopping': params.backgroundColor = UI_COLOR.GOLDEN_ORANGE break case 'Error': @@ -300,20 +309,11 @@ function newRestartSimulation () { case 'Saving': label = 'SAVING STRATEGIES CHANGES...' break - case 'Restarting': - label = 'RESTARTING TRADING ENGINE...' - break - case 'Refreshing': - label = 'RE-LOADING...' - break case 'Calculating': - label = 'CALCULATING...' + label = 'STOP CALCULATING' break - case 'Reviewing': - label = 'WAITING TO UPDATE...' - break - case '2nd Refresh': - label = 'UPDATING...' + case 'Stopping': + label = 'STOPPING...' break case 'Error': label = 'ERROR, RETRY LATER' diff --git a/Dashboard.js b/Dashboard.js index 4811d2b5a..ac0507ce8 100644 --- a/Dashboard.js +++ b/Dashboard.js @@ -1,6 +1,7 @@ let canvas let markets let ecosystem = newEcosystem() +let systemEventHandler let viewPort try { @@ -32,29 +33,34 @@ function newDashboard () { function start () { try { - /* If this method is executed for a second time, it should finalize the current execution structure */ + systemEventHandler = newSystemEventHandler() + systemEventHandler.initialize(startCanvas) - if (canvas !== undefined) { canvas.finalize() } + function startCanvas () { + /* If this method is executed for a second time, it should finalize the current execution structure */ - /* Here we check where we are executing. In case we are Local we need to create a Local Dummy User and Team. */ + if (canvas !== undefined) { canvas.finalize() } - if (window.canvasApp.executingAt === 'Local') { - window.localStorage.setItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY, 'Local Access Token') // Dummy Access Token - window.localStorage.setItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY, '{"authId":"x|x","alias":"user"}') // Dummy Local User + /* Here we check where we are executing. In case we are Local we need to create a Local Dummy User and Team. */ + + if (window.canvasApp.executingAt === 'Local') { + window.localStorage.setItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY, 'Local Access Token') // Dummy Access Token + window.localStorage.setItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY, '{"authId":"x|x","alias":"user"}') // Dummy Local User + } + + /* Here we will setup the global eventHandler that will enable the Canvas App to react to events happening outside its execution scope. */ + + window.canvasApp.eventHandler = newEventHandler() + userProfileChangedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('User Profile Changed', userProfileChanged) + browserResizedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('Browser Resized', browserResized) + + /* Here we used to have a call to the Teams Module to get the profile pictures. That was removed but to keep things working, we do this: */ + + window.canvasApp.context.teamProfileImages = new Map() + window.canvasApp.context.fbProfileImages = new Map() + + setTimeout(delayedStart, DEBUG_START_UP_DELAY) } - - /* Here we will setup the global eventHandler that will enable the Canvas App to react to events happening outside its execution scope. */ - - window.canvasApp.eventHandler = newEventHandler() - userProfileChangedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('User Profile Changed', userProfileChanged) - browserResizedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('Browser Resized', browserResized) - - /* Here we used to have a call to the Teams Module to get the profile pictures. That was removed but to keep things working, we do this: */ - - window.canvasApp.context.teamProfileImages = new Map() - window.canvasApp.context.fbProfileImages = new Map() - - setTimeout(delayedStart, DEBUG_START_UP_DELAY) } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] start -> err = ' + err.stack) } } diff --git a/FloatingSpace/CircularProgressBar.js b/FloatingSpace/CircularProgressBar.js new file mode 100644 index 000000000..2bb3da856 --- /dev/null +++ b/FloatingSpace/CircularProgressBar.js @@ -0,0 +1,107 @@ + +function newCircularProgressBar () { + const MODULE_NAME = 'Circular Progress Bar' + + let thisObject = { + container: undefined, + isDeployed: undefined, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME, 'Circle') + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let opacityCounters = [] + + return thisObject + + function finalize () { + thisObject.container = undefined + + thisObject.payload = undefined + thisObject.fitFunction = undefined + } + + function initialize (payload) { + thisObject.payload = payload + + for (let i = 0; i < 60; i++) { + opacityCounters.push(0) + } + + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.createEventHandler(key) + systemEventHandler.listenToEvent(key, 'Heartbeat', undefined, key, undefined, onHeartBeat) + } + + function onHeartBeat (message) { + opacityCounters[message.event.seconds] = 2000 + } + + function getContainer (point) { + let container + + for (let i = 0; i < menuItems.length; i++) { + let menutItem = menuItems[i] + container = menutItem.getContainer(point) + if (container !== undefined) { return container } + } + } + + function physics () { + for (let i = 0; i < 60; i++) { + opacityCounters[i] = opacityCounters[i] - 2 + + if (opacityCounters[i] < 0) { + opacityCounters[i] = 0 + } + } + } + + function drawBackground (pFloatingObject) { + const VISIBLE_RADIUS = thisObject.container.frame.radius * 2 + + let visiblePosition = { + x: thisObject.container.frame.position.x, + y: thisObject.container.frame.position.y + } + + visiblePosition = thisObject.container.frame.frameThisPoint(visiblePosition) + visiblePosition = thisObject.fitFunction(visiblePosition) + + for (let i = 0; i < 60; i++) { + let OPACITY = opacityCounters[i] / 1000 + + let initialAngle = Math.PI * 2 / 60 * i - Math.PI / 2 + let finalAngle = Math.PI * 2 / 60 * (i + 1) - Math.PI / 2 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, initialAngle, finalAngle, false) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY * 2 + ')' + browserCanvasContext.lineWidth = 6 + opacityCounters[i] / 100 + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.stroke() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 0 + opacityCounters[i] / 100 + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.stroke() + } + } + + function drawForeground (pFloatingObject) { + + } +} diff --git a/FloatingSpace/CodeEditor.js b/FloatingSpace/CodeEditor.js index 5bfa65232..c552a55e7 100644 --- a/FloatingSpace/CodeEditor.js +++ b/FloatingSpace/CodeEditor.js @@ -133,7 +133,7 @@ function newCodeEditor () { let textAreaPosition = { x: 0 - thisObject.container.frame.width / 2, - y: 0 - thisObject.container.frame.height * 3 / 7 + y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN } textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition) diff --git a/FloatingSpace/StrategyPart.js b/FloatingSpace/StrategyPart.js index e8653f1ad..1bbd10c08 100644 --- a/FloatingSpace/StrategyPart.js +++ b/FloatingSpace/StrategyPart.js @@ -16,9 +16,12 @@ function newStrategyPart () { payload: undefined, codeEditor: undefined, partTitle: undefined, + circularProgressBar: undefined, isExecuting: undefined, isRunning: undefined, run: run, + play: play, + stop: stop, setRunningStatus: setRunningStatus, setNotRunningStatus: setNotRunningStatus, getReadyToAttach: getReadyToAttach, @@ -175,8 +178,11 @@ function newStrategyPart () { thisObject.payload.targetPosition.y = thisObject.payload.chainParent.payload.position.y } + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.physics() + } + iconPhysics() - runningPhisycs() highlightPhisycs() errorMessagePhisycs() valuePhisycs() @@ -224,6 +230,42 @@ function newStrategyPart () { compatibleType = '->' + 'Parameters' compatibleSubType = undefined break + case 'Time Range': + compatibleType = '->' + 'Parameters' + compatibleSubType = undefined + break + case 'Slippage': + compatibleType = '->' + 'Parameters' + compatibleSubType = undefined + break + case 'Fee Structure': + compatibleType = '->' + 'Parameters' + compatibleSubType = undefined + break + case 'Task Manager': + compatibleType = '->' + 'Definition' + compatibleSubType = undefined + break + case 'Task': + compatibleType = '->' + 'Task Manager' + compatibleSubType = undefined + break + case 'Sensor': + compatibleType = '->' + 'Task' + compatibleSubType = undefined + break + case 'Indicator': + compatibleType = '->' + 'Task' + compatibleSubType = undefined + break + case 'Trading Engine': + compatibleType = '->' + 'Task' + compatibleSubType = undefined + break + case 'Process': + compatibleType = '->' + 'Sensor' + '->' + 'Indicator' + '->' + 'Trading Engine' + compatibleSubType = undefined + break case 'Strategy': compatibleType = '->' + 'Trading System' compatibleSubType = undefined @@ -289,7 +331,7 @@ function newStrategyPart () { compatibleSubType = undefined break case 'Formula': - compatibleType = '->' + 'Base Asset' + '->' + 'Position Size' + '->' + 'Position Rate' + '->' + 'Phase' + compatibleType = '->' + 'Position Size' + '->' + 'Position Rate' + '->' + 'Phase' compatibleSubType = undefined break case 'Next Phase Event': @@ -322,10 +364,19 @@ function newStrategyPart () { let nearbyNode = floatingObject.payload.node if (compatibleType.indexOf('->' + nearbyNode.type) >= 0) { /* Discard objects with busy coonection ports */ + if (thisObject.payload.node.type === 'Task Manager' && nearbyNode.taskManager !== undefined) { continue } + if (thisObject.payload.node.type === 'Task' && nearbyNode.task !== undefined) { continue } + if (thisObject.payload.node.type === 'Sensor' && nearbyNode.bot !== undefined) { continue } + if (thisObject.payload.node.type === 'Indicator' && nearbyNode.bot !== undefined) { continue } + if (thisObject.payload.node.type === 'Trading Engine' && nearbyNode.bot !== undefined) { continue } + if (thisObject.payload.node.type === 'Process' && nearbyNode.process !== undefined) { continue } if (thisObject.payload.node.type === 'Trading System' && nearbyNode.tradingSystem !== undefined) { continue } if (thisObject.payload.node.type === 'Personal Data' && nearbyNode.personalData !== undefined) { continue } if (thisObject.payload.node.type === 'Parameters' && nearbyNode.parameters !== undefined) { continue } if (thisObject.payload.node.type === 'Base Asset' && nearbyNode.baseAsset !== undefined) { continue } + if (thisObject.payload.node.type === 'Time Range' && nearbyNode.timeRange !== undefined) { continue } + if (thisObject.payload.node.type === 'Slippage' && nearbyNode.slippage !== undefined) { continue } + if (thisObject.payload.node.type === 'Fee Structure' && nearbyNode.feeStructure !== undefined) { continue } if (thisObject.payload.node.type === 'Trigger Stage' && nearbyNode.triggerStage !== undefined) { continue } if (thisObject.payload.node.type === 'Open Stage' && nearbyNode.openStage !== undefined) { continue } if (thisObject.payload.node.type === 'Manage Stage' && nearbyNode.manageStage !== undefined) { continue } @@ -480,16 +531,22 @@ function newStrategyPart () { } } - function runningPhisycs () { - if (canvas.strategySpace.workspace.definition !== undefined) { - if (canvas.strategySpace.workspace.definition.id !== thisObject.payload.node.id) { - runningCounter-- - } + function play () { + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.finalize() } - if (runningCounter < 0) { - runningCounter = 0 - thisObject.isRunning = false + thisObject.circularProgressBar = newCircularProgressBar() + thisObject.circularProgressBar.initialize(thisObject.payload) + thisObject.circularProgressBar.fitFunction = thisObject.fitFunction + thisObject.circularProgressBar.container = thisObject.container + } + + function stop () { + setTimeout(removecircularProgressBar, 90000) + function removecircularProgressBar () { + thisObject.circularProgressBar.finalize() + thisObject.circularProgressBar = undefined } } @@ -559,6 +616,10 @@ function newStrategyPart () { thisObject.menu.drawBackground() } } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.drawBackground() + } } function drawMiddleground () { @@ -576,6 +637,10 @@ function newStrategyPart () { thisObject.menu.drawForeground() } } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.drawForeground() + } } function drawOnFocus () { @@ -709,14 +774,14 @@ function newStrategyPart () { position = thisObject.container.frame.frameThisPoint(position) - let radius = thisObject.container.frame.radius + let radius = thisObject.container.frame.radius * 3.5 /* Label Text */ let labelPoint let fontSize = thisObject.payload.floatingObject.currentFontSize * 3 / 4 let label if (radius > 6) { - const MAX_LABEL_LENGTH = 40 + const MAX_LABEL_LENGTH = 80 label = errorMessage @@ -875,17 +940,6 @@ function newStrategyPart () { browserCanvasContext.fill() - if (thisObject.isOnFocus === true) { - VISIBLE_RADIUS = thisObject.container.frame.radius - browserCanvasContext.beginPath() - browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS - 2, 0, Math.PI * 2, true) - browserCanvasContext.closePath() - - browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 0.25)' - - browserCanvasContext.fill() - } - if (thisObject.isRunning === true) { VISIBLE_RADIUS = thisObject.container.frame.radius * 2 let OPACITY = runningCounter / 30 @@ -901,6 +955,17 @@ function newStrategyPart () { browserCanvasContext.stroke() } + if (thisObject.isOnFocus === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS - 2, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 0.25)' + + browserCanvasContext.fill() + } + if (hasError === true) { VISIBLE_RADIUS = thisObject.container.frame.radius * 1 let OPACITY = errorMessageCounter / 30 diff --git a/FloatingSpace/StrategyPartConstructor.js b/FloatingSpace/StrategyPartConstructor.js index 0ea5e295b..04651ffc9 100644 --- a/FloatingSpace/StrategyPartConstructor.js +++ b/FloatingSpace/StrategyPartConstructor.js @@ -217,16 +217,16 @@ function newStrategyPartConstructor () { addLeftIcons(menuItemsInitialValues, floatingObject) menuItemsInitialValues.push( { - action: 'Run Trading System', + action: 'Run Definition', actionFunction: payload.uiObject.run, label: 'Run', visible: false, iconPathOn: 'paper-plane', - iconPathOff: 'menu-share', + iconPathOff: 'paper-plane', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -70 + angle: -75 } ) menuItemsInitialValues.push( @@ -246,22 +246,6 @@ function newStrategyPartConstructor () { angle: -40 } ) - menuItemsInitialValues.push( - { - action: 'Delete Definition', - askConfirmation: true, - confirmationLabel: 'Confirm to Delete', - actionFunction: payload.onMenuItemClick, - label: 'Delete Definition', - visible: false, - iconPathOn: 'delete', - iconPathOff: 'delete', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, - angle: -10 - } - ) menuItemsInitialValues.push( { action: 'Add Trading System', @@ -272,7 +256,7 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 10 + angle: -20 } ) menuItemsInitialValues.push( @@ -285,7 +269,111 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Task Manager', + actionFunction: payload.onMenuItemClick, + label: 'Add Task Manager', + visible: false, + relatedStrategyPart: 'Task Manager', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Definition', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Definition', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, angle: 40 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 75 + } + ) + break + } + case 'Task Manager': { + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Run All Tasks', + actionFunction: payload.onMenuItemClick, + label: 'Run All Tasks', + visible: false, + iconPathOn: 'paper-plane', + iconPathOff: 'paper-plane', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -60 + } + ) + menuItemsInitialValues.push( + { + action: 'Stop All Tasks', + actionFunction: payload.onMenuItemClick, + label: 'Stop All Tasks', + visible: false, + iconPathOn: 'stop', + iconPathOff: 'stop', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -20 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Task', + actionFunction: payload.onMenuItemClick, + label: 'Add Task', + visible: false, + relatedStrategyPart: 'Task Manager', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Task Manager', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Task Manager', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 } ) menuItemsInitialValues.push( @@ -299,13 +387,322 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 70 + angle: 60 + } + ) + break + } + case 'Task': { + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Run Task', + actionFunction: payload.onMenuItemClick, + label: 'Run', + visible: false, + iconPathOn: 'paper-plane', + iconPathOff: 'paper-plane', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -75 } ) + menuItemsInitialValues.push( + { + action: 'Stop Task', + actionFunction: payload.onMenuItemClick, + label: 'Stop', + visible: false, + iconPathOn: 'stop', + iconPathOff: 'stop', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Sensor', + actionFunction: payload.onMenuItemClick, + label: 'Add Sensor', + visible: false, + relatedStrategyPart: 'Sensor', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -20 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Indicator', + actionFunction: payload.onMenuItemClick, + label: 'Add Indicator', + visible: false, + relatedStrategyPart: 'Indicator', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Trading Engine', + actionFunction: payload.onMenuItemClick, + label: 'Add Trading Engine', + visible: false, + relatedStrategyPart: 'Trading Engine', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Task', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Process', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: +40 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 75 + } + ) + break + } + case 'Sensor': { + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Add Process', + actionFunction: payload.onMenuItemClick, + label: 'Add Process', + visible: false, + relatedStrategyPart: 'Process', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Sensor', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Sensor', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Indicator': { + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Add Process', + actionFunction: payload.onMenuItemClick, + label: 'Add Process', + visible: false, + relatedStrategyPart: 'Process', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Indicator', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Indicator', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Trading Engine': { + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Add Process', + actionFunction: payload.onMenuItemClick, + label: 'Add Process', + visible: false, + relatedStrategyPart: 'Process', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Trading Engine', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Trading Engine', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Process': { + strategyPart.codeEditor = newCodeEditor() + strategyPart.codeEditor.isVisibleFunction = strategyPart.isVisibleFunction + strategyPart.codeEditor.initialize() + strategyPart.codeEditor.container.connectToParent(strategyPart.container, false, false, true, true, false, false, false, false) + + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Edit Process', + actionFunction: strategyPart.codeEditor.activate, + label: 'Edit Process', + visible: false, + iconPathOn: 'html', + iconPathOff: 'html', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40, + dontShowAtFullscreen: true + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Process', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Process', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) break } case 'Personal Data': { addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Add Exchange Account', + actionFunction: payload.onMenuItemClick, + label: 'Add Exchange Account', + visible: false, + relatedStrategyPart: 'Exchange Account', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -20 + } + ) menuItemsInitialValues.push( { action: 'Delete Personal Data', @@ -319,26 +716,39 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -20 - } - ) - menuItemsInitialValues.push( - { - action: 'Add Exchange Account', - actionFunction: payload.onMenuItemClick, - label: 'Add Exchange Account', - visible: false, - relatedStrategyPart: 'Exchange Account', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, angle: +20 } - ) + ) break } case 'Exchange Account': { addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Add Exchange Account Asset', + actionFunction: payload.onMenuItemClick, + label: 'Add Asset', + visible: false, + relatedStrategyPart: 'Exchange Account Asset', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Add Exchange Account Key', + actionFunction: payload.onMenuItemClick, + label: 'Add Key', + visible: false, + relatedStrategyPart: 'Exchange Account Key', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) menuItemsInitialValues.push( { action: 'Delete Exchange Account', @@ -352,35 +762,9 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -40 - } - ) - menuItemsInitialValues.push( - { - action: 'Add Exchange Account Asset', - actionFunction: payload.onMenuItemClick, - label: 'Add Asset', - visible: false, - relatedStrategyPart: 'Exchange Account Asset', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, - angle: 0 - } - ) - menuItemsInitialValues.push( - { - action: 'Add Exchange Account Key', - actionFunction: payload.onMenuItemClick, - label: 'Add Key', - visible: false, - relatedStrategyPart: 'Exchange Account Key', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, angle: 40 } - ) + ) break } case 'Exchange Account Asset': { @@ -445,22 +829,6 @@ function newStrategyPartConstructor () { } case 'Trading System': { addLeftIcons(menuItemsInitialValues, floatingObject) - menuItemsInitialValues.push( - { - action: 'Delete Trading System', - askConfirmation: true, - confirmationLabel: 'Confirm to Delete', - actionFunction: payload.onMenuItemClick, - label: 'Delete Trading System', - visible: false, - iconPathOn: 'delete', - iconPathOff: 'delete', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, - angle: -60 - } - ) menuItemsInitialValues.push( { action: 'Add Strategy', @@ -471,7 +839,7 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -20 + angle: -60 } ) menuItemsInitialValues.push( @@ -484,9 +852,25 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 20 + angle: -20 } ) + menuItemsInitialValues.push( + { + action: 'Delete Trading System', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Trading System', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 + } + ) menuItemsInitialValues.push( { action: 'Share', @@ -507,14 +891,11 @@ function newStrategyPartConstructor () { addLeftIcons(menuItemsInitialValues, floatingObject) menuItemsInitialValues.push( { - action: 'Delete Parameters', - askConfirmation: true, - confirmationLabel: 'Confirm to Delete', + action: 'Add Missing Parameters', actionFunction: payload.onMenuItemClick, - label: 'Delete Parameters', + label: 'Add Missing Params', visible: false, - iconPathOn: 'delete', - iconPathOff: 'delete', + relatedStrategyPart: 'Parameters', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -523,11 +904,14 @@ function newStrategyPartConstructor () { ) menuItemsInitialValues.push( { - action: 'Add Missing Parameters', + action: 'Delete Parameters', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', actionFunction: payload.onMenuItemClick, - label: 'Add Missing Params', + label: 'Delete Parameters', visible: false, - relatedStrategyPart: 'Parameters', + iconPathOn: 'delete', + iconPathOff: 'delete', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -551,7 +935,26 @@ function newStrategyPartConstructor () { break } case 'Base Asset': { + strategyPart.codeEditor = newCodeEditor() + strategyPart.codeEditor.isVisibleFunction = strategyPart.isVisibleFunction + strategyPart.codeEditor.initialize() + strategyPart.codeEditor.container.connectToParent(strategyPart.container, false, false, true, true, false, false, false, false) + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Edit Base Asset', + actionFunction: strategyPart.codeEditor.activate, + label: 'Edit Base Asset', + visible: false, + iconPathOn: 'settings', + iconPathOff: 'settings', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) menuItemsInitialValues.push( { action: 'Delete Base Asset', @@ -565,16 +968,162 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Time Range': { + strategyPart.codeEditor = newCodeEditor() + strategyPart.codeEditor.isVisibleFunction = strategyPart.isVisibleFunction + strategyPart.codeEditor.initialize() + strategyPart.codeEditor.container.connectToParent(strategyPart.container, false, false, true, true, false, false, false, false) + + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Edit Time Range', + actionFunction: strategyPart.codeEditor.activate, + label: 'Edit Time Range', + visible: false, + iconPathOn: 'settings', + iconPathOff: 'settings', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, angle: -40 } ) menuItemsInitialValues.push( { - action: 'Add Formula', + action: 'Delete Time Range', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', actionFunction: payload.onMenuItemClick, - label: 'Add Formula', + label: 'Delete Time Range', visible: false, - relatedStrategyPart: 'Formula', + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Slippage': { + strategyPart.codeEditor = newCodeEditor() + strategyPart.codeEditor.isVisibleFunction = strategyPart.isVisibleFunction + strategyPart.codeEditor.initialize() + strategyPart.codeEditor.container.connectToParent(strategyPart.container, false, false, true, true, false, false, false, false) + + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Edit Slippage', + actionFunction: strategyPart.codeEditor.activate, + label: 'Edit Slippage', + visible: false, + iconPathOn: 'settings', + iconPathOff: 'settings', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Slippage', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Slippage', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 0 + } + ) + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: payload.onMenuItemClick, + label: 'Share', + visible: false, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 + } + ) + break + } + case 'Fee Structure': { + strategyPart.codeEditor = newCodeEditor() + strategyPart.codeEditor.isVisibleFunction = strategyPart.isVisibleFunction + strategyPart.codeEditor.initialize() + strategyPart.codeEditor.container.connectToParent(strategyPart.container, false, false, true, true, false, false, false, false) + + addLeftIcons(menuItemsInitialValues, floatingObject) + menuItemsInitialValues.push( + { + action: 'Edit Fee Structure', + actionFunction: strategyPart.codeEditor.activate, + label: 'Edit Fee Structure', + visible: false, + iconPathOn: 'settings', + iconPathOff: 'settings', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: -40 + } + ) + menuItemsInitialValues.push( + { + action: 'Delete Fee Structure', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', + actionFunction: payload.onMenuItemClick, + label: 'Delete Fee Structure', + visible: false, + iconPathOn: 'delete', + iconPathOff: 'delete', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -849,14 +1398,11 @@ function newStrategyPartConstructor () { addLeftIcons(menuItemsInitialValues, floatingObject) menuItemsInitialValues.push( { - action: 'Delete Position Size', - askConfirmation: true, - confirmationLabel: 'Confirm to Delete', + action: 'Add Formula', actionFunction: payload.onMenuItemClick, - label: 'Delete Position Size', + label: 'Add Formula', visible: false, - iconPathOn: 'delete', - iconPathOff: 'delete', + relatedStrategyPart: 'Formula', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -865,11 +1411,14 @@ function newStrategyPartConstructor () { ) menuItemsInitialValues.push( { - action: 'Add Formula', + action: 'Delete Position Size', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', actionFunction: payload.onMenuItemClick, - label: 'Add Formula', + label: 'Delete Position Size', visible: false, - relatedStrategyPart: 'Formula', + iconPathOn: 'delete', + iconPathOff: 'delete', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -896,14 +1445,11 @@ function newStrategyPartConstructor () { addLeftIcons(menuItemsInitialValues, floatingObject) menuItemsInitialValues.push( { - action: 'Delete Position Rate', - askConfirmation: true, - confirmationLabel: 'Confirm to Delete', + action: 'Add Formula', actionFunction: payload.onMenuItemClick, - label: 'Delete Position Rate', + label: 'Add Formula', visible: false, - iconPathOn: 'delete', - iconPathOff: 'delete', + relatedStrategyPart: 'Formula', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -912,11 +1458,14 @@ function newStrategyPartConstructor () { ) menuItemsInitialValues.push( { - action: 'Add Formula', + action: 'Delete Position Rate', + askConfirmation: true, + confirmationLabel: 'Confirm to Delete', actionFunction: payload.onMenuItemClick, - label: 'Add Formula', + label: 'Delete Position Rate', visible: false, - relatedStrategyPart: 'Formula', + iconPathOn: 'delete', + iconPathOff: 'delete', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -1619,6 +2168,30 @@ function newStrategyPartConstructor () { level_0() break } + case 'Task Manager': { + level_1() + break + } + case 'Task': { + level_1() + break + } + case 'Sensor': { + level_3() + break + } + case 'Indicator': { + level_3() + break + } + case 'Trading Engine': { + level_3() + break + } + case 'Process': { + level_4() + break + } case 'Exchange Account': { level_1() break @@ -1647,6 +2220,18 @@ function newStrategyPartConstructor () { level_3() break } + case 'Time Range': { + level_3() + break + } + case 'Slippage': { + level_3() + break + } + case 'Fee Structure': { + level_3() + break + } case 'Strategy': { level_1() break @@ -1761,7 +2346,7 @@ function newStrategyPartConstructor () { floatingObject.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' if (payload.node.savedPayload === undefined) { - floatingObject.tensionToggle() + // floatingObject.tensionToggle() } } @@ -1777,7 +2362,7 @@ function newStrategyPartConstructor () { floatingObject.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)' if (payload.node.savedPayload === undefined) { - floatingObject.tensionToggle() + // floatingObject.tensionToggle() } } @@ -1793,7 +2378,7 @@ function newStrategyPartConstructor () { floatingObject.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)' if (payload.node.savedPayload === undefined) { - floatingObject.tensionToggle() + // floatingObject.tensionToggle() } } @@ -1809,7 +2394,7 @@ function newStrategyPartConstructor () { floatingObject.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)' if (payload.node.savedPayload === undefined) { - floatingObject.tensionToggle() + // floatingObject.tensionToggle() } } diff --git a/FloatingSpace/StrategyPartTitle.js b/FloatingSpace/StrategyPartTitle.js index 7a79eaf73..4d859608d 100644 --- a/FloatingSpace/StrategyPartTitle.js +++ b/FloatingSpace/StrategyPartTitle.js @@ -52,6 +52,26 @@ function newStrategyPartTitle () { thisObject.allwaysVisible = true break } + case 'Task Manager': { + break + } + case 'Task': { + break + } + case 'Sensor': { + thisObject.allwaysVisible = true + break + } + case 'Indicator': { + thisObject.allwaysVisible = true + break + } + case 'Trading Engine': { + break + } + case 'Process': { + break + } case 'Personal Data': { break } @@ -78,6 +98,15 @@ function newStrategyPartTitle () { case 'Base Asset': { break } + case 'Time Range': { + break + } + case 'Slippage': { + break + } + case 'Fee Structure': { + break + } case 'Strategy': { thisObject.allwaysVisible = true break @@ -317,7 +346,7 @@ function newStrategyPartTitle () { function trimTitle (title) { if (title === undefined) { return } - const MAX_LABEL_LENGTH = 30 + const MAX_LABEL_LENGTH = 50 if (title.length > MAX_LABEL_LENGTH) { title = title.substring(0, MAX_LABEL_LENGTH) + '...' } diff --git a/Globals.js b/Globals.js index 564259e92..4e09972ee 100644 --- a/Globals.js +++ b/Globals.js @@ -1,3 +1,4 @@ + /* User Interface Colors */ const STORAGE_PROVIDER = 'AAWeb'; @@ -135,6 +136,7 @@ if (testUser !== null) { LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY = "access_token" } +const DEFAULT_CONFIG_TEXT = '// Write your config here' const DEFAULT_FORMULA_TEXT = '// Write your formula here' const DEFAULT_CODE_TEXT = '// Write your code here' diff --git a/Scales/TimePeriodScale.js b/Scales/TimePeriodScale.js index 25d94cf15..38ba997b4 100644 --- a/Scales/TimePeriodScale.js +++ b/Scales/TimePeriodScale.js @@ -175,12 +175,12 @@ to be visible at the top of the viewPort. */ point1 = { x: 0, - y: frame.height - frame.height / 10 + y: frame.height - frame.height * 2 / 100 } point2 = { x: frame.width, - y: frame.height - frame.height / 10 + y: frame.height - frame.height * 2 / 100 } point3 = { diff --git a/Spaces/StrategySpace.js b/Spaces/StrategySpace.js index b5c0fbfde..d940f2325 100644 --- a/Spaces/StrategySpace.js +++ b/Spaces/StrategySpace.js @@ -40,6 +40,12 @@ function newStrategySpace () { function buildIconByPartTypeMap () { const relationshipArray = [ ['Definition', 'text'], + ['Task Manager', 'task'], + ['Task', 'timeline'], + ['Sensor', 'pipette'], + ['Indicator', 'vector'], + ['Trading Engine', 'brainstorming'], + ['Process', 'grid'], ['Personal Data', 'security'], ['Exchange Account', 'approve'], ['Exchange Account Asset', 'paper-plane'], @@ -47,7 +53,10 @@ function newStrategySpace () { ['Workspace', 'design-tool'], ['Trading System', 'analysis'], ['Parameters', 'settings'], - ['Base Asset', 'sitemap'], + ['Base Asset', 'loading'], + ['Time Range', 'chronometer'], + ['Slippage', 'support'], + ['Fee Structure', 'sitemap'], ['Strategy', 'quality'], ['Trigger Stage', 'stage-trigger'], ['Open Stage', 'stage-open'], diff --git a/StrategySpace/StrategizerGateway.js b/StrategySpace/StrategizerGateway.js index 1fb458ec8..b623990b4 100644 --- a/StrategySpace/StrategizerGateway.js +++ b/StrategySpace/StrategizerGateway.js @@ -108,7 +108,7 @@ function newStrategizerGateway () { return } - let tradingSystem = canvas.strategySpace.workspace.getProtocolTradingSystem() + let tradingSystem = canvas.strategySpace.workspace.getProtocolDefinitionNode() if (tradingSystem === undefined || tradingSystem === null) { logger.write('[ERROR] saveToStrategyzer -> Can not save when tradingSystem is null or undefined.') return diff --git a/StrategySpace/Workspace/FunctionLibraries/AttachDetach.js b/StrategySpace/Workspace/FunctionLibraries/AttachDetach.js index 107539f5f..86e4d13f1 100644 --- a/StrategySpace/Workspace/FunctionLibraries/AttachDetach.js +++ b/StrategySpace/Workspace/FunctionLibraries/AttachDetach.js @@ -49,6 +49,36 @@ function newAttachDetach () { completeDetachment(node, rootNodes) return } + case 'Task Manager': { + node.payload.parentNode.taskManager = undefined + completeDetachment(node, rootNodes) + return + } + case 'Task': { + node.payload.parentNode.task = undefined + completeDetachment(node, rootNodes) + return + } + case 'Sensor': { + node.payload.parentNode.sensor = undefined + completeDetachment(node, rootNodes) + return + } + case 'Indicator': { + node.payload.parentNode.indicator = undefined + completeDetachment(node, rootNodes) + return + } + case 'Trading Engine': { + node.payload.parentNode.tradingEngine = undefined + completeDetachment(node, rootNodes) + return + } + case 'Process': { + node.payload.parentNode.process = undefined + completeDetachment(node, rootNodes) + return + } case 'Trading System': { node.payload.parentNode.tradingSystem = undefined completeDetachment(node, rootNodes) @@ -59,6 +89,21 @@ function newAttachDetach () { completeDetachment(node, rootNodes) return } + case 'Time Range': { + node.payload.parentNode.timeRange = undefined + completeDetachment(node, rootNodes) + return + } + case 'Slippage': { + node.payload.parentNode.slippage = undefined + completeDetachment(node, rootNodes) + return + } + case 'Fee Structure': { + node.payload.parentNode.feeStructure = undefined + completeDetachment(node, rootNodes) + return + } case 'Strategy': { let payload = node.payload for (let i = 0; i < payload.parentNode.strategies.length; i++) { @@ -203,6 +248,48 @@ function newAttachDetach () { function attachNode (node, attachToNode, rootNodes) { switch (node.type) { + case 'Task Manager': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.taskManager = node + completeAttachment(node, rootNodes) + } + break + case 'Task': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.tasks.push(node) + completeAttachment(node, rootNodes) + } + break + case 'Sensor': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.bot = node + completeAttachment(node, rootNodes) + } + break + case 'Indicator': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.bot = node + completeAttachment(node, rootNodes) + } + break + case 'Trading Engine': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.bot = node + completeAttachment(node, rootNodes) + } + break + case 'Process': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.processes.push(node) + completeAttachment(node, rootNodes) + } + break case 'Personal Data': { node.payload.parentNode = attachToNode node.payload.chainParent = attachToNode @@ -245,6 +332,27 @@ function newAttachDetach () { completeAttachment(node, rootNodes) } break + case 'Time Range': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.timeRange = node + completeAttachment(node, rootNodes) + } + break + case 'Slippage': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.slippage = node + completeAttachment(node, rootNodes) + } + break + case 'Fee Structure': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.feeStructure = node + completeAttachment(node, rootNodes) + } + break case 'Trading System': { node.payload.parentNode = attachToNode node.payload.chainParent = attachToNode diff --git a/StrategySpace/Workspace/FunctionLibraries/NodeChildren.js b/StrategySpace/Workspace/FunctionLibraries/NodeChildren.js index 9d57b1a87..df0a8b531 100644 --- a/StrategySpace/Workspace/FunctionLibraries/NodeChildren.js +++ b/StrategySpace/Workspace/FunctionLibraries/NodeChildren.js @@ -11,6 +11,24 @@ function newNodeChildren () { case 'Definition': { return countChildrenDefinition(parentNode, childNode) } + case 'Task Manager': { + return countChildrenTaskManager(parentNode, childNode) + } + case 'Task': { + return countChildrenTask(parentNode, childNode) + } + case 'Sensor': { + return countChildrenSensor(parentNode, childNode) + } + case 'Indicator': { + return countChildrenIndicator(parentNode, childNode) + } + case 'Trading Engine': { + return countChildrenTradingEngine(parentNode, childNode) + } + case 'Process': { + return countChildrenProcess(parentNode, childNode) + } case 'Personal Data': { return countChildrenPersonalData(parentNode, childNode) } @@ -32,6 +50,15 @@ function newNodeChildren () { case 'Base Asset': { return countChildrenBaseAsset(parentNode, childNode) } + case 'Time Range': { + return countChildrenTimeRange(parentNode, childNode) + } + case 'Slippage': { + return countChildrenSlippage(parentNode, childNode) + } + case 'Fee Structure': { + return countChildrenFeeStructure(parentNode, childNode) + } case 'Strategy': { return countChildrenStrategy(parentNode, childNode) } @@ -118,6 +145,98 @@ function newNodeChildren () { response.childIndex = response.childrenCount } } + if (parentNode.taskManager !== undefined) { + response.childrenCount++ + if (parentNode.taskManager.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenTaskManager (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + + for (let i = 0; i < parentNode.tasks.length; i++) { + let child = parentNode.tasks[i] + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenTask (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + if (parentNode.bot !== undefined) { + response.childrenCount++ + if (parentNode.bot.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenSensor (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + + for (let i = 0; i < parentNode.processes.length; i++) { + let child = parentNode.processes[i] + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenIndicator (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + + for (let i = 0; i < parentNode.processes.length; i++) { + let child = parentNode.processes[i] + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenTradingEngine (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + + for (let i = 0; i < parentNode.processes.length; i++) { + let child = parentNode.processes[i] + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + return response + } + + function countChildrenProcess (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } return response } @@ -211,6 +330,24 @@ function newNodeChildren () { response.childIndex = response.childrenCount } } + if (parentNode.timeRange !== undefined) { + response.childrenCount++ + if (parentNode.timeRange.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + if (parentNode.slippage !== undefined) { + response.childrenCount++ + if (parentNode.slippage.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + if (parentNode.feeStructure !== undefined) { + response.childrenCount++ + if (parentNode.feeStructure.id === childNode.id) { + response.childIndex = response.childrenCount + } + } return response } @@ -219,11 +356,29 @@ function newNodeChildren () { childrenCount: 0, childIndex: undefined } - if (parentNode.formula !== undefined) { - response.childrenCount++ - if (parentNode.formula.id === childNode.id) { - response.childIndex = response.childrenCount - } + return response + } + + function countChildrenTimeRange (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + return response + } + + function countChildrenSlippage (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + return response + } + + function countChildrenFeeStructure (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined } return response } @@ -536,4 +691,3 @@ function newNodeChildren () { return response } } - diff --git a/StrategySpace/Workspace/FunctionLibraries/NodeDeleter.js b/StrategySpace/Workspace/FunctionLibraries/NodeDeleter.js index e4ded4e0b..517f705bb 100644 --- a/StrategySpace/Workspace/FunctionLibraries/NodeDeleter.js +++ b/StrategySpace/Workspace/FunctionLibraries/NodeDeleter.js @@ -1,6 +1,12 @@ function newNodeDeleter () { thisObject = { deleteDefinition: deleteDefinition, + deleteTaskManager: deleteTaskManager, + deleteTask: deleteTask, + deleteSensor: deleteBot, + deleteIndicator: deleteBot, + deleteTradingEngine: deleteBot, + deleteProcess: deleteProcess, deletePersonalData: deletePersonalData, deleteExchangeAccount: deleteExchangeAccount, deleteExchangeAccountAsset: deleteExchangeAccountAsset, @@ -9,6 +15,9 @@ function newNodeDeleter () { deleteTradingSystem: deleteTradingSystem, deleteParameters: deleteParameters, deleteBaseAsset: deleteBaseAsset, + deleteTimeRange: deleteTimeRange, + deleteSlippage: deleteSlippage, + deleteFeeStructure: deleteFeeStructure, deleteStrategy: deleteStrategy, deleteTriggerStage: deleteTriggerStage, deleteOpenStage: deleteOpenStage, @@ -43,6 +52,30 @@ function newNodeDeleter () { deleteDefinition(rootNode, rootNodes, true) break } + case 'Task Manager': { + deleteTaskManager(rootNode, rootNodes) + break + } + case 'Task': { + deleteTask(rootNode, rootNodes) + break + } + case 'Sensor': { + deleteBot(rootNode, rootNodes) + break + } + case 'Indicator': { + deleteBot(rootNode, rootNodes) + break + } + case 'Trading Engine': { + deleteBot(rootNode, rootNodes) + break + } + case 'Process': { + deleteProcess(rootNode, rootNodes) + break + } case 'Personal Data': { deletePersonalData(rootNode, rootNodes) break @@ -71,6 +104,18 @@ function newNodeDeleter () { deleteBaseAsset(rootNode, rootNodes) break } + case 'Time Range': { + deleteTimeRange(rootNode, rootNodes) + break + } + case 'Slippage': { + deleteSlippage(rootNode, rootNodes) + break + } + case 'Fee Structure': { + deleteFeeStructure(rootNode, rootNodes) + break + } case 'Strategy': { deleteStrategy(rootNode, rootNodes) break @@ -176,6 +221,68 @@ function newNodeDeleter () { if (node.personalData !== undefined) { deletePersonalData(node.personalData, rootNodes) } + if (node.taskManager !== undefined) { + deleteTaskManager(node.taskManager, rootNodes) + } + completeDeletion(node, rootNodes) + destroyPart(node) + cleanNode(node) + } + + function deleteTaskManager (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + payload.parentNode.taskManager = undefined + } + while (node.tasks.length > 0) { + deleteTask(node.tasks[0], rootNodes) + } + completeDeletion(node, rootNodes) + destroyPart(node) + cleanNode(node) + } + + function deleteTask (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.tasks.length; j++) { + let task = payload.parentNode.tasks[j] + if (task.id === node.id) { + payload.parentNode.tasks.splice(j, 1) + } + } + } + if (node.bot !== undefined) { + deleteBot(node.bot, rootNodes) + } + completeDeletion(node, rootNodes) + destroyPart(node) + cleanNode(node) + } + + function deleteBot (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + payload.parentNode.bot = undefined + } + while (node.processes.length > 0) { + deleteProcess(node.processes[0], rootNodes) + } + completeDeletion(node, rootNodes) + destroyPart(node) + cleanNode(node) + } + + function deleteProcess (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.processes.length; j++) { + let task = payload.parentNode.processes[j] + if (task.id === node.id) { + payload.parentNode.processes.splice(j, 1) + } + } + } completeDeletion(node, rootNodes) destroyPart(node) cleanNode(node) @@ -280,6 +387,15 @@ function newNodeDeleter () { if (node.baseAsset !== undefined) { deleteBaseAsset(node.baseAsset, rootNodes) } + if (node.timeRange !== undefined) { + deleteTimeRange(node.timeRange, rootNodes) + } + if (node.slippage !== undefined) { + deleteSlippage(node.slippage, rootNodes) + } + if (node.feeStructure !== undefined) { + deleteFeeStructure(node.feeStructure, rootNodes) + } destroyPart(node) cleanNode(node) } @@ -291,8 +407,38 @@ function newNodeDeleter () { } else { completeDeletion(node, rootNodes) } - if (node.formula !== undefined) { - deleteFormula(node.formula, rootNodes) + destroyPart(node) + cleanNode(node) + } + + function deleteTimeRange (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + payload.parentNode.timeRange = undefined + } else { + completeDeletion(node, rootNodes) + } + destroyPart(node) + cleanNode(node) + } + + function deleteSlippage (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + payload.parentNode.slippage = undefined + } else { + completeDeletion(node, rootNodes) + } + destroyPart(node) + cleanNode(node) + } + + function deleteFeeStructure (node, rootNodes) { + let payload = node.payload + if (payload.parentNode !== undefined) { + payload.parentNode.feeStructure = undefined + } else { + completeDeletion(node, rootNodes) } destroyPart(node) cleanNode(node) diff --git a/StrategySpace/Workspace/FunctionLibraries/PartsFromNodes.js b/StrategySpace/Workspace/FunctionLibraries/PartsFromNodes.js index 5aa38b5ff..412d6160e 100644 --- a/StrategySpace/Workspace/FunctionLibraries/PartsFromNodes.js +++ b/StrategySpace/Workspace/FunctionLibraries/PartsFromNodes.js @@ -1,6 +1,12 @@ function newPartsFromNodes () { thisObject = { createPartFromNode: createPartFromNode, + addTaskManager: addTaskManager, + addTask: addTask, + addSensor: addSensor, + addIndicator: addIndicator, + addTradingEngine: addTradingEngine, + addProcess: addProcess, addTradingSystem: addTradingSystem, addPersonalData: addPersonalData, addExchangeAccount: addExchangeAccount, @@ -244,9 +250,18 @@ function newPartsFromNodes () { } case 'Base Asset': { createPart('Base Asset', node.name, node, parentNode, chainParent, 'Base Asset') - if (node.formula !== undefined) { - createPartFromNode(node.formula, node, node) - } + return + } + case 'Time Range': { + createPart('Time Range', node.name, node, parentNode, chainParent, 'Time Range') + return + } + case 'Slippage': { + createPart('Slippage', node.name, node, parentNode, chainParent, 'Slippage') + return + } + case 'Fee Structure': { + createPart('Fee Structure', node.name, node, parentNode, chainParent, 'Fee Structure') return } case 'Parameters': { @@ -254,6 +269,15 @@ function newPartsFromNodes () { if (node.baseAsset !== undefined) { createPartFromNode(node.baseAsset, node, node) } + if (node.timeRange !== undefined) { + createPartFromNode(node.timeRange, node, node) + } + if (node.slippage !== undefined) { + createPartFromNode(node.slippage, node, node) + } + if (node.feeStructure !== undefined) { + createPartFromNode(node.feeStructure, node, node) + } return } case 'Trading System': { @@ -309,11 +333,119 @@ function newPartsFromNodes () { if (node.personalData !== undefined) { createPartFromNode(node.personalData, node, node) } + if (node.taskManager !== undefined) { + createPartFromNode(node.taskManager, node, node) + } + return + } + case 'Task Manager': { + createPart('Task Manager', node.name, node, parentNode, chainParent, 'Task Manager') + for (let m = 0; m < node.tasks.length; m++) { + let task = node.tasks[m] + createPartFromNode(task, node, node) + } + return + } + case 'Task': { + createPart('Task', node.name, node, parentNode, chainParent, 'Task') + if (node.bot !== undefined) { + createPartFromNode(node.bot, node, node) + } + return + } + case 'Sensor': { + createPart('Sensor', node.name, node, parentNode, chainParent, 'Sensor') + for (let m = 0; m < node.processes.length; m++) { + let process = node.processes[m] + createPartFromNode(process, node, node) + } + return + } + case 'Indicator': { + createPart('Indicator', node.name, node, parentNode, chainParent, 'Indicator') + for (let m = 0; m < node.processes.length; m++) { + let process = node.processes[m] + createPartFromNode(process, node, node) + } + return + } + case 'Trading Engine': { + createPart('Trading Engine', node.name, node, parentNode, chainParent, 'Trading Engine') + for (let m = 0; m < node.processes.length; m++) { + let process = node.processes[m] + createPartFromNode(process, node, node) + } + return + } + case 'Process': { + createPart('Process', node.name, node, parentNode, chainParent, 'Process') return } } } + function addTaskManager (node) { + if (node.taskManager === undefined) { + node.taskManager = { + tasks: [] + } + createPart('Task Manager', '', node.taskManager, node, node) + } + + return node.taskManager + } + + function addTask (node) { + let task = { + name: 'New Task' + } + node.tasks.push(task) + createPart('Task', task.name, task, node, node, 'Task') + + return task + } + + function addSensor (node) { + if (node.bot === undefined) { + node.bot = { + processes: [] + } + createPart('Sensor', '', node.bot, node, node) + } + return node.bot + } + + function addIndicator (node) { + if (node.bot === undefined) { + node.bot = { + processes: [] + } + createPart('Indicator', '', node.bot, node, node) + } + return node.bot + } + + function addTradingEngine (node) { + if (node.bot === undefined) { + node.bot = { + processes: [] + } + createPart('Trading Engine', '', node.bot, node, node) + } + return node.bot + } + + function addProcess (node) { + let process = { + name: 'New Process', + code: '// Write the configuration here.' + } + node.processes.push(process) + createPart('Process', process.name, process, node, node, 'Process') + + return process + } + function addTradingSystem (node) { if (node.tradingSystem === undefined) { node.tradingSystem = { @@ -321,6 +453,7 @@ function newPartsFromNodes () { } createPart('Trading System', '', node.tradingSystem, node, node) } + return node.tradingSystem } function addPersonalData (node) { @@ -330,6 +463,8 @@ function newPartsFromNodes () { } createPart('Personal Data', '', node.personalData, node, node) } + + return node.personalData } function addExchangeAccount (parentNode) { @@ -341,6 +476,8 @@ function newPartsFromNodes () { } personalData.exchangeAccounts.push(exchangeAccount) createPart('Exchange Account', exchangeAccount.name, exchangeAccount, personalData, personalData, 'Exchange Account') + + return exchangeAccount } function addExchangeAccountAsset (parentNode) { @@ -350,6 +487,8 @@ function newPartsFromNodes () { } exchangeAccount.assets.push(asset) createPart('Exchange Account Asset', asset.name, asset, exchangeAccount, exchangeAccount, 'Account Asset') + + return asset } function addExchangeAccountKey (parentNode) { @@ -360,6 +499,8 @@ function newPartsFromNodes () { } exchangeAccount.keys.push(key) createPart('Exchange Account Key', key.name, key, exchangeAccount, exchangeAccount, 'Account Key') + + return key } function addStrategy (parentNode) { @@ -424,6 +565,8 @@ function newPartsFromNodes () { createPart('Stop', 'Initial Stop', strategy.openStage.initialDefinition.stopLoss, strategy.openStage.initialDefinition, strategy.openStage.initialDefinition) createPart('Take Profit', 'Initial Take Profit', strategy.openStage.initialDefinition.takeProfit, strategy.openStage.initialDefinition, strategy.openStage.initialDefinition) createPart('Formula', '', strategy.triggerStage.positionSize.formula, strategy.triggerStage.positionSize, strategy.triggerStage.positionSize) + + return strategy } function addParameters (node) { @@ -434,18 +577,38 @@ function newPartsFromNodes () { createPart('Parameters', '', node.parameters, node, node) addMissingParameters(node.parameters) } + + return node.parameters } function addMissingParameters (node) { if (node.baseAsset === undefined) { node.baseAsset = { name: 'Base Asset', - formula: { - code: DEFAULT_FORMULA_TEXT - } + code: DEFAULT_CONFIG_TEXT } createPart('Base Asset', '', node.baseAsset, node, node) - createPart('Formula', '', node.baseAsset.formula, node.baseAsset, node.baseAsset) + } + if (node.timeRange === undefined) { + node.timeRange = { + name: 'Time Range', + code: DEFAULT_CONFIG_TEXT + } + createPart('Time Range', '', node.timeRange, node, node) + } + if (node.slippage === undefined) { + node.slippage = { + name: 'Slippage', + code: DEFAULT_CONFIG_TEXT + } + createPart('Slippage', '', node.slippage, node, node) + } + if (node.feeStructure === undefined) { + node.feeStructure = { + name: 'Fee Structure', + code: DEFAULT_CONFIG_TEXT + } + createPart('Fee Structure', '', node.feeStructure, node, node) } } @@ -606,6 +769,8 @@ function newPartsFromNodes () { createPart('Position Rate', '', node.initialDefinition.positionRate, node.initialDefinition, node.initialDefinition) createPart('Formula', '', node.initialDefinition.positionRate.formula, node.initialDefinition.positionRate, node.initialDefinition.positionRate) } + + return node.initialDefinition } function addOpenExecution (node) { @@ -613,6 +778,8 @@ function newPartsFromNodes () { node.openExecution = {} createPart('Open Execution', '', node.openExecution, node, node) } + + return node.openExecution } function addCloseExecution (node) { @@ -620,6 +787,8 @@ function newPartsFromNodes () { node.closeExecution = {} createPart('Close Execution', '', node.closeExecution, node, node) } + + return node.closeExecution } function addFormula (node) { @@ -629,6 +798,8 @@ function newPartsFromNodes () { } createPart('Formula', '', node.formula, node, node) } + + return node.formula } function addNextPhaseEvent (node) { @@ -638,6 +809,8 @@ function newPartsFromNodes () { } createPart('Next Phase Event', '', node.nextPhaseEvent, node, node) } + + return node.nextPhaseEvent } function addCode (node) { @@ -647,6 +820,8 @@ function newPartsFromNodes () { } createPart('Code', '', node.code, node, node) } + + return node.code } function addPhase (parentNode) { @@ -681,6 +856,8 @@ function newPartsFromNodes () { createPart('Phase', phase.name, phase, phaseParent, phaseChainParent, 'Phase') createPart('Formula', '', phase.formula, phase, phase, 'Formula') createPart('Next Phase Event', '', phase.nextPhaseEvent, phase, phase) + + return phase } function addSituation (parentNode) { @@ -691,6 +868,8 @@ function newPartsFromNodes () { } parentNode.situations.push(situation) createPart('Situation', situation.name, situation, parentNode, parentNode, 'Situation') + + return situation } function addCondition (parentNode) { @@ -705,6 +884,8 @@ function newPartsFromNodes () { situation.conditions.push(condition) createPart('Condition', condition.name, condition, situation, situation, 'Condition') createPart('Code', '', condition.code, condition, condition, 'Code') + + return condition } function createPart (partType, name, node, parentNode, chainParent, title) { diff --git a/StrategySpace/Workspace/FunctionLibraries/ProtocolNode.js b/StrategySpace/Workspace/FunctionLibraries/ProtocolNode.js index c3eb4d943..b9a9f95f4 100644 --- a/StrategySpace/Workspace/FunctionLibraries/ProtocolNode.js +++ b/StrategySpace/Workspace/FunctionLibraries/ProtocolNode.js @@ -4,7 +4,7 @@ function newProtocolNode () { } return thisObject - function getProtocolNode (node, removePersonalData) { + function getProtocolNode (node, removePersonalData, parseJSONStrings, includeIds) { if (node === undefined) { return } switch (node.type) { case 'Code': @@ -23,7 +23,7 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - code: getProtocolNode(node.code, removePersonalData) + code: getProtocolNode(node.code, removePersonalData, parseJSONStrings, includeIds) } return condition } @@ -36,7 +36,7 @@ function newProtocolNode () { } for (let m = 0; m < node.conditions.length; m++) { - let condition = getProtocolNode(node.conditions[m], removePersonalData) + let condition = getProtocolNode(node.conditions[m], removePersonalData, parseJSONStrings, includeIds) situation.conditions.push(condition) } return situation @@ -60,7 +60,7 @@ function newProtocolNode () { situations: [] } for (let m = 0; m < node.situations.length; m++) { - let situation = getProtocolNode(node.situations[m], removePersonalData) + let situation = getProtocolNode(node.situations[m], removePersonalData, parseJSONStrings, includeIds) event.situations.push(situation) } return event @@ -70,8 +70,8 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - formula: getProtocolNode(node.formula, removePersonalData), - nextPhaseEvent: getProtocolNode(node.nextPhaseEvent, removePersonalData) + formula: getProtocolNode(node.formula, removePersonalData, parseJSONStrings, includeIds), + nextPhaseEvent: getProtocolNode(node.nextPhaseEvent, removePersonalData, parseJSONStrings, includeIds) } return phase } @@ -84,7 +84,7 @@ function newProtocolNode () { } for (let m = 0; m < node.phases.length; m++) { - let phase = getProtocolNode(node.phases[m], removePersonalData) + let phase = getProtocolNode(node.phases[m], removePersonalData, parseJSONStrings, includeIds) stop.phases.push(phase) } return stop @@ -98,7 +98,7 @@ function newProtocolNode () { } for (let m = 0; m < node.phases.length; m++) { - let phase = getProtocolNode(node.phases[m], removePersonalData) + let phase = getProtocolNode(node.phases[m], removePersonalData, parseJSONStrings, includeIds) takeProfit.phases.push(phase) } return takeProfit @@ -112,7 +112,7 @@ function newProtocolNode () { } for (let m = 0; m < node.situations.length; m++) { - let situation = getProtocolNode(node.situations[m], removePersonalData) + let situation = getProtocolNode(node.situations[m], removePersonalData, parseJSONStrings, includeIds) event.situations.push(situation) } return event @@ -126,7 +126,7 @@ function newProtocolNode () { } for (let m = 0; m < node.situations.length; m++) { - let situation = getProtocolNode(node.situations[m], removePersonalData) + let situation = getProtocolNode(node.situations[m], removePersonalData, parseJSONStrings, includeIds) event.situations.push(situation) } return event @@ -140,7 +140,7 @@ function newProtocolNode () { } for (let m = 0; m < node.situations.length; m++) { - let situation = getProtocolNode(node.situations[m], removePersonalData) + let situation = getProtocolNode(node.situations[m], removePersonalData, parseJSONStrings, includeIds) event.situations.push(situation) } return event @@ -150,10 +150,10 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - stopLoss: getProtocolNode(node.stopLoss, removePersonalData), - takeProfit: getProtocolNode(node.takeProfit, removePersonalData), - positionSize: getProtocolNode(node.positionSize, removePersonalData), - positionRate: getProtocolNode(node.positionRate, removePersonalData) + stopLoss: getProtocolNode(node.stopLoss, removePersonalData, parseJSONStrings, includeIds), + takeProfit: getProtocolNode(node.takeProfit, removePersonalData, parseJSONStrings, includeIds), + positionSize: getProtocolNode(node.positionSize, removePersonalData, parseJSONStrings, includeIds), + positionRate: getProtocolNode(node.positionRate, removePersonalData, parseJSONStrings, includeIds) } return object } @@ -178,7 +178,7 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - formula: getProtocolNode(node.formula, removePersonalData) + formula: getProtocolNode(node.formula, removePersonalData, parseJSONStrings, includeIds) } return object } @@ -187,7 +187,7 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - formula: getProtocolNode(node.formula, removePersonalData) + formula: getProtocolNode(node.formula, removePersonalData, parseJSONStrings, includeIds) } return object } @@ -196,9 +196,9 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - triggerOn: getProtocolNode(node.triggerOn, removePersonalData), - triggerOff: getProtocolNode(node.triggerOff, removePersonalData), - takePosition: getProtocolNode(node.takePosition, removePersonalData) + triggerOn: getProtocolNode(node.triggerOn, removePersonalData, parseJSONStrings, includeIds), + triggerOff: getProtocolNode(node.triggerOff, removePersonalData, parseJSONStrings, includeIds), + takePosition: getProtocolNode(node.takePosition, removePersonalData, parseJSONStrings, includeIds) } return stage } @@ -207,8 +207,8 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - initialDefinition: getProtocolNode(node.initialDefinition, removePersonalData), - openExecution: getProtocolNode(node.openExecution, removePersonalData) + initialDefinition: getProtocolNode(node.initialDefinition, removePersonalData, parseJSONStrings, includeIds), + openExecution: getProtocolNode(node.openExecution, removePersonalData, parseJSONStrings, includeIds) } return stage } @@ -217,8 +217,8 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - stopLoss: getProtocolNode(node.stopLoss, removePersonalData), - takeProfit: getProtocolNode(node.takeProfit, removePersonalData) + stopLoss: getProtocolNode(node.stopLoss, removePersonalData, parseJSONStrings, includeIds), + takeProfit: getProtocolNode(node.takeProfit, removePersonalData, parseJSONStrings, includeIds) } return stage } @@ -227,7 +227,7 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - closeExecution: getProtocolNode(node.closeExecution, removePersonalData) + closeExecution: getProtocolNode(node.closeExecution, removePersonalData, parseJSONStrings, includeIds) } return stage } @@ -236,10 +236,10 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - triggerStage: getProtocolNode(node.triggerStage, removePersonalData), - openStage: getProtocolNode(node.openStage, removePersonalData), - manageStage: getProtocolNode(node.manageStage, removePersonalData), - closeStage: getProtocolNode(node.closeStage, removePersonalData) + triggerStage: getProtocolNode(node.triggerStage, removePersonalData, parseJSONStrings, includeIds), + openStage: getProtocolNode(node.openStage, removePersonalData, parseJSONStrings, includeIds), + manageStage: getProtocolNode(node.manageStage, removePersonalData, parseJSONStrings, includeIds), + closeStage: getProtocolNode(node.closeStage, removePersonalData, parseJSONStrings, includeIds) } return strategy } @@ -248,7 +248,34 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - formula: getProtocolNode(node.formula, removePersonalData) + code: node.code + } + return object + } + case 'Time Range': { + let object = { + type: node.type, + subType: node.subType, + name: node.name, + code: node.code + } + return object + } + case 'Slippage': { + let object = { + type: node.type, + subType: node.subType, + name: node.name, + code: node.code + } + return object + } + case 'Fee Structure': { + let object = { + type: node.type, + subType: node.subType, + name: node.name, + code: node.code } return object } @@ -257,7 +284,10 @@ function newProtocolNode () { type: node.type, subType: node.subType, name: node.name, - baseAsset: getProtocolNode(node.baseAsset, removePersonalData) + baseAsset: getProtocolNode(node.baseAsset, removePersonalData, parseJSONStrings, includeIds), + timeRange: getProtocolNode(node.timeRange, removePersonalData, parseJSONStrings, includeIds), + slippage: getProtocolNode(node.slippage, removePersonalData, parseJSONStrings, includeIds), + feeStructure: getProtocolNode(node.feeStructure, removePersonalData, parseJSONStrings, includeIds) } return object } @@ -267,11 +297,11 @@ function newProtocolNode () { subType: node.subType, name: node.name, strategies: [], - parameters: getProtocolNode(node.parameters, removePersonalData) + parameters: getProtocolNode(node.parameters, removePersonalData, parseJSONStrings, includeIds) } for (let m = 0; m < node.strategies.length; m++) { - let strategy = getProtocolNode(node.strategies[m], removePersonalData) + let strategy = getProtocolNode(node.strategies[m], removePersonalData, parseJSONStrings, includeIds) tradingSystem.strategies.push(strategy) } return tradingSystem @@ -286,7 +316,7 @@ function newProtocolNode () { } for (let m = 0; m < node.exchangeAccounts.length; m++) { - let exchangeAccount = getProtocolNode(node.exchangeAccounts[m], removePersonalData) + let exchangeAccount = getProtocolNode(node.exchangeAccounts[m], removePersonalData, parseJSONStrings, includeIds) personalData.exchangeAccounts.push(exchangeAccount) } return personalData @@ -302,11 +332,11 @@ function newProtocolNode () { } for (let m = 0; m < node.assets.length; m++) { - let asset = getProtocolNode(node.assets[m], removePersonalData) + let asset = getProtocolNode(node.assets[m], removePersonalData, parseJSONStrings, includeIds) exchangeAccount.assets.push(asset) } for (let m = 0; m < node.keys.length; m++) { - let key = getProtocolNode(node.keys[m], removePersonalData) + let key = getProtocolNode(node.keys[m], removePersonalData, parseJSONStrings, includeIds) exchangeAccount.keys.push(key) } return exchangeAccount @@ -330,13 +360,97 @@ function newProtocolNode () { } return key } + case 'Task Manager': { + let taskManager = { + type: node.type, + subType: node.subType, + name: node.name, + tasks: [] + } + + for (let m = 0; m < node.tasks.length; m++) { + let task = getProtocolNode(node.tasks[m], removePersonalData, parseJSONStrings, includeIds) + taskManager.tasks.push(task) + } + return taskManager + } + case 'Task': { + let object = { + type: node.type, + subType: node.subType, + name: node.name, + bot: getProtocolNode(node.bot, removePersonalData, parseJSONStrings, includeIds) + } + if (includeIds) { + object.id = node.id + } + return object + } + case 'Sensor': { + let bot = { + type: node.type, + subType: node.subType, + name: node.name, + processes: [] + } + + for (let m = 0; m < node.processes.length; m++) { + let process = getProtocolNode(node.processes[m], removePersonalData, parseJSONStrings, includeIds) + bot.processes.push(process) + } + return bot + } + case 'Indicator': { + let bot = { + type: node.type, + subType: node.subType, + name: node.name, + processes: [] + } + + for (let m = 0; m < node.processes.length; m++) { + let process = getProtocolNode(node.processes[m], removePersonalData, parseJSONStrings, includeIds) + bot.processes.push(process) + } + return bot + } + case 'Trading Engine': { + let bot = { + type: node.type, + subType: node.subType, + name: node.name, + processes: [] + } + + for (let m = 0; m < node.processes.length; m++) { + let process = getProtocolNode(node.processes[m], removePersonalData, parseJSONStrings, includeIds) + bot.processes.push(process) + } + return bot + } + case 'Process': { + let object = { + type: node.type, + subType: node.subType, + name: node.name, + code: node.code + } + if (parseJSONStrings) { + object.code = JSON.parse(object.code) + } + if (includeIds) { + object.id = node.id + } + return object + } case 'Definition': { let object = { type: node.type, subType: node.subType, name: node.name, - tradingSystem: getProtocolNode(node.tradingSystem, removePersonalData), - personalData: getProtocolNode(node.personalData, removePersonalData) + tradingSystem: getProtocolNode(node.tradingSystem, removePersonalData, parseJSONStrings, includeIds), + personalData: getProtocolNode(node.personalData, removePersonalData, parseJSONStrings, includeIds), + taskManager: getProtocolNode(node.taskManager, removePersonalData, parseJSONStrings, includeIds) } return object } diff --git a/StrategySpace/Workspace/FunctionLibraries/StringifyNode.js b/StrategySpace/Workspace/FunctionLibraries/StringifyNode.js index 4191cacd0..79a4ed6de 100644 --- a/StrategySpace/Workspace/FunctionLibraries/StringifyNode.js +++ b/StrategySpace/Workspace/FunctionLibraries/StringifyNode.js @@ -293,7 +293,40 @@ function newStringifyNode () { type: node.type, subType: node.subType, name: node.name, - formula: prepareForStringify(node.formula, removePersonalData), + code: node.code, + savedPayload: getSavedPayload(node) + } + return object + } + case 'Time Range': { + let object = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + code: node.code, + savedPayload: getSavedPayload(node) + } + return object + } + case 'Slippage': { + let object = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + code: node.code, + savedPayload: getSavedPayload(node) + } + return object + } + case 'Fee Structure': { + let object = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + code: node.code, savedPayload: getSavedPayload(node) } return object @@ -305,6 +338,9 @@ function newStringifyNode () { subType: node.subType, name: node.name, baseAsset: prepareForStringify(node.baseAsset, removePersonalData), + timeRange: prepareForStringify(node.timeRange, removePersonalData), + slippage: prepareForStringify(node.slippage, removePersonalData), + feeStructure: prepareForStringify(node.feeStructure, removePersonalData), savedPayload: getSavedPayload(node) } return object @@ -389,6 +425,96 @@ function newStringifyNode () { } return key } + case 'Task Manager': { + let taskManager = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + tasks: [], + savedPayload: getSavedPayload(node) + } + + for (let m = 0; m < node.tasks.length; m++) { + let task = prepareForStringify(node.tasks[m], removePersonalData) + taskManager.tasks.push(task) + } + + return taskManager + } + case 'Task': { + let object = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + bot: prepareForStringify(node.bot, removePersonalData), + savedPayload: getSavedPayload(node) + } + return object + } + case 'Sensor': { + let bot = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + processes: [], + savedPayload: getSavedPayload(node) + } + + for (let m = 0; m < node.processes.length; m++) { + let process = prepareForStringify(node.processes[m], removePersonalData) + bot.processes.push(process) + } + + return bot + } + case 'Indicator': { + let bot = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + processes: [], + savedPayload: getSavedPayload(node) + } + + for (let m = 0; m < node.processes.length; m++) { + let process = prepareForStringify(node.processes[m], removePersonalData) + bot.processes.push(process) + } + + return bot + } + case 'Trading Engine': { + let bot = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + processes: [], + savedPayload: getSavedPayload(node) + } + + for (let m = 0; m < node.processes.length; m++) { + let process = prepareForStringify(node.processes[m], removePersonalData) + bot.processes.push(process) + } + + return bot + } + case 'Process': { + let object = { + id: node.id, + type: node.type, + subType: node.subType, + name: node.name, + code: node.code, + savedPayload: getSavedPayload(node) + } + return object + } case 'Definition': { let object = { id: node.id, @@ -397,6 +523,7 @@ function newStringifyNode () { name: node.name, tradingSystem: prepareForStringify(node.tradingSystem, removePersonalData), personalData: prepareForStringify(node.personalData, removePersonalData), + taskManager: prepareForStringify(node.taskManager, removePersonalData), savedPayload: getSavedPayload(node) } return object diff --git a/StrategySpace/Workspace/FunctionLibraries/TaskFunctions.js b/StrategySpace/Workspace/FunctionLibraries/TaskFunctions.js new file mode 100644 index 000000000..df729a1c3 --- /dev/null +++ b/StrategySpace/Workspace/FunctionLibraries/TaskFunctions.js @@ -0,0 +1,62 @@ +function newTaskFunctions () { + thisObject = { + runTask: runTask, + stopTask: stopTask, + runAllTasks: runAllTasks, + stopAllTasks: stopAllTasks + } + + return thisObject + + function runTask (node, functionLibraryProtocolNode) { + /* Check if it is possible to Run or not */ + if (node.bot === undefined) { return } + if (node.bot.processes.length === 0) { return } + + for (let i = 0; i < node.bot.processes.length; i++) { + let process = node.bot.processes[i] + process.payload.uiObject.play() + } + + node.payload.uiObject.play() + + let event = { + taskId: node.id, + taskName: node.name, + definition: JSON.stringify(functionLibraryProtocolNode.getProtocolNode(node, false, true, true)) // <- We need to do this workaround in order no to send unescaped charactars to the taskManager. + } + systemEventHandler.raiseEvent('Task Manager', 'Run Task', event) + } + + function stopTask (node, functionLibraryProtocolNode) { + let event = { + taskId: node.id, + taskName: node.name + } + systemEventHandler.raiseEvent('Task Manager', 'Stop Task', event) + + node.payload.uiObject.stop() + + if (node.bot === undefined) { return } + if (node.bot.processes.length === 0) { return } + + for (let i = 0; i < node.bot.processes.length; i++) { + let process = node.bot.processes[i] + process.payload.uiObject.stop() + } + } + + function runAllTasks (taskManager, functionLibraryProtocolNode) { + for (let i = 0; i < taskManager.tasks.length; i++) { + let node = taskManager.tasks[i] + runTask(node, functionLibraryProtocolNode) + } + } + + function stopAllTasks (taskManager, functionLibraryProtocolNode) { + for (let i = 0; i < taskManager.tasks.length; i++) { + let node = taskManager.tasks[i] + stopTask(node, functionLibraryProtocolNode) + } + } +} diff --git a/StrategySpace/Workspace/Workspace.js b/StrategySpace/Workspace/Workspace.js index c7840da06..dae445530 100644 --- a/StrategySpace/Workspace/Workspace.js +++ b/StrategySpace/Workspace/Workspace.js @@ -1,6 +1,9 @@ function newWorkspace () { const MODULE_NAME = 'Workspace' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME let thisObject = { tradingSystem: undefined, @@ -8,7 +11,7 @@ function newWorkspace () { enabled: false, nodeChildren: undefined, onMenuItemClick: onMenuItemClick, - getProtocolTradingSystem: getProtocolTradingSystem, + getProtocolDefinitionNode: getProtocolDefinitionNode, physics: physics, spawn: spawn, detachNode: detachNode, @@ -42,6 +45,7 @@ function newWorkspace () { let functionLibraryPartsFromNodes = newPartsFromNodes() let functionLibraryProtocolNode = newProtocolNode() let functionLibraryWorkspaceNodes = newStringifyNode() + let functionLibraryTaskFunctions = newTaskFunctions() thisObject.nodeChildren = newNodeChildren() return thisObject @@ -53,29 +57,33 @@ function newWorkspace () { } async function initialize () { - let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) - if (user === null) { - return - } - user = JSON.parse(user) - - let idAtStrategizer = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Strategizer Gateway' + '.' + user.alias) - let savedWorkspace = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Workspace' + '.' + user.alias) - - if (savedWorkspace === null || idAtStrategizer === null) { - workspaceNode.type = 'Workspace' - workspaceNode.name = 'My Workspace' - functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) - spawnPosition.y = spawnPosition.y + 250 - initializeLoadingFromStrategizer() - } else { - workspaceNode = JSON.parse(savedWorkspace) - functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) - for (let i = 0; i < workspaceNode.rootNodes.length; i++) { - let rootNode = workspaceNode.rootNodes[i] - functionLibraryPartsFromNodes.createPartFromNode(rootNode, undefined, undefined) + try { + let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) + if (user === null) { + return } - thisObject.enabled = true + user = JSON.parse(user) + + let idAtStrategizer = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Strategizer Gateway' + '.' + user.alias) + let savedWorkspace = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Workspace' + '.' + user.alias) + + if (savedWorkspace === null || idAtStrategizer === null) { + workspaceNode.type = 'Workspace' + workspaceNode.name = 'My Workspace' + functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) + spawnPosition.y = spawnPosition.y + 250 + initializeLoadingFromStrategizer() + } else { + workspaceNode = JSON.parse(savedWorkspace) + functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) + for (let i = 0; i < workspaceNode.rootNodes.length; i++) { + let rootNode = workspaceNode.rootNodes[i] + functionLibraryPartsFromNodes.createPartFromNode(rootNode, undefined, undefined) + } + thisObject.enabled = true + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } } } @@ -90,8 +98,8 @@ function newWorkspace () { } } - function getProtocolTradingSystem () { - return functionLibraryProtocolNode.getProtocolNode(thisObject.definition) + function getProtocolDefinitionNode () { + return functionLibraryProtocolNode.getProtocolNode(thisObject.definition, false, true, true) // <- We need to do this workaround in order no to send unescaped charactars through a system event. } function detachNode (node) { @@ -119,8 +127,10 @@ function newWorkspace () { let stringifyReadyNodes = [] for (let i = 0; i < workspaceNode.rootNodes.length; i++) { let rootNode = workspaceNode.rootNodes[i] - let workspace = functionLibraryWorkspaceNodes.prepareForStringify(rootNode, removePersonalData) - stringifyReadyNodes.push(workspace) + let node = functionLibraryWorkspaceNodes.prepareForStringify(rootNode, removePersonalData) + if (node) { + stringifyReadyNodes.push(node) + } } let workspace = { type: 'Workspace', @@ -132,24 +142,28 @@ function newWorkspace () { } function spawn (nodeText, point) { - point = canvas.floatingSpace.container.frame.unframeThisPoint(point) - spawnPosition.x = point.x - spawnPosition.y = point.y + try { + point = canvas.floatingSpace.container.frame.unframeThisPoint(point) + spawnPosition.x = point.x + spawnPosition.y = point.y - let droppedNode = JSON.parse(nodeText) + let droppedNode = JSON.parse(nodeText) - if (droppedNode.type === 'Workspace') { - functionLibraryNodeDeleter.deleteWorkspace(workspaceNode, workspaceNode.rootNodes) - workspaceNode = droppedNode - functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) - for (let i = 0; i < workspaceNode.rootNodes.length; i++) { - let rootNode = workspaceNode.rootNodes[i] + if (droppedNode.type === 'Workspace') { + functionLibraryNodeDeleter.deleteWorkspace(workspaceNode, workspaceNode.rootNodes) + workspaceNode = droppedNode + functionLibraryPartsFromNodes.createPartFromNode(workspaceNode, undefined, undefined) + for (let i = 0; i < workspaceNode.rootNodes.length; i++) { + let rootNode = workspaceNode.rootNodes[i] + functionLibraryPartsFromNodes.createPartFromNode(rootNode, undefined, undefined) + } + } else { + let rootNode = functionLibraryProtocolNode.getProtocolNode(droppedNode) + workspaceNode.rootNodes.push(rootNode) functionLibraryPartsFromNodes.createPartFromNode(rootNode, undefined, undefined) } - } else { - let rootNode = functionLibraryProtocolNode.getProtocolNode(droppedNode) - workspaceNode.rootNodes.push(rootNode) - functionLibraryPartsFromNodes.createPartFromNode(rootNode, undefined, undefined) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] spawn -> err = ' + err.stack) } } } @@ -206,6 +220,56 @@ function newWorkspace () { download(fileName, text) } + break + case 'Run Task': + { + functionLibraryTaskFunctions.runTask(payload.node, functionLibraryProtocolNode) + } + break + case 'Stop Task': + { + functionLibraryTaskFunctions.stopTask(payload.node, functionLibraryProtocolNode) + } + break + case 'Run All Tasks': + { + functionLibraryTaskFunctions.runAllTasks(payload.node, functionLibraryProtocolNode) + } + break + case 'Stop All Tasks': + { + functionLibraryTaskFunctions.stopAllTasks(payload.node, functionLibraryProtocolNode) + } + break + case 'Add Task Manager': + { + functionLibraryPartsFromNodes.addTaskManager(payload.node) + } + break + case 'Add Task': + { + functionLibraryPartsFromNodes.addTask(payload.node) + } + break + case 'Add Sensor': + { + functionLibraryPartsFromNodes.addSensor(payload.node) + } + break + case 'Add Indicator': + { + functionLibraryPartsFromNodes.addIndicator(payload.node) + } + break + case 'Add Trading Engine': + { + functionLibraryPartsFromNodes.addTradingEngine(payload.node) + } + break + case 'Add Process': + { + functionLibraryPartsFromNodes.addProcess(payload.node) + } break case 'Add Strategy': { @@ -307,6 +371,30 @@ function newWorkspace () { functionLibraryPartsFromNodes.addPersonalData(payload.node) } break + case 'Delete Task Manager': { + functionLibraryNodeDeleter.deleteTaskManager(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Task': { + functionLibraryNodeDeleter.deleteTask(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Sensor': { + functionLibraryNodeDeleter.deleteSensor(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Indicator': { + functionLibraryNodeDeleter.deleteIndicator(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Trading Engine': { + functionLibraryNodeDeleter.deleteTradingEngine(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Process': { + functionLibraryNodeDeleter.deleteProcess(payload.node, workspaceNode.rootNodes) + break + } case 'Delete Trading System': { functionLibraryNodeDeleter.deleteTradingSystem(payload.node, workspaceNode.rootNodes) break @@ -319,6 +407,18 @@ function newWorkspace () { functionLibraryNodeDeleter.deleteBaseAsset(payload.node, workspaceNode.rootNodes) break } + case 'Delete Time Range': { + functionLibraryNodeDeleter.deleteTimeRange(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Slippage': { + functionLibraryNodeDeleter.deleteSlippage(payload.node, workspaceNode.rootNodes) + break + } + case 'Delete Fee Structure': { + functionLibraryNodeDeleter.deleteFeeStructure(payload.node, workspaceNode.rootNodes) + break + } case 'Delete Strategy': { functionLibraryNodeDeleter.deleteStrategy(payload.node, workspaceNode.rootNodes) break diff --git a/SystemEventHandler.js b/SystemEventHandler.js new file mode 100644 index 000000000..072fa880b --- /dev/null +++ b/SystemEventHandler.js @@ -0,0 +1,129 @@ + +function newSystemEventHandler () { + /* Web Sockets Connection */ + + const MODULE_NAME = 'System Event Handler' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + const WEB_SOCKETS_URL = 'ws://localhost:8080' + let WEB_SOCKETS_CONNECTION + + let thisObject = { + initialize: initialize, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + return thisObject + + function initialize (callBackFunction) { + setuptWebSockets(callBackFunction) + } + + function sendCommand (command, responseCallBack, eventsCallBack) { + if (command.action === 'listenToEvent') { + eventListeners.set(command.eventHandlerName + '-' + command.eventType, eventsCallBack) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + WEB_SOCKETS_CONNECTION.send(JSON.stringify(command)) + } + + function createEventHandler (eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler (eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent (eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening (eventHandlerName, eventSubscriptionId, callerId, responseCallBack) { + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent (eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function setuptWebSockets (callBackFunction) { + try { + WEB_SOCKETS_CONNECTION = new WebSocket(WEB_SOCKETS_URL) + WEB_SOCKETS_CONNECTION.onopen = () => { + console.log('Websocket connection opened.') + } + WEB_SOCKETS_CONNECTION.onerror = error => { + console.log('WebSocket error:' + JSON.stringify(error)) + } + WEB_SOCKETS_CONNECTION.onopen = () => { + callBackFunction() + } + WEB_SOCKETS_CONNECTION.onmessage = e => { + // console.log('Websoked Message Received: ' + e.data) + + let message = JSON.parse(e.data) + + if (message.action === 'Event Raised') { + let handler = eventListeners.get(message.eventHandlerName + '-' + message.eventType) + if (handler) { + handler(message) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setuptWebSockets -> err = ' + err.stack) } + } + } +} diff --git a/Utilities.js b/Utilities.js index 17f0dba24..1639bdb62 100644 --- a/Utilities.js +++ b/Utilities.js @@ -7,25 +7,9 @@ function getSimulationParams () { let timePeriodArray = timePeriodsMasterArray[currentTimePeriod.filePeriodIndex] let timePeriod = timePeriodArray[currentTimePeriod.timePeriodIndex][1] - let timePeriodDailyArray = [] - let timePeriodMarketArray = [] - - switch (currentTimePeriod.filePeriodIndex) { - case 0: { - timePeriodMarketArray.push(timePeriod) - break - } - case 1: { - timePeriodDailyArray.push(timePeriod) - break - } - } - let simulationParams = { beginDatetime: dateAtScreenCorner.valueOf(), - resumeExecution: false, - timePeriodDailyArray: timePeriodDailyArray, - timePeriodMarketArray: timePeriodMarketArray, + timePeriod: timePeriod, timestamp: (new Date()).valueOf() }