From 35b852ff2a020f4264b5564695c6903999c8c27f Mon Sep 17 00:00:00 2001 From: Philip Love Date: Thu, 13 May 2021 14:48:10 -0400 Subject: [PATCH] Add: User-Defined-Code for Stages --- .../Schemas/App-Schema/close-stage.json | 6 ++ .../Schemas/App-Schema/javascript-code.json | 2 +- .../Schemas/App-Schema/manage-stage.json | 6 ++ .../Schemas/App-Schema/open-stage.json | 6 ++ .../Schemas/App-Schema/trigger-stage.json | 6 ++ .../Schemas/App-Schema/user-defined-code.json | 55 +++++++++++++ .../Low-Frequency-Trading/TradingStages.js | 81 ++++++++++++++++++- .../Low-Frequency-Trading/TradingSystem.js | 59 +++++++++++--- .../TradingSessionFunctions.js | 6 +- 9 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 Projects/Superalgos/Schemas/App-Schema/user-defined-code.json diff --git a/Projects/Superalgos/Schemas/App-Schema/close-stage.json b/Projects/Superalgos/Schemas/App-Schema/close-stage.json index 6b83b2b9d..2a976b11d 100644 --- a/Projects/Superalgos/Schemas/App-Schema/close-stage.json +++ b/Projects/Superalgos/Schemas/App-Schema/close-stage.json @@ -64,6 +64,12 @@ "name": "announcements", "type": "array", "childType": "Announcement" + }, + { + "name": "userDefinedCode", + "type": "node", + "childType": "User Defined Code", + "autoAdd": false } ], "propertyNameAtParent": "closeStage" diff --git a/Projects/Superalgos/Schemas/App-Schema/javascript-code.json b/Projects/Superalgos/Schemas/App-Schema/javascript-code.json index 979991812..b80ffe9f0 100644 --- a/Projects/Superalgos/Schemas/App-Schema/javascript-code.json +++ b/Projects/Superalgos/Schemas/App-Schema/javascript-code.json @@ -9,7 +9,7 @@ "code": "// Type you code" }, "attachingRules": { - "compatibleTypes": "->Condition->" + "compatibleTypes": "->Condition->User Defined Code->" }, "menuItems": [ { diff --git a/Projects/Superalgos/Schemas/App-Schema/manage-stage.json b/Projects/Superalgos/Schemas/App-Schema/manage-stage.json index 7758230ad..128eb5c14 100644 --- a/Projects/Superalgos/Schemas/App-Schema/manage-stage.json +++ b/Projects/Superalgos/Schemas/App-Schema/manage-stage.json @@ -45,6 +45,12 @@ "name": "announcements", "type": "array", "childType": "Announcement" + }, + { + "name": "userDefinedCode", + "type": "node", + "childType": "User Defined Code", + "autoAdd": false } ], "propertyNameAtParent": "manageStage" diff --git a/Projects/Superalgos/Schemas/App-Schema/open-stage.json b/Projects/Superalgos/Schemas/App-Schema/open-stage.json index 09c869ab0..0a0badc3e 100644 --- a/Projects/Superalgos/Schemas/App-Schema/open-stage.json +++ b/Projects/Superalgos/Schemas/App-Schema/open-stage.json @@ -64,6 +64,12 @@ "name": "announcements", "type": "array", "childType": "Announcement" + }, + { + "name": "userDefinedCode", + "type": "node", + "childType": "User Defined Code", + "autoAdd": false } ], "propertyNameAtParent": "openStage" diff --git a/Projects/Superalgos/Schemas/App-Schema/trigger-stage.json b/Projects/Superalgos/Schemas/App-Schema/trigger-stage.json index 7669be6ff..0864e2d95 100644 --- a/Projects/Superalgos/Schemas/App-Schema/trigger-stage.json +++ b/Projects/Superalgos/Schemas/App-Schema/trigger-stage.json @@ -51,6 +51,12 @@ "name": "announcements", "type": "array", "childType": "Announcement" + }, + { + "name": "userDefinedCode", + "type": "node", + "childType": "User Defined Code", + "autoAdd": false } ], "propertyNameAtParent": "triggerStage" diff --git a/Projects/Superalgos/Schemas/App-Schema/user-defined-code.json b/Projects/Superalgos/Schemas/App-Schema/user-defined-code.json new file mode 100644 index 000000000..42b25a682 --- /dev/null +++ b/Projects/Superalgos/Schemas/App-Schema/user-defined-code.json @@ -0,0 +1,55 @@ +{ + "type": "User Defined Code", + "menuItems": [ + { + "action": "Configure", + "actionFunction": "uiObject.configEditor.activate", + "label": "Configure", + "iconPathOn": "configuration", + "iconPathOff": "configuration" + }, + { + "action": "Add UI Object", + "propertyToCheckFor": "javascriptCode", + "label": "Add Code", + "relatedUiObject": "Javascript Code", + "actionFunction": "payload.executeAction" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.executeAction", + "label": "Delete", + "iconPathOn": "delete-entity", + "iconPathOff": "delete-entity" + }, + { + "action": "Copy Node Path", + "label": "Copy Node Path", + "iconPathOn": "copy-path", + "iconPathOff": "copy-path", + "actionFunction": "payload.executeAction" + } + ], + "addLeftIcons": true, + "initialValues": { + "config": "{ \n \"runWhenEnteringStage\": false, \n \"runWhenExitingStage\": false, \n \"runWhileAtStage\": false, \n \"WhileAtStage_whenToRun\": \"first|last\" \n}" + }, + "editors": { + "config": true + }, + "level": 2, + "attachingRules": { + "compatibleTypes": "->Trigger Stage->Open Stage->Manage Stage->Close Stage->" + }, + "childrenNodesProperties": [ + { + "name": "javascriptCode", + "type": "array", + "childType": "Javascript Code", + "autoAdd": true + } + ], + "propertyNameAtParent": "userDefinedCode" +} \ No newline at end of file diff --git a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingStages.js b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingStages.js index 52efea02d..861ea8175 100644 --- a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingStages.js +++ b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingStages.js @@ -169,6 +169,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { function checkTriggerOff() { if (tradingEngine.tradingCurrent.strategyTriggerStage.status.value === 'Open') { + checkUserDefinedCode('Trigger Stage', 'Running', 'first'); let strategy = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value] let triggerStage = strategy.triggerStage @@ -246,6 +247,8 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { changeStageStatus('Trigger Stage', 'Closed', 'Position Taken') changeStageStatus('Open Stage', 'Opening') changeStageStatus('Manage Stage', 'Opening') + } else { + checkUserDefinedCode('Trigger Stage', 'Running', 'last'); } } } @@ -299,7 +302,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { tradingPositionModuleObject.initialTargets(tradingSystemStage, tradingEngineStage) initializeStageTargetSize() - /* From here on, the stage is officialy Open */ + /* From here on, the stage is officially Open */ changeStageStatus('Open Stage', 'Open') } } @@ -311,6 +314,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { While the Open Stage is Open, we do our regular stuff: place orders and check what happened to the orders already placed. */ + checkUserDefinedCode('Open Stage', 'Running', 'first'); let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].openStage let tradingEngineStage = tradingEngine.tradingCurrent.strategyOpenStage let executionNode = tradingSystemStage.openExecution @@ -324,6 +328,11 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { tradingEngineStage ) checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Open Stage') + + /* User Defined Code if runWhileAtStage==true */ + if (tradingEngine.tradingCurrent.strategyOpenStage.status.value === 'Open') { + checkUserDefinedCode('Open Stage', 'Running', 'last'); + } } } @@ -410,7 +419,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { if (tradingEngine.tradingCurrent.strategyManageStage.status.value === 'Opening') { let strategy = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value] let manageStage = strategy.manageStage - + /* The system allows the user not to define a Manage Stage, because the Manage Stage is optional. Here we are going to see if that is the case and if it is, we will inmidiatelly consider @@ -431,6 +440,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { function runWhenStatusIsOpen() { /* Open Status Procedure */ if (tradingEngine.tradingCurrent.strategyManageStage.status.value === 'Open') { + checkUserDefinedCode('Manage Stage', 'Running', 'first'); let strategy = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value] let manageStage = strategy.manageStage @@ -450,6 +460,11 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { /* Checking if Stop or Take Profit were hit */ checkStopLossOrTakeProfitWasHit() + + /* If User Defined Code exists check for runWhileAtStage */ + if (tradingEngine.tradingCurrent.strategyManageStage.status.value === 'Open') { + checkUserDefinedCode('Manage Stage', 'Running', 'last'); + } } function calculateStopLoss() { @@ -846,6 +861,7 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { /* This will happen as long as the Close Stage is Open. */ + checkUserDefinedCode('Close Stage', 'Running', 'first'); let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].closeStage let tradingEngineStage = tradingEngine.tradingCurrent.strategyCloseStage let executionNode = tradingSystemStage.closeExecution @@ -859,6 +875,10 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { ) checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Close Stage') + /* If User Defined Code exists check for runWhileAtStage */ + if (tradingEngine.tradingCurrent.strategyCloseStage.status.value === 'Open') { + checkUserDefinedCode('Close Stage', 'Running', 'last'); + } } } @@ -1122,9 +1142,11 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { } if (stage.status.value === 'Open') { openStage(stage) + checkUserDefinedCode(stageName, 'Open') } if (stage.status.value === 'Closed') { closeStage(stage) + checkUserDefinedCode(stageName, 'Closed') } /* @@ -1228,4 +1250,57 @@ exports.newSuperalgosBotModulesTradingStages = function (processIndex) { } throw 'Error Already Recorded' } -} + + /* checkUserDefinedCode(): Checks if User Defined Code exists and processes if applicable. */ + function checkUserDefinedCode(stage, status, when) { + let tradingSystemStage = getTradingSystemStage(stage); + + if (tradingSystemStage.userDefinedCode !== undefined) { + if (status === 'Running' && when !== tradingSystemStage.userDefinedCode.config.WhileAtStage_whenToRun) { return; } + + switch(status) { + case 'Open' : { + if (tradingSystemStage.userDefinedCode.config.runWhenEnteringStage) { + tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code'); + } + break; + } + case 'Running' : { + if (tradingSystemStage.userDefinedCode.config.runWhileAtStage && + tradingSystemStage.userDefinedCode.config.WhileAtStage_whenToRun === when) { + tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code'); + } + break; + } + case 'Closed' : { + if (tradingSystemStage.userDefinedCode.config.runWhenExitingStage) { + tradingSystem.evalUserCode(tradingSystemStage, 'User Defined Code'); + } + break; + } + } + } + } + + /* getTradingSystemStage(): takes stage name returns stage object. */ + function getTradingSystemStage(stage) { + switch(stage) { + case 'Trigger Stage' : { + return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].triggerStage; + break; + } + case 'Open Stage' : { + return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].openStage; + break; + } + case 'Manage Stage' : { + return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].manageStage; + break + } + case 'Close Stage' : { + return tradingSystem.tradingStrategies[tradingEngine.tradingCurrent.strategy.index.value].closeStage; + break; + } + } + } +} \ No newline at end of file diff --git a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingSystem.js b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingSystem.js index 4152980a2..e00bf4b43 100644 --- a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingSystem.js +++ b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Low-Frequency-Trading/TradingSystem.js @@ -13,7 +13,7 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { finalize: finalize } - /* + /* These 3 are the main data structures available to users when writing conditions and formulas. */ @@ -74,6 +74,10 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { evalNode(startingNode, 'Formulas', descendentOfNodeType) } + tradingSystem.evalUserCode = function (startingNode, descendentOfNodeType) { + evalNode(startingNode, 'User Codes', descendentOfNodeType); + } + tradingSystem.addError = function (errorDataArray) { /* This function adds to the array of error info a rate. @@ -87,7 +91,7 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { tradingSystem.addWarning = function (warningDataArray) { /* - This function adds to the array of warning info a rate. + This function adds to the array of warning info a rate. This rate will later help plotting the warning at the charts. */ @@ -98,7 +102,7 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { tradingSystem.addInfo = function (infoDataArray) { /* - This function adds to the array of warning info a rate. + This function adds to the array of warning info a rate. This rate will later help plotting the warning at the charts. */ @@ -108,9 +112,9 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { } function isItInside(elementWithTimestamp, elementWithBeginEnd) { - /* - The function is to allow in Conditions and Formulas to easily - know when an element with timestamp (like the ones of + /* + The function is to allow in Conditions and Formulas to easily + know when an element with timestamp (like the ones of single files) are inside the time range of an element with a time range, like a candle. */ @@ -167,8 +171,8 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { } function updateChart(pChart, pExchange, pMarket) { - /* - We need these 3 data structures to be a local objects + /* + We need these 3 data structures to be a local objects accessible while evaluating conditions and formulas. */ chart = pChart @@ -216,7 +220,7 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { tradingStagesModuleObject.cycleBasedStatistics() } catch (err) { - /* + /* If an error ocurred during execution, it was alreeady logged and included at the errors array. That means we need to do nothing here, just prevent the execution to be halted for not handling exceptions. @@ -258,6 +262,15 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { } } + /* Here we check if there is a User Defined-Javascript Code to be evaluated: */ + if (node.type === 'Javascript Code' && evaluating === 'User Codes') { + if (node.code !== undefined) { + if (isDescendent === true) { + evalJSCode(node); + } + } + } + /* Now we go down through all this node children */ let schemaDocument = TS.projects.superalgos.globals.taskConstants.APP_SCHEMA_MAP.get(node.project + '-' + node.type) if (schemaDocument === undefined) { return } @@ -395,5 +408,31 @@ exports.newSuperalgosBotModulesTradingSystem = function (processIndex) { TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalFormula -> value = ' + value) } -} + function evalJSCode(node) { + let value + let errorMessage + let docs + + try { + value = eval(node.code); + + } catch (err) { + value = 0 + errorMessage = err.message + docs = { + project: 'Superalgos', + category: 'Topic', + type: 'TS LF Trading Bot Error - Evaluating User Code Error', + placeholder: {} + } + TS.projects.superalgos.utilities.docsFunctions.buildPlaceholder(docs, err, node.name, node.code, undefined) + } + + if (errorMessage !== undefined) { + tradingSystem.addError([node.id, errorMessage, docs]) + TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, '[INFO] evalFormula -> errorMessage = ' + errorMessage) + return + } + } +} \ No newline at end of file diff --git a/Projects/Superalgos/UI/Function-Libraries/TradingSessionFunctions.js b/Projects/Superalgos/UI/Function-Libraries/TradingSessionFunctions.js index a48b4b0ff..e64c9dbc2 100644 --- a/Projects/Superalgos/UI/Function-Libraries/TradingSessionFunctions.js +++ b/Projects/Superalgos/UI/Function-Libraries/TradingSessionFunctions.js @@ -107,7 +107,8 @@ function newSuperalgosFunctionLibraryTradingSessionFunctions() { 'Size In Base Asset->Size In Quoted Asset->Position Rate->Formula->' + 'Situation->Condition->Javascript Code->' + 'Market Order->Limit Order->' + - 'Simulated Exchange Events->Simulated Partial Fill->Simulated Actual Rate->Simulated Fees Paid->Formula->' + 'Simulated Exchange Events->Simulated Partial Fill->Simulated Actual Rate->Simulated Fees Paid->Formula->' + + 'User Defined Code->Javascript Code->' let tradingSystem = UI.projects.superalgos.functionLibraries.protocolNode.getProtocolNode(node.tradingSystemReference.payload.referenceParent, false, true, true, false, false, lightingPath) @@ -145,7 +146,8 @@ function newSuperalgosFunctionLibraryTradingSessionFunctions() { 'Begin->End->Exit Type->Status->Begin Rate->End Rate->Stage Base Asset->Stage Quoted Asset->Size Placed->Target Size->Size Filled->Fees Paid->Stage Defined In->' + 'Serial Number->Identifier->Begin->End->Begin Rate->End Rate->Strategy Name->Status->Exit Type->' + 'Balance->Begin Balance->End Balance->' + - 'Index->Situation Name->Formula->Periods->' + 'Index->Situation Name->Formula->Periods->' + + 'User Variables->User Defined Variable->' let tradingEngine = UI.projects.superalgos.functionLibraries.protocolNode.getProtocolNode(node.tradingEngineReference.payload.referenceParent, false, true, true, false, false, lightingPath)