Merge branch 'develop' of https://github.com/Superalgos/Superalgos into develop

This commit is contained in:
Julian Molina committed 2020-08-15 12:50:56 +02:00
commit f7e299084e
18 files changed
+842 -804

No files matched your search

+114 -223
View File
@@ -777,6 +777,9 @@
"editors": {
"config": true
},
"title": [
"Use Reference Parent"
],
"initialValues": {
"config": "{ \n\"offsetX\": 0,\n \"offsetY\": 0\n }"
},
@@ -963,6 +966,9 @@
"editors": {
"config": true
},
"title": [
"Use Reference Parent"
],
"initialValues": {
"config": "{ \n\"offsetX\": 0,\n \"offsetY\": 0\n }"
},
@@ -2687,6 +2693,12 @@
"childType": "Episode Statistics",
"autoAdd": true
},
{
"name": "distanceToEvent",
"type": "node",
"childType": "Distance To Event",
"autoAdd": true
},
{
"name": "candle",
"type": "node",
@@ -2694,9 +2706,9 @@
"autoAdd": true
},
{
"name": "distanceToEvent",
"name": "cycle",
"type": "node",
"childType": "Distance To Event",
"childType": "Cycle",
"autoAdd": true
}
]
@@ -3214,12 +3226,6 @@
"childType": "Hit Fail",
"autoAdd": true
},
{
"name": "hitRatio",
"type": "node",
"childType": "Hit Ratio",
"autoAdd": true
},
{
"name": "days",
"type": "node",
@@ -3227,9 +3233,9 @@
"autoAdd": true
},
{
"name": "anualizedRateOfReturn",
"name": "annualizedRateOfReturn",
"type": "node",
"childType": "Anualized Rate Of Return",
"childType": "Annualized Rate Of Return",
"autoAdd": true
},
{
@@ -3280,12 +3286,6 @@
"icon": "shapes-polygon-body",
"propertyNameAtParent": "stopLoss",
"properties": [
{
"name": "stopLossStage",
"type": "node",
"childType": "Stop Loss Stage",
"autoAdd": true
},
{
"name": "stopLossPhase",
"type": "node",
@@ -3340,12 +3340,6 @@
"icon": "shapes-polygon-body",
"propertyNameAtParent": "stopLoss",
"properties": [
{
"name": "takeProfitStage",
"type": "node",
"childType": "Take Profit Stage",
"autoAdd": true
},
{
"name": "takeProfitPhase",
"type": "node",
@@ -3360,40 +3354,6 @@
}
]
},
{
"type": "Stop Loss Stage",
"menuItems": [
{
"action": "Configure This",
"label": "Configure This",
"iconPathOn": "settings",
"iconPathOff": "settings",
"actionFunction": "uiObject.configEditor.activate"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"editors": {
"config": true
},
"initialValues": {
"config": "{\n\"initialValue\": \"\"\n}"
},
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Stop Loss->"
},
"icon": "volume",
"propertyNameAtParent": "stopLossStage"
},
{
"type": "Stop Loss Phase",
"menuItems": [
@@ -3428,40 +3388,6 @@
"icon": "volume",
"propertyNameAtParent": "stopLossPhase"
},
{
"type": "Take Profit Stage",
"menuItems": [
{
"action": "Configure This",
"label": "Configure This",
"iconPathOn": "settings",
"iconPathOff": "settings",
"actionFunction": "uiObject.configEditor.activate"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"editors": {
"config": true
},
"initialValues": {
"config": "{\n\"initialValue\": \"\"\n}"
},
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Take Profit->"
},
"icon": "bitmex",
"propertyNameAtParent": "takeProfitStage"
},
{
"type": "Take Profit Phase",
"menuItems": [
@@ -3811,9 +3737,9 @@
"autoAdd": true
},
{
"name": "anualizedRateOfReturn",
"name": "annualizedRateOfReturn",
"type": "node",
"childType": "Anualized Rate Of Return",
"childType": "Annualized Rate Of Return",
"autoAdd": true
},
{
@@ -3900,9 +3826,9 @@
"autoAdd": true
},
{
"name": "anualizedRateOfReturn",
"name": "annualizedRateOfReturn",
"type": "node",
"childType": "Anualized Rate Of Return",
"childType": "Annualized Rate Of Return",
"autoAdd": true
},
{
@@ -4321,7 +4247,7 @@
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Episode Statistics->Episode Base Asset->Episode Quoted Asset->"
"compatibleTypes": "->Episode Base Asset->Episode Quoted Asset->"
},
"icon": "histogram",
"propertyNameAtParent": "hitRatio"
@@ -4531,7 +4457,7 @@
"propertyNameAtParent": "ROI"
},
{
"type": "Anualized Rate Of Return",
"type": "Annualized Rate Of Return",
"menuItems": [
{
"action": "Configure This",
@@ -4562,7 +4488,7 @@
"compatibleTypes": "->Episode Statistics->Episode Base Asset->Episode Quoted Asset->"
},
"icon": "bitmex",
"propertyNameAtParent": "anualizedRateOfReturn"
"propertyNameAtParent": "annualizedRateOfReturn"
},
{
"type": "Hit Fail",
@@ -5353,6 +5279,12 @@
"childType": "End Rate",
"autoAdd": true
},
{
"name": "stageDefinedIn",
"type": "node",
"childType": "Stage Defined In",
"autoAdd": true
},
{
"name": "stageBaseAsset",
"type": "node",
@@ -5430,6 +5362,12 @@
"childType": "End Rate",
"autoAdd": true
},
{
"name": "stageDefinedIn",
"type": "node",
"childType": "Stage Defined In",
"autoAdd": true
},
{
"name": "stageBaseAsset",
"type": "node",
@@ -5922,7 +5860,7 @@
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Episode->Strategy->Position->Candle->Market Order->Limit Order->Strategy Trigger Stage->Strategy Open Stage->Strategy Manage Stage->Strategy Close Stage->"
"compatibleTypes": "->Episode->Strategy->Position->Candle->Cycle->Market Order->Limit Order->Strategy Trigger Stage->Strategy Open Stage->Strategy Manage Stage->Strategy Close Stage->"
},
"icon": "monero",
"propertyNameAtParent": "begin"
@@ -5956,7 +5894,7 @@
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Episode->Strategy->Position->Candle->Market Order->Limit Order->Strategy Trigger Stage->Strategy Open Stage->Strategy Manage Stage->Strategy Close Stage->"
"compatibleTypes": "->Episode->Strategy->Position->Candle->Cycle->Market Order->Limit Order->Strategy Trigger Stage->Strategy Open Stage->Strategy Manage Stage->Strategy Close Stage->"
},
"icon": "upbit",
"propertyNameAtParent": "end"
@@ -6346,6 +6284,40 @@
"icon": "pantone",
"propertyNameAtParent": "exitTargetRate"
},
{
"type": "Stage Defined In",
"menuItems": [
{
"action": "Configure This",
"label": "Configure This",
"iconPathOn": "settings",
"iconPathOff": "settings",
"actionFunction": "uiObject.configEditor.activate"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"editors": {
"config": true
},
"initialValues": {
"config": "{\n\"initialValue\": \"\"\n}"
},
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Strategy Open Stage->Strategy Close Stage->"
},
"icon": "pantone",
"propertyNameAtParent": "stageDefinedIn"
},
{
"type": "Size",
"menuItems": [
@@ -6664,6 +6636,47 @@
}
]
},
{
"type": "Cycle",
"menuItems": [
{
"action": "Add Missing Children",
"label": "Add Missing Children",
"relatedUiObject": "Parameters",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Episode->"
},
"icon": "qtum",
"propertyNameAtParent": "cycle",
"properties": [
{
"name": "begin",
"type": "node",
"childType": "Begin",
"autoAdd": true
},
{
"name": "end",
"type": "node",
"childType": "End",
"autoAdd": true
}
]
},
{
"type": "Exchange Orders",
"menuItems": [
@@ -9604,12 +9617,6 @@
"childType": "Initial Targets",
"autoAdd": true
},
{
"name": "initialDefinition",
"type": "node",
"childType": "Initial Definition",
"autoAdd": true
},
{
"name": "openExecution",
"type": "node",
@@ -9676,48 +9683,6 @@
],
"propertyNameAtParent": "closeStageEvent"
},
{
"type": "Initial Definition",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#initial-definition",
"menuItems": [
{
"action": "Add Missing Children",
"label": "Add Missing Items",
"relatedUiObject": "Initial Definition",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"addLeftIcons": true,
"level": 2,
"chainAttachesTo": {
"compatibleTypes": "->Open Stage->"
},
"icon": "initial-definition",
"properties": [
{
"name": "initialStopLoss",
"type": "node",
"childType": "Initial Stop",
"autoAdd": true
},
{
"name": "initialTakeProfit",
"type": "node",
"childType": "Initial Take Profit",
"autoAdd": true
}
],
"propertyNameAtParent": "initialDefinition"
},
{
"type": "Initial Targets",
"menuItems": [
@@ -9801,43 +9766,6 @@
],
"propertyNameAtParent": "managedStopLoss"
},
{
"type": "Initial Stop",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#initial-stop",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Phase",
"relatedUiObject": "Phase",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Manage Stage->Initial Definition->"
},
"icon": "stop",
"properties": [
{
"name": "phases",
"type": "array",
"maxItems": 1,
"childType": "Phase",
"autoAdd": true
}
],
"propertyNameAtParent": "initialStopLoss"
},
{
"type": "Phase",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#phase-0",
@@ -9884,7 +9812,7 @@
"level": 1,
"chainedToSameType": true,
"chainAttachesTo": {
"compatibleTypes": "->Stop->Take Profit->Initial Stop->Initial Take Profit->Phase->"
"compatibleTypes": "->Managed Stop->Managed Take Profit->Phase->"
},
"icon": "placeholder",
"properties": [
@@ -10045,43 +9973,6 @@
],
"propertyNameAtParent": "managedTakeProfit"
},
{
"type": "Initial Take Profit",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#initial-take-profit",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Phase",
"relatedUiObject": "Phase",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete This",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.onMenuItemClick"
}
],
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Manage Stage->Initial Definition->"
},
"icon": "stage-open-take-profit",
"properties": [
{
"name": "phases",
"type": "array",
"maxItems": 1,
"childType": "Phase",
"autoAdd": true
}
],
"propertyNameAtParent": "initialTakeProfit"
},
{
"type": "Target Size In Base Asset",
"menuItems": [
@@ -10914,7 +10805,7 @@
}
],
"initialValues": {
"config": "{ \n\"percentage\": 0.01\n}"
"config": "{ \n\"percentage\": 0.1\n}"
},
"editors": {
"config": true
@@ -0,0 +1 @@
{"type":"Trading Engine","name":"Low Frequency","current":{"type":"Current","name":"New Current","episode":{"type":"Episode","name":"New Episode","serialNumber":{"type":"Serial Number","name":"New Serial Number","config":"{\n\"initialValue\": 0\n}","id":"5eb83933-432a-4029-8a3e-e4d8d760dd27","savedPayload":{"position":{"x":32628.925931291356,"y":29764.991484231054},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"identifier":{"type":"Identifier","name":"New Identifier","config":"{\n\"initialValue\": \"\"\n}","id":"8ba7fe0c-c5df-408e-be8a-ebde90f2a50d","savedPayload":{"position":{"x":32744.541202314154,"y":29624.04899186301},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"begin":{"type":"Begin","name":"New Begin","config":"{\n\"initialValue\": 0\n}","id":"d7faf864-3fa8-4280-b7d3-a022338b0dfe","savedPayload":{"position":{"x":32885.35243415358,"y":29508.47178182837},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"end":{"type":"End","name":"New End","config":"{\n\"initialValue\": 0\n}","id":"f6722d05-77f9-4a68-ba69-38f56cdc7e91","savedPayload":{"position":{"x":33045.99145646664,"y":29422.602168012847},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"beginRate":{"type":"Begin Rate","name":"New Begin Rate","config":"{\n\"initialValue\": 0\n}","id":"613903ae-45e4-4eee-a178-d6ec049490ec","savedPayload":{"position":{"x":33220.29145920026,"y":29369.7257306039},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"endRate":{"type":"End Rate","name":"New End Rate","config":"{\n\"initialValue\": 0\n}","id":"b2bfb20d-64d5-4c98-b0cd-a466654c1e6a","savedPayload":{"position":{"x":33401.452087304264,"y":29351.87056129139},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"status":{"type":"Status","name":"New Status","config":"{\n\"initialValue\": \"Not Open\"\n}","id":"2615490b-e968-4607-a6e5-009c3ae78957","savedPayload":{"position":{"x":33582.621371994755,"y":29369.721550636412},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"exitType":{"type":"Exit Type","name":"New Exit Type","config":"{\n\"initialValue\": \"No Exit\"\n}","id":"6bcc264b-a189-4aca-8397-d3520bdf4078","savedPayload":{"position":{"x":33756.91970975856,"y":29422.591759304833},"targetPosition":{"x":33401.448976596075,"y":30280.9349218038},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"episodeBaseAsset":{"type":"Episode Base Asset","name":"New Episode Base Asset","balance":{"type":"Balance","name":"Base Asset","config":"{\n\"initialValue\": 0\n}","id":"72f6a6b6-f35b-4d80-b8ad-4a4ed55f2387","savedPayload":{"position":{"x":33866.52652515162,"y":28582.72931076452},"targetPosition":{"x":33916.72751479778,"y":29509.897829277033},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"beginBalance":{"type":"Begin Balance","name":"New Begin Balance","config":"{\n\"initialValue\": 0\n}","id":"0176b616-7628-42b1-8c06-4cfb1bfd05aa","savedPayload":{"position":{"x":33999.24564731282,"y":28585.12649092692},"targetPosition":{"x":33916.72751479778,"y":29509.897829277033},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"endBalance":{"type":"End Balance","name":"New End Balance","config":"{\n\"initialValue\": 0\n}","id":"d3844a95-57a8-48f5-bfa9-e093bf815f57","savedPayload":{"position":{"x":34130.0563129769,"y":28606.19458048408},"targetPosition":{"x":33916.72751479778,"y":29509.897829277033},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"hits":{"type":"Hits","name":"New Hits","config":"{\n\"initialValue\": 0\n}","id":"c5eff46f-38bc-4ab9-9d07-ed6c5e161895","savedPLine truncated
@@ -28,7 +28,6 @@ function newSessionFunctions() {
'Trigger Stage->Trigger On Event->Trigger Off Event->Take Position Event->' +
'Announcement->Announcement Formula->' +
'Open Stage->' +
'Initial Definition->Initial Stop->Initial Take Profit->' +
'Manage Stage->' +
'Managed Stop Loss->Managed Take Profit->' +
'Phase->Formula->Next Phase Event->Move To Phase Event->Phase->' +
@@ -54,8 +53,8 @@ function newSessionFunctions() {
'Episode->' +
'Episode Base Asset->Episode Quoted Asset->Episode Counters->Episode Statistics->' +
'Strategies->Positions->Orders->Hits->Fails->' +
'Profit Loss->Hit Ratio->Hit Fail->Days->ROI->Anualized Rate Of Return->User Defined Statistic->' +
'Candle->' +
'Profit Loss->Hit Ratio->Hit Fail->Days->ROI->Annualized Rate Of Return->User Defined Statistic->' +
'Candle->Cycle->' +
'Begin->End->Open->Close->Min->Max->Index->' +
'Distance To Event->' +
'Trigger On->Trigger Off->Take Position->Close Position->Next Phase->Move To Phase->Create Order->Cancel Order->Close Order->' +
@@ -63,8 +62,8 @@ function newSessionFunctions() {
'Strategy Counters->' +
'Position->' +
'Entry Target Rate->Exit Target Rate->' +
'Stop Loss->Stop Loss Stage->Stop Loss Phase->Stop Loss Position->' +
'Take Profit->Take Profit Stage->Take Profit Phase->Take Profit Position->' +
'Stop Loss->Stop Loss Phase->Stop Loss Position->' +
'Take Profit->Take Profit Phase->Take Profit Position->' +
'Position Counters->' +
'Position Statistics->Days->User Defined Statistic->' +
'Position Base Asset->Position Quoted Asset->Entry Target Size->Exit Target Size->' +
@@ -77,7 +76,7 @@ function newSessionFunctions() {
'Size->Size Filled->Fees Paid->' +
'Order Statistics->Percentage Filled->Actual Rate->Days->User Defined Statistic->' +
'Strategy Trigger Stage->Strategy Open Stage->Strategy Manage Stage->Strategy Close Stage->' +
'Begin->End->Exit Type->Status->Begin Rate->End Rate->Stage Base Asset->Stage Quoted Asset->Size Placed->Target Size->Size Filled->Fees Paid->' +
'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->'
+6 -14
View File
@@ -94,6 +94,7 @@ function newPlotter() {
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction, pProductDefinition) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0]
dailyFiles = pStorage.dailyFiles[0]
@@ -300,10 +301,8 @@ function newPlotter() {
jsonifiedArray.push(record)
previous = record
if (record.begin === undefined || record.end === undefined) { console.log('Could not find the property begin or end which are needed for the plotter to work.') }
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record
thisObject.container.eventHandler.raiseEvent('Current Record Changed', thisObject.currentRecord)
if (record.begin === undefined || record.end === undefined) {
console.log('Could not find the property begin or end which are needed for the plotter to work. productDefinition.config.codeName = ' + productDefinition.config.codeName)
}
}
}
@@ -425,15 +424,6 @@ function newPlotter() {
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS)
}
/* Lests check if all the visible screen is going to be covered by candle-record. */
let lowerEnd = leftDate.valueOf()
let upperEnd = rightDate.valueOf()
if (records.length > 0) {
if (records[0].begin > lowerEnd || records[records.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000)
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write('[ERROR] recalculateUsingDailyFiles -> err = ' + err.stack) }
}
@@ -899,7 +889,9 @@ function newPlotter() {
}
function onDragFinished() {
recalculate()
if (canvas.chartingSpace.visible === true) {
recalculate()
}
}
function onDisplace(event) {
+1 -1
View File
@@ -1 +1 @@
{"type":"Data Mine","name":"Masters-Trading","config":"{ \n\"codeName\": \"Masters-Trading\"\n}","sensorBots":[],"indicatorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\"\n}","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"liveTradingWaitTime\": 0,\n \"paperTradingWaitTime\": 0,\n \"fordwardTestingWaitTime\": 0,\n \"backtestingWaitTime\": 0,\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","id":"6fc3054e-5034-4fc7-bddf-02360e0a6acf","savedPayload":{"position":{"x":15126.709841795062,"y":14115.844903867037},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"db063d7b-db05-4bf6-941f-999e1ccb38fc"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","id":"c3c2240f-514b-40de-825f-674037117d0b","savedPayload":{"position":{"x":15060.50960648652,"y":13958.543096746436},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"0764aaaf-d970-4f23-9df8-55fe5b7dd273"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","id":"1dfdfc0d-842f-4e63-a591-256ac8a06b0f","savedPayload":{"position":{"x":15000.170158581612,"y":13798.909615340754},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","id":"39360c55-5ba8-413b-b0bd-cac3789ebf5f","savedPayload":{"position":{"x":14945.771166150153,"y":13637.156840896287},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Current Candle","id":"ba614415-6e4f-4979-97a5-34c72a59e67c","savedPayload":{"position":{"x":14897.386505301713,"y":13473.814926987687},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Current Candle","id":"1a2e3461-33e6-45b1-bcc1-c56a8c5d275c","savedPayload":{"position":{"x":14855.08212849534,"y":13309.762335468542},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":14818.915848183393,"y":13144.277773603595},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":14788.936996026083,"y":12977.582492132336},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"213b8fe8-f772-41ea-a3f5-97867be555f7"}}},{"type":"Output Dataset","name":"Multi-Period-Market Distance To Event","id":"a30e7306-7be9-41b6-Line truncated
{"type":"Data Mine","name":"Masters-Trading","config":"{ \n\"codeName\": \"Masters-Trading\"\n}","sensorBots":[],"indicatorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\"\n}","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"liveTradingWaitTime\": 0,\n \"paperTradingWaitTime\": 0,\n \"fordwardTestingWaitTime\": 0,\n \"backtestingWaitTime\": 0,\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","id":"6fc3054e-5034-4fc7-bddf-02360e0a6acf","savedPayload":{"position":{"x":15126.709841795062,"y":14115.844903867037},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"db063d7b-db05-4bf6-941f-999e1ccb38fc"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","id":"c3c2240f-514b-40de-825f-674037117d0b","savedPayload":{"position":{"x":15060.50960648652,"y":13958.543096746436},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"0764aaaf-d970-4f23-9df8-55fe5b7dd273"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","id":"1dfdfc0d-842f-4e63-a591-256ac8a06b0f","savedPayload":{"position":{"x":15000.170158581612,"y":13798.909615340754},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","id":"39360c55-5ba8-413b-b0bd-cac3789ebf5f","savedPayload":{"position":{"x":14945.771166150153,"y":13637.156840896287},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Current Candle","id":"ba614415-6e4f-4979-97a5-34c72a59e67c","savedPayload":{"position":{"x":14897.386505301713,"y":13473.814926987687},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Current Candle","id":"1a2e3461-33e6-45b1-bcc1-c56a8c5d275c","savedPayload":{"position":{"x":14855.08212849534,"y":13309.762335468542},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":14818.915848183393,"y":13144.277773603595},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":14788.936996026083,"y":12977.582492132336},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"213b8fe8-f772-41ea-a3f5-97867be555f7"}}},{"type":"Output Dataset","name":"Multi-Period-Market Distance To Event","id":"a30e7306-7be9-41b6-Line truncated
+2 -2
View File
@@ -442,7 +442,7 @@ function newFileCursor() {
if (beginDateRange !== undefined) {
let compareBeginDate = removeTime(beginDateRange)
if (compareTargetDate.valueOf() < compareBeginDate) {
if (compareTargetDate.valueOf() < compareBeginDate.valueOf()) {
controlLoop()
return
}
@@ -451,7 +451,7 @@ function newFileCursor() {
if (endDateRange !== undefined) {
let compareEndDate = removeTime(endDateRange)
if (compareTargetDate.valueOf() > compareEndDate) {
if (compareTargetDate.valueOf() >= compareEndDate.valueOf()) {
controlLoop()
return
}
+32 -59
View File
@@ -6,9 +6,11 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
const MODULE_NAME = 'Trading Engine'
let thisObject = {
mantain: mantain,
reset: reset,
getNodeById: getNodeById,
cloneValues: cloneValues,
setCurrentCandle: setCurrentCandle,
setCurrentCycle: setCurrentCycle,
initializeNode: initializeNode,
initialize: initialize,
finalize: finalize
@@ -43,7 +45,10 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
}
function mantain() {
updateDistanceToEventsCounters()
}
function reset() {
}
function getNodeById(NodeId) {
@@ -59,6 +64,32 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
tradingEngine.current.episode.candle.max.value = candle.max
}
function setCurrentCycle(cycle) {
switch (cycle) {
case 'First': {
tradingEngine.current.episode.cycle.begin.value =
tradingEngine.current.episode.candle.begin.value +
sessionParameters.timeFrame.config.value
tradingEngine.current.episode.cycle.end.value =
tradingEngine.current.episode.candle.begin.value +
sessionParameters.timeFrame.config.value +
sessionParameters.timeFrame.config.value / 2 - 1
break
}
case 'Second': {
tradingEngine.current.episode.cycle.begin.value =
tradingEngine.current.episode.candle.begin.value +
sessionParameters.timeFrame.config.value +
sessionParameters.timeFrame.config.value / 2
tradingEngine.current.episode.cycle.end.value =
tradingEngine.current.episode.candle.begin.value +
sessionParameters.timeFrame.config.value +
sessionParameters.timeFrame.config.value * 4 / 4 - 1
break
}
}
}
function cloneValues(originNode, destinationNode) {
if (originNode === undefined) { return }
if (destinationNode === undefined) { return }
@@ -180,63 +211,5 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
}
}
}
function updateDistanceToEventsCounters() {
/* Keeping Distance Counters Up-to-date while avoinding counting before the first event happens. */
if (
tradingEngine.current.episode.distanceToEvent.triggerOn.value > 0
) {
tradingEngine.current.episode.distanceToEvent.triggerOn.value++
}
if (
tradingEngine.current.episode.distanceToEvent.triggerOff.value > 0
) {
tradingEngine.current.episode.distanceToEvent.triggerOff.value++
}
if (
tradingEngine.current.episode.distanceToEvent.takePosition.value > 0
) {
tradingEngine.current.episode.distanceToEvent.takePosition.value++
}
if (
tradingEngine.current.episode.distanceToEvent.closePosition.value > 0
) {
tradingEngine.current.episode.distanceToEvent.closePosition.value++
}
if (
tradingEngine.current.episode.distanceToEvent.nextPhase.value > 0
) {
tradingEngine.current.episode.distanceToEvent.nextPhase.value++
}
if (
tradingEngine.current.episode.distanceToEvent.moveToPhase.value > 0
) {
tradingEngine.current.episode.distanceToEvent.moveToPhase.value++
}
if (
tradingEngine.current.episode.distanceToEvent.createOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.createOrder.value++
}
if (
tradingEngine.current.episode.distanceToEvent.cancelOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.cancelOrder.value++
}
if (
tradingEngine.current.episode.distanceToEvent.closeOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.closeOrder.value++
}
}
}
@@ -5,6 +5,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
const MODULE_NAME = 'Trading Episode'
let thisObject = {
mantain: mantain,
reset: reset,
openEpisode: openEpisode,
updateExitType: updateExitType,
closeEpisode: closeEpisode,
@@ -33,10 +34,14 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
}
function mantain() {
resetTradingEngineDataStructure()
updateCounters()
updateStatistics()
updateEnds()
updateDistanceToEventsCounters()
}
function reset() {
resetTradingEngineDataStructure()
}
function openEpisode() {
@@ -132,12 +137,12 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
tradingEngine.current.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.ROI.value, 10)
/* Updating Anualized Rate Of Return */
tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value =
/* Updating Annualized Rate Of Return */
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeStatistics.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeStatistics.profitLoss.value > 0) {
@@ -197,17 +202,17 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.hitRatio.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value, 10)
/* Updating Anualized Rate Of Return */
tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value =
/* Updating Annualized Rate Of Return */
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeBaseAsset.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value =
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeQuotedAsset.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeBaseAsset.profitLoss.value > 0) {
@@ -221,4 +226,61 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
tradingEngine.current.episode.episodeQuotedAsset.hitFail.value = 'Fail'
}
}
function updateDistanceToEventsCounters() {
/* Keeping Distance Counters Up-to-date while avoinding counting before the first event happens. */
if (
tradingEngine.current.episode.distanceToEvent.triggerOn.value > 0
) {
tradingEngine.current.episode.distanceToEvent.triggerOn.value++
}
if (
tradingEngine.current.episode.distanceToEvent.triggerOff.value > 0
) {
tradingEngine.current.episode.distanceToEvent.triggerOff.value++
}
if (
tradingEngine.current.episode.distanceToEvent.takePosition.value > 0
) {
tradingEngine.current.episode.distanceToEvent.takePosition.value++
}
if (
tradingEngine.current.episode.distanceToEvent.closePosition.value > 0
) {
tradingEngine.current.episode.distanceToEvent.closePosition.value++
}
if (
tradingEngine.current.episode.distanceToEvent.nextPhase.value > 0
) {
tradingEngine.current.episode.distanceToEvent.nextPhase.value++
}
if (
tradingEngine.current.episode.distanceToEvent.moveToPhase.value > 0
) {
tradingEngine.current.episode.distanceToEvent.moveToPhase.value++
}
if (
tradingEngine.current.episode.distanceToEvent.createOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.createOrder.value++
}
if (
tradingEngine.current.episode.distanceToEvent.cancelOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.cancelOrder.value++
}
if (
tradingEngine.current.episode.distanceToEvent.closeOrder.value > 0
) {
tradingEngine.current.episode.distanceToEvent.closeOrder.value++
}
}
}
@@ -6,6 +6,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
let thisObject = {
mantain: mantain,
reset: reset,
runExecution: runExecution,
initialize: initialize,
finalize: finalize
@@ -41,6 +42,10 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
tradingOrdersModule.mantain()
}
function reset() {
tradingOrdersModule.reset()
}
async function runExecution(
executionNode,
tradingEngineStage
+166 -100
View File
@@ -6,6 +6,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
let thisObject = {
mantain: mantain,
reset: reset,
checkOrders: checkOrders,
initialize: initialize,
finalize: finalize
@@ -45,6 +46,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
}
function mantain() {
mantainReset(true, false)
}
function reset() {
mantainReset(false, true)
}
function mantainReset(mantain, reset) {
if (tradingEngine.current.strategy.index.value === tradingEngine.current.strategy.index.config.initialValue) { return }
@@ -53,42 +62,46 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
executionNode = stageNode.openExecution
mantainExecutionNode(executionNode, tradingEngine.current.strategyOpenStage.status.value)
processExecutionNode(executionNode, tradingEngine.current.strategyOpenStage.status.value)
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
executionNode = stageNode.closeExecution
mantainExecutionNode(executionNode, tradingEngine.current.strategyCloseStage.status.value)
processExecutionNode(executionNode, tradingEngine.current.strategyCloseStage.status.value)
function mantainExecutionNode(executionNode, stageStatus) {
function processExecutionNode(executionNode, stageStatus) {
mantainExecutionAlgorithms(executionNode)
preocessExecutionAlgorithms(executionNode)
function mantainExecutionAlgorithms(executionNode) {
function preocessExecutionAlgorithms(executionNode) {
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
let executionAlgorithm = executionNode.executionAlgorithms[i]
mantainOrders(executionAlgorithm.marketBuyOrders)
mantainOrders(executionAlgorithm.marketSellOrders)
mantainOrders(executionAlgorithm.limitBuyOrders)
mantainOrders(executionAlgorithm.limitSellOrders)
processOrders(executionAlgorithm.marketBuyOrders)
processOrders(executionAlgorithm.marketSellOrders)
processOrders(executionAlgorithm.limitBuyOrders)
processOrders(executionAlgorithm.limitSellOrders)
}
}
function mantainOrders(orders) {
function processOrders(orders) {
for (let i = 0; i < orders.length; i++) {
let tradingSystemOrder = orders[i]
if (tradingSystemOrder.referenceParent === undefined) { continue }
if (tradingSystemOrder.referenceParent === undefined) {
badDefinitionUnhandledException(undefined, 'tradingSystemOrder.referenceParent === undefined', tradingSystemOrder)
}
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
try {
if (tradingEngineOrder.status === undefined) { continue }
} catch (err) {
console.log(err.stack)
if (tradingEngineOrder.status === undefined) {
badDefinitionUnhandledException(undefined, 'tradingEngineOrder.status === undefined', tradingEngineOrder)
}
updateCounters(tradingEngineOrder)
updateEnds(tradingEngineOrder)
updateStatistics(tradingEngineOrder)
resetTradingEngineDataStructure(tradingEngineOrder, tradingSystemOrder, stageStatus)
if (mantain === true) {
updateCounters(tradingEngineOrder)
updateEnds(tradingEngineOrder)
updateStatistics(tradingEngineOrder)
}
if (reset === true) {
resetTradingEngineDataStructure(tradingEngineOrder, tradingSystemOrder, stageStatus)
}
}
}
}
@@ -151,6 +164,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Cancel the order at the Exchange, if needed. */
await exchangeCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
updateEndsWithCycle(tradingEngineOrder)
}
}
}
@@ -216,8 +231,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Create Order Procedure */
tradingEngineOrder.status.value = 'Open'
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
tradingEngineOrder.begin.value = tradingEngine.current.episode.candle.begin.value
tradingEngineOrder.end.value = tradingEngine.current.episode.candle.end.value
tradingEngineOrder.begin.value = tradingEngine.current.episode.cycle.begin.value
tradingEngineOrder.end.value = tradingEngine.current.episode.cycle.end.value
tradingEngineOrder.serialNumber.value = tradingEngine.current.episode.episodeCounters.orders.value
tradingEngineOrder.orderName.value = tradingSystemOrder.name
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
@@ -253,24 +268,42 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) }
if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) }
/* Size in Base Asset */
tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
/*
The Size calculation depends on how the user defined the size of the position.
The user could have defined the size of the position in Base Asset or Quoted Asset.
*/
switch (tradingEngineStage.stageDefinedIn.value) {
case 'Base Asset': {
/* Size in Base Asset */
tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
/* Size in Quoted Asset */
tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
/* Check that the Size calculated would not surpass Stage Target Size */
if (
tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value >
tradingEngineStage.stageBaseAsset.targetSize.value
) {
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value
}
/* Check that the Size calculated would not surpass Stage Target Size */
if (
tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value >
tradingEngineStage.stageBaseAsset.targetSize.value
) {
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value
}
if (
tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value >
tradingEngineStage.stageQuotedAsset.targetSize.value
) {
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value
/* Size in Quoted Asset */
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value
break
}
case 'Quoted Asset': {
/* Size in Quoted Asset */
tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
if (
tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value >
tradingEngineStage.stageQuotedAsset.targetSize.value
) {
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value
}
/* Size in Base Asset */
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value
break
}
}
tradingEngineOrder.orderBaseAsset.size.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.size.value, 10)
@@ -333,8 +366,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value
actualRateSimulation()
feesPaidSimulation()
percentageFilledSimulation()
feesPaidSimulation()
sizeFilledSimulation()
doTheAccounting(
@@ -397,67 +430,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
}
function feesPaidSimulation() {
/* Fees Paid Simulation */
let calculatedBasedOnTradingSystem = false
/* Based on the Trading System Definition */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
if (tradingEngineOrder.orderBaseAsset.feesPaid.value === tradingEngineOrder.orderBaseAsset.feesPaid.config.initialValue) {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
calculatedBasedOnTradingSystem = true
}
if (tradingEngineOrder.orderQuotedAsset.feesPaid.value === tradingEngineOrder.orderQuotedAsset.feesPaid.config.initialValue) {
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
calculatedBasedOnTradingSystem = true
}
}
}
/* Based on the Session Parameters Definition */
if (calculatedBasedOnTradingSystem === false) {
/* Fees are simulated based on the Session Paremeters */
switch (tradingEngineOrder.type) {
case 'Market Order': {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
bot.SESSION.parameters.feeStructure.config.taker / 100
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
bot.SESSION.parameters.feeStructure.config.taker / 100
break
}
case 'Limit Order': {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
bot.SESSION.parameters.feeStructure.config.maker / 100
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
bot.SESSION.parameters.feeStructure.config.maker / 100
break
}
}
}
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
}
function percentageFilledSimulation() {
/*
Order Filling Simulation: the way we are going to handle this is the following.
@@ -481,6 +453,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
}
break
}
case 'Market Buy Order': {
orderWasHit = true
break
}
case 'Market Sell Order': {
orderWasHit = true
break
}
}
if (orderWasHit === true) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
@@ -507,10 +487,86 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Initialize this */
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
updateEndsWithCycle(tradingEngineOrder)
}
}
}
function feesPaidSimulation() {
/*
The way the fess paid is calculated is as follows: From the order size we apply the % of fee,
that would give us the total fees for that order. To that we apply the % filled of the orders,
that give use the final result of the fees paid for the current % filled.
But before that, we will see if there is an specific definition on how to simulate the fees for
this order, or if there is not, we use the fees session parameters definitions.
*/
let calculatedBasedOnTradingSystem = false
/* Based on the Trading System Definition */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
if (tradingEngineOrder.orderBaseAsset.feesPaid.value === tradingEngineOrder.orderBaseAsset.feesPaid.config.initialValue) {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
calculatedBasedOnTradingSystem = true
}
if (tradingEngineOrder.orderQuotedAsset.feesPaid.value === tradingEngineOrder.orderQuotedAsset.feesPaid.config.initialValue) {
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
calculatedBasedOnTradingSystem = true
}
}
}
/* Based on the Session Parameters Definition */
if (calculatedBasedOnTradingSystem === false) {
/* Fees are simulated based on the Session Paremeters */
switch (tradingEngineOrder.type) {
case 'Market Order': {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
bot.SESSION.parameters.feeStructure.config.taker / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
bot.SESSION.parameters.feeStructure.config.taker / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
break
}
case 'Limit Order': {
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
bot.SESSION.parameters.feeStructure.config.maker / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
bot.SESSION.parameters.feeStructure.config.maker / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
break
}
}
}
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
}
function sizeFilledSimulation() {
/* Size Filled */
tradingEngineOrder.orderBaseAsset.sizeFilled.value =
@@ -563,6 +619,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Initialize this */
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
updateEndsWithCycle(tradingEngineOrder)
}
if (order.remaining > 0 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
@@ -572,6 +630,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Initialize this */
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
updateEndsWithCycle(tradingEngineOrder)
}
if (order.status === AT_EXCHANGE_STATUS.CANCELLED) {
@@ -581,6 +641,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
/* Initialize this */
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
updateEndsWithCycle(tradingEngineOrder)
}
syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
@@ -891,6 +953,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
}
}
function updateEndsWithCycle(tradingEngineOrder) {
tradingEngineOrder.end.value = tradingEngine.current.episode.cycle.end.value
}
function updateCounters(tradingEngineOrder) {
if (tradingEngineOrder.status.value === 'Open') {
tradingEngineOrder.orderCounters.periods.value++
@@ -5,6 +5,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
const MODULE_NAME = 'Trading Position'
let thisObject = {
mantain: mantain,
reset: reset,
openPosition: openPosition,
closingPosition: closingPosition,
closePosition: closePosition,
@@ -39,34 +40,39 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
}
function mantain() {
resetTradingEngineDataStructure()
updateCounters()
updateEnds()
}
function reset() {
resetTradingEngineDataStructure()
}
function openPosition(situationName) {
/* Recording the opening at the Trading Engine Data Structure */
tradingEngine.current.position.status.value = 'Open'
tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value
tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value + 1
tradingEngine.current.position.identifier.value = global.UNIQUE_ID()
tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.begin.value
tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.position.beginRate.value = tradingEngine.current.episode.candle.close.value
tradingEngine.current.position.positionBaseAsset.beginBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.beginBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value
tradingEngine.current.position.situationName.value = situationName
/* Initializing Stop and Take Profit Stage / Phase */
tradingEngine.current.position.stopLoss.stopLossStage.value = 'Open Stage'
tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Open Stage'
tradingEngine.current.position.stopLoss.stopLossPhase.value = 0
tradingEngine.current.position.takeProfit.takeProfitPhase.value = 0
/* Initializing Stop and Take Phase */
tradingEngine.current.position.stopLoss.stopLossPhase.value = 1
tradingEngine.current.position.takeProfit.takeProfitPhase.value = 1
/* Updating Episode Counters */
tradingEngine.current.episode.episodeCounters.positions.value++
/* Inicializing this counter */
tradingEngine.current.episode.distanceToEvent.takePosition.value = 1
/* Remember the balance we had before taking the position to later calculate profit or loss */
tradingEngine.current.position.positionBaseAsset.beginBalance = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.beginBalance = tradingEngine.current.episode.episodeQuotedAsset.balance.value
}
function closingPosition(exitType) {
@@ -98,47 +104,45 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
tradingEngine.current.position.takeProfit.value = formulas.get(formulaId)
}
function updateStopLoss(phase, stage) {
function updateStopLoss(phase) {
tradingEngine.current.position.stopLoss.stopLossPhase.value = phase
tradingEngine.current.position.stopLoss.stopLossStage.value = stage
}
function updateTakeProfit(phase, stage) {
function updateTakeProfit(phase) {
tradingEngine.current.position.takeProfit.takeProfitPhase.value = phase
tradingEngine.current.position.takeProfit.takeProfitStage.value = stage
}
function initialTargets(stageNode) {
function initialTargets(tradingSystemStageNode, tradingEngineStageNode) {
if (stageNode.initialTargets === undefined) {
if (tradingSystemStageNode.initialTargets === undefined) {
const message = 'Stage without Initial Targets node. Add one please.'
badDefinitionUnhandledException(undefined, message, stageNode)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode)
}
setTargetRate()
setTargetSize()
function setTargetRate() {
if (stageNode.initialTargets.targetRate === undefined) {
if (tradingSystemStageNode.initialTargets.targetRate === undefined) {
const message = 'Target Rate Node not Found. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets)
}
if (stageNode.initialTargets.targetRate.formula === undefined) {
if (tradingSystemStageNode.initialTargets.targetRate.formula === undefined) {
const message = 'Formula of Target Rate Node not Found. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate)
}
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetRate.formula.id)
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetRate.formula.id)
if (value === undefined) {
const message = 'Target Rate can not be undefined. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate.formula)
}
if (isNaN(value)) {
const message = 'Target Rate must be a number. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate.formula)
}
switch (stageNode.type) {
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.entryTargetRate.value = global.PRECISE(value, 10)
break
@@ -153,27 +157,27 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
function setTargetSize() {
/* Basic Validation */
if (
stageNode.initialTargets.targetSizeInBaseAsset !== undefined &&
stageNode.initialTargets.targetSizeInQuotedAsset !== undefined
tradingSystemStageNode.initialTargets.targetSizeInBaseAsset !== undefined &&
tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset !== undefined
) {
const message = 'Only Target Size In Base Asset or Target Size In Quoted Asset is allowed. Remove one of them please.'
badDefinitionUnhandledException(undefined, message, stageNode)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode)
}
/* Position In Base Asset */
if (stageNode.initialTargets.targetSizeInBaseAsset !== undefined) {
if (stageNode.initialTargets.targetSizeInBaseAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetSizeInBaseAsset.formula.id)
if (tradingSystemStageNode.initialTargets.targetSizeInBaseAsset !== undefined) {
if (tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula.id)
if (value === undefined) {
const message = 'Target Size In Base Asset cannot be undefined. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
}
if (value === undefined) {
const message = 'Target Size In Base Asset cannot be zero. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
}
switch (stageNode.type) {
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value =
@@ -187,25 +191,28 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
break
}
}
/* Remember how the end user defined this stage. */
tradingEngineStageNode.stageDefinedIn.value = 'Base Asset'
} else {
const message = 'You need to specify a Formula for this.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset)
}
}
/* Position In Quoted Asset */
if (stageNode.initialTargets.targetSizeInQuotedAsset !== undefined) {
if (stageNode.initialTargets.targetSizeInQuotedAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetSizeInQuotedAsset.formula.id)
if (tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset !== undefined) {
if (tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula.id)
if (value === undefined) {
const message = 'Target Size In Quoted Asset cannot be undefined. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
if (value === undefined) {
const message = 'Target Size In Quoted Asset cannot be zero. Fix this please.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
switch (stageNode.type) {
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value =
@@ -219,9 +226,12 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
break
}
}
/* Remember how the end user defined this stage. */
tradingEngineStageNode.stageDefinedIn.value = 'Quoted Asset'
} else {
const errorText = 'You need to specify a Formula for this.'
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
}
}
@@ -31,7 +31,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
outputDatasetsMap = undefined
}
function appendRecords() {
function appendRecords(cycle) {
/*
Here we add records to the output files. At the product config property nodePath
we have a pointer to the node that have the information we need to extract.
@@ -53,6 +53,14 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
}
function persistRecords() {
/*
There are a few products configured to be saved only at an specific cycle.
*/
if (product.config.saveAtCycle !== undefined) {
if (cycle !== product.config.saveAtCycle) {
return
}
}
/* Clean the file from information of previous executions */
pruneOutputFile(product, outputDatasetArray)
@@ -329,7 +337,12 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
let end = record[j]
if (end >= tradingEngine.current.episode.candle.end.value) {
outputFile.splice(i, 1)
pruneOutputFile(product, outputFile)
/*
This will execute the next prune call in the next iteration of the NodeJs event loop
allowing for other callbacks to be executed. It also prevents the error
'Maximum call stack size exceeded', since the call is not placed at the call stack.
*/
setImmediate(pruneOutputFile, product, outputFile)
return
}
}
@@ -110,6 +110,9 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Candle End @ ' + (new Date(candle.end)).toLocaleString()) }
tradingEngineModule.setCurrentCandle(candle) // We move the current candle we are standing at, to the trading engine data structure to make it available to anyone, including conditions and formulas.
/* We emit a heart beat so that the UI can now where we are at the overal process.*/
heartBeat()
if (firstLoopExecution === true) {
tradingEpisodeModule.openEpisode()
@@ -131,22 +134,48 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
return
}
heartBeat()
positionChartAtCurrentCandle()
/* The chart was recalculated based on the current candle. */
tradingSystemModule.updateChart(chart)
/* Do the stuff needed previous to the run */
/*
Do the stuff needed previous to the run like
Episode Counters and Statistics update
*/
tradingSystemModule.mantain()
/* Episode Counters and Statistics update */
tradingEpisodeModule.mantain()
/* Run your maintaince procedure */
tradingEngineModule.mantain()
/* Run one cycle of the Trading System*/
/* Reset Data Structures */
tradingSystemModule.reset()
tradingEpisodeModule.reset()
tradingEngineModule.reset()
/*
Run the first cycle of the Trading System. In this first cycle we
give some room so that orders can be canceled or filled and we can
write those records into the output memory.
*/
tradingEngineModule.setCurrentCycle('First')
await tradingSystemModule.run()
/* Add new records to the process output */
tradingRecordsModule.appendRecords('First')
/* Reset Data Structures */
tradingSystemModule.reset()
tradingEpisodeModule.reset()
tradingEngineModule.reset()
/*
Run the second cycle of the Trading System. During this second run
some new orders might be created at slots freed up during the first
run. This allows for example for an Limit Order to be cancelled during the
first run, and the same Limit Order definition to spawn a new order
without the need to wait until the next candle.
*/
tradingEngineModule.setCurrentCycle('Second')
await tradingSystemModule.run()
controlLoop()
@@ -171,11 +200,16 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
if (tradingEngine.current.episode.candle.index.value + 1 < candles.length) {
/* Add new records to the process output */
tradingRecordsModule.appendRecords()
tradingRecordsModule.appendRecords('Second')
tradingEngine.current.episode.candle.index.value++
setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed.
/*
This will execute the next loop in the next iteration of the NodeJs event loop
allowing for other callbacks to be executed. It also prevents the error
'Maximum call stack size exceeded', since the call is not placed at the call stack.
*/
setImmediate(loop)
} else {
updateEpisode('All Candles Processed')
}
@@ -186,7 +220,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
tradingEpisodeModule.closeEpisode()
/* Add new records to the process output */
tradingRecordsModule.appendRecords()
tradingRecordsModule.appendRecords('Second')
afterLoop()
}
@@ -195,7 +229,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
tradingEpisodeModule.updateExitType(exitType)
/* Add new records to the process output */
tradingRecordsModule.appendRecords()
tradingRecordsModule.appendRecords('Second')
afterLoop()
}
+295 -324
View File
@@ -6,6 +6,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
let thisObject = {
updateChart: updateChart,
mantain: mantain,
reset: reset,
runTriggerStage: runTriggerStage,
runOpenStage: runOpenStage,
runManageStage: runManageStage,
@@ -88,11 +89,18 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingStrategyModule.mantain()
tradingExecutionModule.mantain()
resetTradingEngineDataStructure()
updateCounters()
updateEnds()
}
function reset() {
tradingPositionModule.reset()
tradingStrategyModule.reset()
tradingExecutionModule.reset()
resetTradingEngineDataStructure()
}
function runTriggerStage() {
/*
We check if we will be triggering on, off or taking position.
@@ -251,149 +259,119 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
async function runOpenStage() {
/* Abort Open Stage Check */
if (tradingEngine.current.strategyOpenStage.status.value === 'Open' && tradingEngine.current.strategyCloseStage.status.value === 'Open') {
/*
if the Close stage is opened while the open stage is still open that means that
we need to stop placing orders, check what happened to the orders already placed,
and cancel all unfilled limit orders. We call this the Closing status of the Open Stage.
*/
changeStageStatus('Open Stage', 'Closing')
}
/* Opening Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Opening') {
checkIfWeNeedToAbortTheStage()
runWhenStatusIsOpening()
await runWhenStatusIsOpen()
await runWhenStatusIsClosing()
/* This procedure is intended to run only once */
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let executionNode = stageNode.openExecution
/* Reset the Exchange Orders data structure to its initial value */
tradingEngineModule.initializeNode(tradingEngine.exchangeOrders)
tradingSystem.evalConditions(stageNode, 'Initial Definition')
tradingSystem.evalFormulas(stageNode, 'Initial Definition')
/* Remember the balance we had before taking the position to later calculate profit or loss */
tradingEngine.current.position.positionBaseAsset.beginBalance = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.beginBalance = tradingEngine.current.episode.episodeQuotedAsset.balance.value
/* Entry Position size and rate */
tradingSystem.evalFormulas(stageNode, 'Initial Targets')
tradingPositionModule.initialTargets(stageNode)
initializeStageTargetSize()
/* Check Execution at open stage node */
tradingSystem.evalConditions(stageNode, 'Open Execution')
tradingSystem.evalFormulas(stageNode, 'Open Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyOpenStage
)
/* From here on, the state is officialy Open */
changeStageStatus('Open Stage', 'Open')
return
}
/* Open Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
/*
While the Open Stage is Open, we do our regular stuff: place orders and check
what happened to the orders already placed.
*/
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let executionNode = stageNode.openExecution
/* Every Loop Cycle Activity */
tradingSystem.evalConditions(stageNode, 'Open Execution')
tradingSystem.evalFormulas(stageNode, 'Open Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyOpenStage
)
/*
The Open is finished when the placedSize reaches the targetSize in any of the
stage assets, and the fillSize + feesPaid reaches the placedSize also in any
of the stage Assets.
This can happens at any time when we update the sizeFilled and the feesPaid values
when we see at the exchange that orders were filled.
*/
if (
(
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.targetSize
||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.targetSize
)
&&
(
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
)
) {
changeStageStatus('Open Stage', 'Closed', 'Position Size Filled')
} else {
/* Check the Close Stage Event */
tradingSystem.evalConditions(stageNode, 'Close Stage Event')
if (checkStopStageEvent(stageNode) === true) {
changeStageStatus('Open Stage', 'Closing', 'Close Stage Event')
}
function checkIfWeNeedToAbortTheStage() {
/* Abort Open Stage Check */
if (tradingEngine.current.strategyOpenStage.status.value === 'Open' && tradingEngine.current.strategyCloseStage.status.value === 'Open') {
/*
if the Close stage is opened while the open stage is still open that means that
we need to stop placing orders, check what happened to the orders already placed,
and cancel all unfilled limit orders. We call this the Closing status of the Open Stage.
*/
changeStageStatus('Open Stage', 'Closing')
}
}
/* Closing Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Closing') {
/*
During the closing stage status, we do not place new orders, just check
if the ones placed were filled, and cancel the ones not filled.
*/
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let executionNode = stageNode.openExecution
function runWhenStatusIsOpening() {
/* Opening Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Opening') {
/*
Check if there are unfilled orders, we will check if they were executed,
and cancel the ones that were not.
*/
tradingSystem.evalConditions(stageNode, 'Open Execution')
tradingSystem.evalFormulas(stageNode, 'Open Execution')
/* This procedure is intended to run only once */
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let tradingEngineStage = tradingEngine.current.strategyOpenStage
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyOpenStage
)
/* Reset the Exchange Orders data structure to its initial value */
tradingEngineModule.initializeNode(tradingEngine.exchangeOrders)
/*
The Closing is finished when the fillSize + feesPaid reaches the ordersSize.
This can happens either because we update the sizeFilled value when we see
at the exchange that orders were filled, or we reduce the ordersSize when
we cancel not yet filled orders. Here it does not matter the targetSize since
this status represent a force closure of this stage.
*/
if (
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
/* Entry Position size and rate */
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
tradingPositionModule.initialTargets(tradingSystemStage, tradingEngineStage)
initializeStageTargetSize()
||
/* From here on, the stage is officialy Open */
changeStageStatus('Open Stage', 'Open')
}
}
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
) {
changeStageStatus('Open Stage', 'Closed')
async function runWhenStatusIsOpen() {
/* Open Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
/*
While the Open Stage is Open, we do our regular stuff: place orders and check
what happened to the orders already placed.
*/
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let tradingEngineStage = tradingEngine.current.strategyOpenStage
let executionNode = tradingSystemStage.openExecution
/* Evaluate conditions and formulas so they are ready during the execution run */
tradingSystem.evalConditions(executionNode, 'Open Execution')
tradingSystem.evalFormulas(executionNode, 'Open Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngineStage
)
checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Open Stage')
}
}
async function runWhenStatusIsClosing() {
/* Closing Status Procedure */
if (tradingEngine.current.strategyOpenStage.status.value === 'Closing') {
/*
During the closing stage status, we do not place new orders, just check
if the ones placed were filled, and cancel the ones not filled.
*/
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let executionNode = tradingSystemStage.openExecution
/*
Check if there are unfilled orders, we will check if they were executed,
and cancel the ones that were not.
*/
tradingSystem.evalConditions(tradingSystemStage, 'Open Execution')
tradingSystem.evalFormulas(tradingSystemStage, 'Open Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyOpenStage
)
/*
The Closing is finished when the sizeFilled + feesPaid reaches the sizePlaced.
This can happens either because we update the sizeFilled value when we see
at the exchange that orders were filled, or we reduce the sizePlaced when
we cancel not yet filled orders. Here it does not matter the targetSize since
this status represent a force closure of this stage.
*/
switch (tradingEngine.current.strategyOpenStage.stageDefinedIn.value) {
case 'Base Asset': {
if (
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
) {
changeStageStatus('Open Stage', 'Closed')
}
break
}
case 'Quoted Asset': {
if (
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
) {
changeStageStatus('Open Stage', 'Closed')
}
break
}
}
}
}
@@ -410,57 +388,50 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
function runManageStage() {
/* Opening Status Procedure */
if (tradingEngine.current.strategyManageStage.status.value === 'Opening') {
/* We jump the management at the loop cycle where the position is being taken. */
changeStageStatus('Manage Stage', 'Open')
return
runWhenStatusIsOpening()
runWhenStatusIsOpen()
function runWhenStatusIsOpening() {
/* Opening Status Procedure */
if (tradingEngine.current.strategyManageStage.status.value === 'Opening') {
/* Now we switch to the Open status. */
changeStageStatus('Manage Stage', 'Open')
}
}
/* Open Status Procedure */
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let manageStage = strategy.manageStage
tradingSystem.evalConditions(manageStage, 'Manage Stage')
tradingSystem.evalFormulas(manageStage, 'Manage Stage')
function runWhenStatusIsOpen() {
/* Open Status Procedure */
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let manageStage = strategy.manageStage
if (
tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' ||
tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage') {
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
tradingSystem.evalConditions(stageNode, 'Initial Definition')
tradingSystem.evalFormulas(stageNode, 'Initial Definition')
/* Evaluate all the stage conditions and formulas to have them ready */
tradingSystem.evalConditions(manageStage, 'Manage Stage')
tradingSystem.evalFormulas(manageStage, 'Manage Stage')
/* Stop Loss Management */
checkStopPhasesEvents()
calculateStopLoss()
calculateStopLossPosition()
/* Take Profit Management */
checkTakeProfitPhaseEvents()
calculateTakeProfit()
calculateTakeProfitPosition()
/* Checking if Stop or Take Profit were hit */
checkStopLossOrTakeProfitWasHit()
}
/* Stop Loss Management */
checkStopPhasesEvents()
calculateStopLoss()
calculateStopLossPosition()
/* Take Profit Management */
checkTakeProfitPhaseEvents()
calculateTakeProfit()
calculateTakeProfitPosition()
/* Checking if Stop or Take Profit were hit */
checkStopLossOrTakeProfitWasHit()
}
function calculateStopLoss() {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let openStage = strategy.openStage
let manageStage = strategy.manageStage
let phase
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' && openStage !== undefined) {
if (openStage.initialDefinition !== undefined) {
if (openStage.initialDefinition.initialStopLoss !== undefined) {
phase = openStage.initialDefinition.initialStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value]
}
}
}
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Manage Stage' && manageStage !== undefined) {
if (manageStage !== undefined) {
if (manageStage.managedStopLoss !== undefined) {
phase = manageStage.managedStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value - 1]
}
@@ -480,19 +451,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
function calculateTakeProfit() {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let openStage = strategy.openStage
let manageStage = strategy.manageStage
let phase
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage' && openStage !== undefined) {
if (openStage.initialDefinition !== undefined) {
if (openStage.initialDefinition.initialTakeProfit !== undefined) {
phase = openStage.initialDefinition.initialTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value]
}
}
}
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Manage Stage' && manageStage !== undefined) {
if (manageStage !== undefined) {
if (manageStage.managedTakeProfit !== undefined) {
phase = manageStage.managedTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1]
}
@@ -513,7 +475,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
function calculateStopLossPosition() {
/*
The position of the Stop Loss (above or below the price) is needed in order to know
later if the price hit the Stop Loss or not at every Simulation cycle. When we get
later if the price hit the Stop Loss or not at every Simulation candle. When we get
the first values of the simulation of the Stop Loss we check if it is above or below
the Position Rate, and we assign the values Above or Below to it.
*/
@@ -529,7 +491,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
function calculateTakeProfitPosition() {
/*
The position of the Take Profit (above or below the price) is needed in order to know
later if the price hit the Take Profit or not at every Simulation cycle. When we get
later if the price hit the Take Profit or not at every Simulation candle. When we get
the first values of the simulation of the Take Profits we check if it is above or below
the Position Rates, and we assign the values Above or Below to it.
*/
@@ -544,28 +506,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
function checkStopPhasesEvents() {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let openStage = strategy.openStage
let manageStage = strategy.manageStage
let parentNode
let phaseIndex
let phase
let stopLoss
let stage
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' && openStage !== undefined) {
stage = openStage
if (openStage.initialDefinition !== undefined) {
if (openStage.initialDefinition.initialStopLoss !== undefined) {
parentNode = openStage.initialDefinition
phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value
stopLoss = openStage.initialDefinition.initialStopLoss
phase = stopLoss.phases[phaseIndex]
}
}
}
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Manage Stage' && manageStage !== undefined) {
stage = manageStage
if (manageStage !== undefined) {
if (manageStage.managedStopLoss !== undefined) {
parentNode = manageStage
phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value - 1
@@ -596,11 +543,14 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingSystem.highlights.push(phase.id)
tradingSystem.highlights.push(stopLoss.id)
tradingSystem.highlights.push(parentNode.id)
tradingSystem.highlights.push(stage.id)
tradingSystem.highlights.push(manageStage.id)
tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1, 'Manage Stage')
tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1)
announcementsModule.makeAnnoucements(nextPhaseEvent)
/* Reset this counter */
tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1
return // only one event can pass at the time
}
}
@@ -626,13 +576,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingSystem.highlights.push(phase.id)
tradingSystem.highlights.push(stopLoss.id)
tradingSystem.highlights.push(parentNode.id)
tradingSystem.highlights.push(stage.id)
tradingSystem.highlights.push(manageStage.id)
let moveToPhase = moveToPhaseEvent.referenceParent
if (moveToPhase !== undefined) {
for (let q = 0; q < stopLoss.phases.length; q++) {
if (stopLoss.phases[q].id === moveToPhase.id) {
tradingPositionModule.updateStopLoss(q + 1, 'Manage Stage')
tradingPositionModule.updateStopLoss(q + 1)
}
}
} else {
@@ -641,6 +591,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
announcementsModule.makeAnnoucements(moveToPhaseEvent)
/* Reset this counter */
tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1
return // only one event can pass at the time
}
}
@@ -657,22 +610,8 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
let phaseIndex
let phase
let takeProfit
let stage
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage' && openStage !== undefined) {
stage = openStage
if (openStage.initialDefinition !== undefined) {
if (openStage.initialDefinition.initialTakeProfit !== undefined) {
parentNode = openStage.initialDefinition
phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value
takeProfit = openStage.initialDefinition.initialTakeProfit
phase = takeProfit.phases[phaseIndex]
}
}
}
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Manage Stage' && manageStage !== undefined) {
stage = manageStage
if (manageStage !== undefined) {
if (manageStage.managedTakeProfit !== undefined) {
parentNode = manageStage
phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1
@@ -703,11 +642,14 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingSystem.highlights.push(phase.id)
tradingSystem.highlights.push(takeProfit.id)
tradingSystem.highlights.push(parentNode.id)
tradingSystem.highlights.push(stage.id)
tradingSystem.highlights.push(manageStage.id)
tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1, 'Manage Stage')
tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1)
announcementsModule.makeAnnoucements(nextPhaseEvent)
/* Reset this counter */
tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1
return // only one event can pass at the time
}
}
@@ -733,13 +675,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingSystem.highlights.push(phase.id)
tradingSystem.highlights.push(takeProfit.id)
tradingSystem.highlights.push(parentNode.id)
tradingSystem.highlights.push(stage.id)
tradingSystem.highlights.push(manageStage.id)
let moveToPhase = moveToPhaseEvent.referenceParent
if (moveToPhase !== undefined) {
for (let q = 0; q < takeProfit.phases.length; q++) {
if (takeProfit.phases[q].id === moveToPhase.id) {
tradingPositionModule.updateTakeProfit(q + 1, 'Manage Stage')
tradingPositionModule.updateTakeProfit(q + 1)
}
}
} else {
@@ -748,6 +690,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
announcementsModule.makeAnnoucements(moveToPhaseEvent)
/* Reset this counter */
tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1
return // only one event can pass at the time
}
}
@@ -809,105 +754,87 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
async function runCloseStage() {
/* Opening Status Procedure */
if (tradingEngine.current.strategyCloseStage.status.value === 'Opening') {
/*
This will happen only once, as soon as the Take Profit or Stop was hit.
We wil not be placing orders at this time because we do not know
the total filled size of the Open Stage. By skiping execution now
we allow the open stage to get a final value for sizeFilled that we can use.
*/
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
/* Exit Position size and rate */
tradingSystem.evalFormulas(stageNode, 'Initial Targets')
tradingPositionModule.initialTargets(stageNode)
runWhenStatusIsOpening()
await runWhenStatusIsOpen()
initializeStageTargetSize()
changeStageStatus('Close Stage', 'Open')
return
}
function runWhenStatusIsOpening() {
/* Opening Status Procedure */
if (tradingEngine.current.strategyCloseStage.status.value === 'Opening') {
/*
This will happen only once, as soon as the Take Profit or Stop was hit.
*/
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
/* Open Status Procedure */
if (tradingEngine.current.strategyCloseStage.status.value === 'Open') {
/*
This will happen as long as the Close Stage is Open.
*/
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
let executionNode = stageNode.closeExecution
/* Exit Position size and rate */
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
tradingPositionModule.initialTargets(tradingSystemStage, tradingEngine.current.strategyCloseStage)
tradingSystem.evalConditions(stageNode, 'Close Execution')
tradingSystem.evalFormulas(stageNode, 'Close Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyCloseStage
)
/*
The Open is finished when the placedSize reaches the targetSize in any of the
stage assets, and the fillSize + feesPaid reaches the placedSize also in any
of the stage Assets.
This can happens at any time when we update the sizeFilled and the feesPaid values
when we see at the exchange that orders were filled.
*/
if (
(
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizePlaced >=
tradingEngine.current.strategyCloseStage.stageBaseAsset.targetSize
||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizePlaced >=
tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize
)
&&
(
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizeFilled.value +
tradingEngine.current.strategyCloseStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizePlaced.value
||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizeFilled.value +
tradingEngine.current.strategyCloseStage.stageQuotedAsset.feesPaid.value >=
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizePlaced.value
)
) {
changeStageStatus('Close Stage', 'Closed', 'Size Placed Filled')
} else {
/* Check the Close Stage Event */
tradingSystem.evalConditions(stageNode, 'Close Stage Event')
if (checkStopStageEvent(stageNode) === true) {
changeStageStatus('Close Stage', 'Closing', 'Close Stage Event')
}
initializeStageTargetSize()
changeStageStatus('Close Stage', 'Open')
}
}
/* Exit Position Validation */
if (
async function runWhenStatusIsOpen() {
/*
The following code will run only when the Open Stage is closed.
The reason for that is that we need the final filled size of the Open Stage
to be known before we start placing orders at the Close Stage.
*/
if (
tradingEngine.current.strategyCloseStage.status.value === 'Open' &&
(
tradingEngine.current.strategyTriggerStage.status.value === 'Closed' ||
tradingEngine.current.strategyTriggerStage.status.value === tradingEngine.current.strategyTriggerStage.status.config.initialValue
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
tradingEngine.current.strategyOpenStage.status.value == tradingEngine.current.strategyOpenStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
tradingEngine.current.strategyOpenStage.status.value === tradingEngine.current.strategyOpenStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyManageStage.status.value === 'Closed' ||
tradingEngine.current.strategyManageStage.status.value === tradingEngine.current.strategyManageStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyCloseStage.status.value === 'Closed'
)
) {
) {
/*
This will happen as long as the Close Stage is Open.
*/
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
let tradingEngineStage = tradingEngine.current.strategyCloseStage
let executionNode = tradingSystemStage.closeExecution
/* Exiting Everything now. */
exitPositionAndStrategy()
tradingSystem.evalConditions(tradingSystemStage, 'Close Execution')
tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
await tradingExecutionModule.runExecution(
executionNode,
tradingEngine.current.strategyCloseStage
)
checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Close Stage')
}
/*
Exit Position Validation: we need al the previous stages to be either closed
or at their initial default value. The last is becasue after Closed objects are
initialized to their defualts.
*/
if (
(
tradingEngine.current.strategyTriggerStage.status.value === 'Closed' ||
tradingEngine.current.strategyTriggerStage.status.value === tradingEngine.current.strategyTriggerStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
tradingEngine.current.strategyOpenStage.status.value === tradingEngine.current.strategyOpenStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyManageStage.status.value === 'Closed' ||
tradingEngine.current.strategyManageStage.status.value === tradingEngine.current.strategyManageStage.status.config.initialValue
)
&&
(
tradingEngine.current.strategyCloseStage.status.value === 'Closed'
)
) {
/* Closing Everything now. */
closePositionAndStrategy()
}
}
function initializeStageTargetSize() {
@@ -921,8 +848,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize.value = global.PRECISE(tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize.value, 10)
}
function exitPositionAndStrategy() {
function closePositionAndStrategy() {
/* Taking Position Snapshot */
if (bot.SESSION.type === 'Backtesting Session') {
if (sessionParameters.snapshots !== undefined) {
@@ -957,8 +883,53 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
}
function checkStopStageEvent(stage) {
function checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, stageName) {
/*
The Stage is closed when the placedSize reaches the targetSize, and the fillSize
+ feesPaid reaches the placedSize. This depends on how the user defined the stage:
either on Base Asset or Quoted Asset.
This can happens at any time when we update the sizeFilled and the feesPaid values
when we see at the exchange that orders were filled.
*/
switch (tradingEngineStage.stageDefinedIn.value) {
case 'Base Asset': {
if (
tradingEngineStage.stageBaseAsset.sizeFilled.value +
tradingEngineStage.stageBaseAsset.feesPaid.value >=
tradingEngineStage.stageBaseAsset.targetSize.value
) {
positionFilled()
} else {
checkCloseStageEvent(tradingSystemStage)
}
break
}
case 'Quoted Asset': {
if (
tradingEngineStage.stageQuotedAsset.sizeFilled.value +
tradingEngineStage.stageQuotedAsset.feesPaid.value >=
tradingEngineStage.stageQuotedAsset.targetSize.value
) {
positionFilled()
} else {
checkCloseStageEvent(tradingSystemStage)
}
break
}
}
function positionFilled() {
changeStageStatus(stageName, 'Closed', 'Position Size Filled')
}
function checkCloseStageEvent(tradingSystemStage) {
/* Check the Close Stage Event */
tradingSystem.evalConditions(tradingSystemStage, 'Close Stage Event')
if (checkStopStageEvent(tradingSystemStage) === true) {
changeStageStatus(stageName, 'Closing', 'Close Stage Event')
}
}
}
function checkStopStageEvent(stage) {
/* Check the Close Stage Event. */
let stopStageEvent = stage.stopStageEvent
if (stopStageEvent !== undefined) {
@@ -1017,33 +988,33 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
function openStage(stage) {
/* Recording the opening at the Trading Engine Data Structure */
stage.begin.value = tradingEngine.current.episode.candle.begin.value
stage.begin.value = tradingEngine.current.episode.cycle.begin.value
stage.beginRate.value = tradingEngine.current.episode.candle.close.value
stage.end.value = tradingEngine.current.episode.candle.end.value
stage.end.value = tradingEngine.current.episode.cycle.end.value
}
function closeStage(stage) {
/* Recording the closing at the Trading Engine Data Structure */
stage.end.value = tradingEngine.current.episode.candle.end.value
stage.end.value = tradingEngine.current.episode.cycle.end.value
stage.endRate.value = tradingEngine.current.episode.candle.close.value
}
}
function updateEnds() {
if (tradingEngine.current.strategyTriggerStage.status.value === 'Open') {
tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.episode.cycle.end.value
tradingEngine.current.strategyTriggerStage.endRate.value = tradingEngine.current.episode.candle.close.value
}
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.episode.cycle.end.value
tradingEngine.current.strategyOpenStage.endRate.value = tradingEngine.current.episode.candle.close.value
}
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.episode.cycle.end.value
tradingEngine.current.strategyManageStage.endRate.value = tradingEngine.current.episode.candle.close.value
}
if (tradingEngine.current.strategyCloseStage.status.value === 'Open') {
tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.episode.cycle.end.value
tradingEngine.current.strategyCloseStage.endRate.value = tradingEngine.current.episode.candle.close.value
}
}
@@ -5,6 +5,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
const MODULE_NAME = 'Trading Strategy'
let thisObject = {
mantain: mantain,
reset: reset,
openStrategy: openStrategy,
closeStrategy: closeStrategy,
initialize: initialize,
@@ -24,16 +25,19 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
}
function mantain() {
resetTradingEngineDataStructure()
updateCounters()
updateEnds()
}
function reset() {
resetTradingEngineDataStructure()
}
function openStrategy(index, situationName, strategyName) {
tradingEngine.current.strategy.status.value = 'Open'
tradingEngine.current.strategy.serialNumber.value = tradingEngine.current.episode.episodeCounters.strategies.value
tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID()
tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.begin.value
tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.strategy.beginRate.value = tradingEngine.current.episode.candle.min.value
+12 -11
View File
@@ -6,6 +6,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
const MODULE_NAME = 'Trading System'
let thisObject = {
mantain: mantain,
reset: reset,
run: run,
updateChart: updateChart,
initialize: initialize,
@@ -97,19 +98,19 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
function mantain() {
reset()
tradingStagesModule.mantain()
}
function reset() {
/* This function helps reset data structures at every cycle of the simulation loop/=. */
tradingSystem.highlights = []
tradingSystem.errors = []
tradingSystem.values = []
tradingSystem.status = []
tradingSystem.progress = []
tradingSystem.running = []
tradingSystem.announcements = []
}
function reset() {
tradingStagesModule.reset()
tradingSystem.highlights = []
tradingSystem.errors = []
tradingSystem.values = []
tradingSystem.status = []
tradingSystem.progress = []
tradingSystem.running = []
tradingSystem.announcements = []
}
function updateChart(pChart) {
+14
View File
@@ -160,6 +160,20 @@
finalDatetime: (new Date()).valueOf()
}
}
} else {
/* Check that we received valid dates */
if(isNaN(new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) {
let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date."
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage)
bot.sessionError(bot.SESSION.parameters, errorMessage)
return false
}
if(isNaN(new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) {
let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date."
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage)
bot.sessionError(bot.SESSION.parameters, errorMessage)
return false
}
}
/* Session Type Forced Values */
+2
View File
@@ -1,3 +1,5 @@
/* Callbacks default responses. */
global.DEFAULT_OK_RESPONSE = {
result: "Ok",