Implementation Finished
This commit is contained in:
1 parent
e84f8a6ed1
commit
6ec5580cd4
32 files changed
+1335
-6639
No files matched your search
+473
-470
File diff suppressed because it is too large.
Load diff
@@ -19,14 +19,14 @@ function newAppLoader() {
|
||||
|
||||
let plotters
|
||||
let modulesArray = []
|
||||
|
||||
|
||||
plotters = defaultPlotters()
|
||||
|
||||
|
||||
modulesArray = modulesArray.concat([
|
||||
'ChartingSpace/Viewport.js',
|
||||
|
||||
|
||||
'Globals.js',
|
||||
|
||||
|
||||
'AppSchema.js',
|
||||
'Workspace.js',
|
||||
|
||||
@@ -44,16 +44,16 @@ function newAppLoader() {
|
||||
'DesignSpace/Workspace/FunctionLibraries/NodeDeleter.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/NodeCloning.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/NodeChildren.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/NodeChildren.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/OnFocus.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/SuperScripts.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/CCXTFunctions.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/WebhookFunctions.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/WebhookFunctions.js',
|
||||
'DesignSpace/Workspace/FunctionLibraries/DependenciesFilter.js',
|
||||
|
||||
|
||||
'Utilities/CoordinateTransformations.js',
|
||||
'Utilities/DateRateTransformations.js',
|
||||
'Utilities/Download.js',
|
||||
@@ -68,7 +68,7 @@ function newAppLoader() {
|
||||
'Panels/Layer.js',
|
||||
'Panels/PlotterPanel.js',
|
||||
'Panels/PanelsVisibleButton.js',
|
||||
|
||||
|
||||
'SideSpace/SidePanelTab.js',
|
||||
'SideSpace/ListView.js',
|
||||
'SideSpace/ListItem.js',
|
||||
@@ -98,7 +98,7 @@ function newAppLoader() {
|
||||
'FloatingSpace/CircularMenuItem.js',
|
||||
'FloatingSpace/CircularProgressBar.js',
|
||||
'FloatingSpace/BusyProgressBar.js',
|
||||
'FloatingSpace/CodeEditor.js',
|
||||
'FloatingSpace/CodeEditor.js',
|
||||
'FloatingSpace/ConfigEditor.js',
|
||||
'FloatingSpace/ConditionEditor.js',
|
||||
'FloatingSpace/FormulaEditor.js',
|
||||
@@ -109,7 +109,7 @@ function newAppLoader() {
|
||||
'Scales/TimeFrameScale.js',
|
||||
'Scales/Commons.js',
|
||||
'Scales/AutoScaleButton.js',
|
||||
|
||||
|
||||
'CockpitSpace/AssetBalances.js',
|
||||
'CockpitSpace/Speedometer.js',
|
||||
'CockpitSpace/FullScreen.js',
|
||||
@@ -187,23 +187,14 @@ function newAppLoader() {
|
||||
}
|
||||
|
||||
function defaultPlotters() {
|
||||
return [
|
||||
return [
|
||||
'Plotters/Masters/Plotters-Candles-Volumes/Candles.js',
|
||||
'Plotters/Masters/Plotters-Candles-Volumes/CandlePanel.js',
|
||||
'Plotters/Masters/Plotters-Candles-Volumes/Volumes.js',
|
||||
'Plotters/Masters/Plotters-Candles-Volumes/VolumePanel.js',
|
||||
'Plotters/Masters/Plotters-Bollinger-Bands/BollingerBands.js',
|
||||
'Plotters/Masters/Plotters-Bollinger-Bands/BollingerBandsPanel.js',
|
||||
'Plotters/Masters/Plotters-Bollinger-Bands/PercentageBandwidth.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/TradingSimulation.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/TradingSimulationAssetBalancesPanel.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/TradingSimulationCurrentPositionPanel.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/TradingSimulationEpisodePanel.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/Conditions.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/Strategies.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/Trades.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/SimulationExecution.js',
|
||||
'Plotters/TradingEngines/Plotters-Trading-Simulation/SimulationExecutionPanel.js'
|
||||
'Plotters/Masters/Plotters-Bollinger-Bands/PercentageBandwidth.js'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ function newSessionFunctions () {
|
||||
'Close Stage->Close Execution->' +
|
||||
'Announcement->Formula->' +
|
||||
'Execution Algorithm->Market Buy Order->Market Sell Order->Limit Buy Order->Limit Sell Order->' +
|
||||
'Create Order Event->Move Order Event->Cancel Order Event->' +
|
||||
'Create Order Event->Cancel Order Event->' +
|
||||
'Position Size->Position Rate->Formula->' +
|
||||
'Situation->Condition->Javascript Code->' +
|
||||
'Market Order->Limit Order->' +
|
||||
@@ -57,7 +57,7 @@ function newSessionFunctions () {
|
||||
'Begin->End->Open->Close->Min->Max->Index->' +
|
||||
'Balance->Base Asset->Quoted Asset->' +
|
||||
'Distance To Event->' +
|
||||
'Trigger On->Trigger Off->Take Position->Close Position->Next Phase->Move To Phase->Create Order->Move Order->Cancel Order->Close Order->' +
|
||||
'Trigger On->Trigger Off->Take Position->Close Position->Next Phase->Move To Phase->Create Order->Cancel Order->Close Order->' +
|
||||
'Strategy->' +
|
||||
'Serial Number->Identifier->Begin->End->Strategy Name->Index->Trigger Stage Status->Open Stage Status->Manage Stage Status->Close Stage Status->Status->Begin Rate->End Rate->Situation Name->' +
|
||||
'Strategy Counters->Periods->' +
|
||||
@@ -70,9 +70,9 @@ function newSessionFunctions () {
|
||||
'Formula->' +
|
||||
'Exchange Orders->Market Buy Orders->Market Sell Orders->Limit Buy Orders->Limit Sell Orders->' +
|
||||
'Market Order->Limit Order->' +
|
||||
'Serial Number->Identifier->Begin->End->Rate->Size->Exit Type->Status->Order Name->Algorithm Name->Situation Name->' +
|
||||
'Serial Number->Identifier->Exchange Id->Begin->End->Rate->Size->Exit Type->Status->Order Name->Algorithm Name->Situation Name->' +
|
||||
'Order Counters->Periods->' +
|
||||
'Order Statistics->Percentage Filled->Actual Rate->Fees Paid->Days->User Defined Statistic->'
|
||||
'Order Statistics->Size Filled->Percentage Filled->Amount Received->Actual Rate->Fees Paid->Days->User Defined Statistic->'
|
||||
|
||||
let tradingEngine = functionLibraryProtocolNode.getProtocolNode(node.tradingEngineReference.payload.referenceParent, false, true, true, false, false, lightingPath)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ function newTaskFunctions () {
|
||||
'Trading Bot Instance->' +
|
||||
'Sensor Process Instance->Indicator Process Instance->Trading Process Instance->' +
|
||||
'Market Reference->' +
|
||||
'Key Instance->Exchange Account Key->' +
|
||||
'Key Reference->Exchange Account Key->' +
|
||||
'Market->Exchange Markets->Crypto Exchange->' +
|
||||
'Market Base Asset->Market Quoted Asset->Asset->' +
|
||||
'Backtesting Session->Live Trading Session->Paper Trading Session->Fordward Testing Session->' +
|
||||
|
||||
@@ -156,7 +156,7 @@ function newEventsServerClient (host, port, hostName) {
|
||||
}
|
||||
|
||||
if (thisObject.isConnected() !== true) {
|
||||
canvas.cockpitSpace.setStatus('Connecting to ' + hostName + ' Network Node. Please wait until the connection is established.', 100, canvas.cockpitSpace.statusTypes.WARNING)
|
||||
canvas.cockpitSpace.setStatus('Connecting to Superalgos Backend at ' + hostName + ' Network Node. Please wait until the connection is established.', 100, canvas.cockpitSpace.statusTypes.WARNING)
|
||||
} else {
|
||||
retryCommandsPhysics()
|
||||
}
|
||||
|
||||
@@ -305,6 +305,7 @@ 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)
|
||||
@@ -550,16 +551,16 @@ function newPlotter () {
|
||||
|
||||
let atMousePosition = false
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
if (record.rate === undefined) {
|
||||
/* Current Record depends only on begin and end. */
|
||||
atMousePosition = true
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', record)
|
||||
} else {
|
||||
if (record.rate !== undefined && productDefinition.referenceParent.config.useRateAtMousePosition === true) {
|
||||
/* Current Record depends also on rate */
|
||||
if (record.rate >= minUserPositionRate && record.rate <= maxUserPositionRate) {
|
||||
atMousePosition = true
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', record)
|
||||
}
|
||||
} else {
|
||||
/* Current Record depends only on begin and end. */
|
||||
atMousePosition = true
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', record)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ function newRecordValues () {
|
||||
let tradingSystem
|
||||
let tradingEngine
|
||||
let productDefinition
|
||||
let productTargetNode
|
||||
let propertyTargetNodeMap = new Map()
|
||||
return thisObject
|
||||
|
||||
@@ -20,7 +19,6 @@ function newRecordValues () {
|
||||
tradingSystem = undefined
|
||||
tradingEngine = undefined
|
||||
productDefinition = undefined
|
||||
productTargetNode = undefined
|
||||
propertyTargetNodeMap = undefined
|
||||
}
|
||||
|
||||
@@ -28,15 +26,45 @@ function newRecordValues () {
|
||||
tradingSystem = canvas.designSpace.workspace.getHierarchyHeadsById(pTradingSystem.id)
|
||||
tradingEngine = canvas.designSpace.workspace.getHierarchyHeadsById(pTradingEngine.id)
|
||||
productDefinition = pProductDefinition
|
||||
productTargetNode = eval(productDefinition.config.nodePath)
|
||||
|
||||
for (let i = 0; i < productDefinition.record.properties.length; i++) {
|
||||
let property = productDefinition.record.properties[i]
|
||||
if (property.config.nodePath !== undefined) {
|
||||
let propertyRoot = eval(property.config.nodePath)
|
||||
propertyTargetNodeMap.set(property.id, propertyRoot)
|
||||
} else {
|
||||
propertyTargetNodeMap.set(property.id, productTargetNode)
|
||||
/*
|
||||
The product root can be a node or a node property of type array.
|
||||
*/
|
||||
let productRoot = eval(productDefinition.config.nodePath)
|
||||
|
||||
if (productDefinition.config.nodePathType === 'array') {
|
||||
/*
|
||||
This means that the configured nodePath is not pointing to a node, but to a node property that is an array.
|
||||
For that reason we will assume that each element of the array is a record to be outputed
|
||||
*/
|
||||
for (let index = 0; index < productRoot.length; index++) {
|
||||
/*
|
||||
The Product Root Node is the root of the node hiriarchy from where we are going to extract the record values.
|
||||
*/
|
||||
let productRootNode = productRoot[index]
|
||||
scanProperties(productRootNode, index)
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
This means that the configured nodePath points to a single node, which is the one whose children constitutes
|
||||
the record to be saved at the output file.
|
||||
*/
|
||||
/*
|
||||
The Product Root Node is the root of the node hiriarchy from where we are going to extract the record values.
|
||||
*/
|
||||
let productRootNode = productRoot
|
||||
scanProperties(productRootNode)
|
||||
}
|
||||
|
||||
function scanProperties (productRootNode, index) {
|
||||
for (let i = 0; i < productDefinition.record.properties.length; i++) {
|
||||
let property = productDefinition.record.properties[i]
|
||||
if (property.config.nodePath !== undefined) {
|
||||
let propertyRoot = eval(property.config.nodePath)
|
||||
propertyTargetNodeMap.set(property.id, propertyRoot)
|
||||
} else {
|
||||
propertyTargetNodeMap.set(property.id, productRootNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"type":"Data Mine","name":"Masters-Trading","config":"{ \n\"codeName\": \"TradingEngines\"\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":17465.0560010716,"y":15761.80866630885},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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":17420.451238280977,"y":15628.427761889405},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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":17383.917462721052,"y":15492.901891955118},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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":17355.49283689082,"y":15355.49978818897},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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":17335.263294947134,"y":15216.672475664938},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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":17323.2958161203,"y":15076.898492832226},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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 Current Balance","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":17319.630690639566,"y":14936.910427852989},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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 Current Balance","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":17324.28043052108,"y":14796.710145505635},"targetPosition":{"x":19686.312348759417,"y":14945.132313749935},"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-aLine 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":17450.068251326462,"y":15841.551559017382},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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":17408.574644476477,"y":15727.325552793509},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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":17373.12088415627,"y":15611.394883926749},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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":17343.701241214498,"y":15493.846491633021},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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":17320.378252589224,"y":15374.95820021913},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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":17303.209229100234,"y":15255.03551122105},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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 Current Balance","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":17292.23789362744,"y":15134.392617857315},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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 Current Balance","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":17287.49247877813,"y":15013.347651781945},"targetPosition":{"x":19639.44096944686,"y":14981.892188598802},"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-a465-Line truncated
|
||||
File diff suppressed because it is too large.
Load diff
-799
@@ -1,799 +0,0 @@
|
||||
function newTradingEnginesPlottersTradingSimulationSimulationExecution() {
|
||||
|
||||
const MODULE_NAME = "Simulation Execution Plotter";
|
||||
const INTENSIVE_LOG = false;
|
||||
const ERROR_LOG = true;
|
||||
const logger = newWebDebugLog();
|
||||
logger.fileName = MODULE_NAME;
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setDatetime: setDatetime,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
let container = newContainer(); // Do not touch this 3 lines, they are just needed.
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
let coordinateSystem
|
||||
|
||||
let timeFrame; // This will hold the current Time Frame the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let fileSequence;
|
||||
let plotElements = []; // This is where the elements to be plotted are stored before plotting.
|
||||
let plotLines = []; // Here we store the lines of open positions.
|
||||
|
||||
let previousNotesSetKey;
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let offsetChangedEventSubscriptionId
|
||||
let filesUpdatedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let userPositionDate
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
try {
|
||||
|
||||
/* Stop listening to the necesary events. */
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
|
||||
fileSequence.eventHandler.stopListening(filesUpdatedEventSubscriptionId);
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
|
||||
/* Destroyd References */
|
||||
|
||||
datetime = undefined;
|
||||
timeFrame = undefined;
|
||||
|
||||
fileSequence = undefined;
|
||||
plotElements = undefined;
|
||||
plotLines = undefined;
|
||||
|
||||
thisObject.fitFunction = undefined
|
||||
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = undefined
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> finalize -> err = " + err.stack.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
datetime = pDatetime;
|
||||
timeFrame = pTimeFrame;
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
fileSequence = pStorage.fileSequences[0];
|
||||
|
||||
recalculate();
|
||||
|
||||
filesUpdatedEventSubscriptionId = fileSequence.eventHandler.listenToEvent("Files Updated", onFilesUpdated); // Only the first sequence is supported right now.
|
||||
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
|
||||
/* Ready for when dimmension changes. */
|
||||
|
||||
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate();
|
||||
})
|
||||
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE);
|
||||
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); }
|
||||
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem() {
|
||||
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function finalizeCoordinateSystem() {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
}
|
||||
|
||||
function onScaleChanged() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function onMouseOver(event) {
|
||||
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
|
||||
userPositionDate = userPosition.valueOf()
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
try {
|
||||
let container;
|
||||
/* First we check if this point is inside this space. */
|
||||
if (this.container.frame.isThisPointHere(point) === true) {
|
||||
return this.container;
|
||||
} else {
|
||||
/* This point does not belong to this space. */
|
||||
return undefined;
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onFilesUpdated() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function setTimeFrame(pTimeFrame) {
|
||||
timeFrame = pTimeFrame;
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function setDatetime(pDatetime) {
|
||||
datetime = pDatetime;
|
||||
}
|
||||
|
||||
function setCoordinateSystem(pCoordinateSystem) {
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
}
|
||||
|
||||
function onViewportPositionChanged() {
|
||||
if (Math.random() * 100 > 95) {
|
||||
recalculate()
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
try {
|
||||
if (fileSequence === undefined) { return; }
|
||||
/*
|
||||
|
||||
We are going to filter the records depending on the Time Frame. We want that for a 1 min time peroid all the records appears on screen,
|
||||
but for higher periods, we will filter out some records, so that they do not overlap ever.
|
||||
|
||||
*/
|
||||
plotElements = [];
|
||||
plotLines = [];
|
||||
|
||||
let lastSellRate;
|
||||
let lastSellDate;
|
||||
let sellExecRate;
|
||||
|
||||
let lastBuyRate;
|
||||
let lastBuyDate;
|
||||
let buyExecRate;
|
||||
|
||||
let maxSequence = fileSequence.getFilesLoaded();
|
||||
|
||||
for (let j = 0; j < maxSequence; j++) {
|
||||
|
||||
let file = fileSequence.getFile(j);
|
||||
|
||||
let history = [];
|
||||
let lines = [];
|
||||
|
||||
/* First the small balls */
|
||||
|
||||
const ONE_MIN_IN_MILISECONDS = 60 * 1000;
|
||||
let step = timeFrame / ONE_MIN_IN_MILISECONDS;
|
||||
step = 1; // For now we dont discard records depending on the zoom level.
|
||||
|
||||
let i = 0;
|
||||
let lastRecordPushed = 0;
|
||||
|
||||
for (i = 0; i < file.length; i = i + step) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14],
|
||||
datetime: file[i][0]
|
||||
};
|
||||
|
||||
/* Finally we add this History Record to the Array */
|
||||
|
||||
history.push(newHistoryRecord);
|
||||
lastRecordPushed = i;
|
||||
|
||||
/* Here we build the lines. */
|
||||
|
||||
//if (timeFrame <= 1 * 60 * 1000) {
|
||||
|
||||
if (newHistoryRecord.lastSellRate > 0) {
|
||||
lastSellRate = newHistoryRecord.lastSellRate;
|
||||
lastSellDate = newHistoryRecord.date;
|
||||
}
|
||||
|
||||
if (newHistoryRecord.sellExecRate > 0) {
|
||||
let newLine = {
|
||||
type: "sell",
|
||||
x1: lastSellDate,
|
||||
y1: lastSellRate,
|
||||
x2: newHistoryRecord.date,
|
||||
y2: newHistoryRecord.sellExecRate
|
||||
};
|
||||
lines.push(newLine);
|
||||
}
|
||||
|
||||
if (newHistoryRecord.lastBuyRate > 0) {
|
||||
lastBuyRate = newHistoryRecord.lastBuyRate;
|
||||
lastBuyDate = newHistoryRecord.date;
|
||||
}
|
||||
|
||||
if (newHistoryRecord.buyExecRate > 0) {
|
||||
let newLine = {
|
||||
type: "buy",
|
||||
x1: lastBuyDate,
|
||||
y1: lastBuyRate,
|
||||
x2: newHistoryRecord.date,
|
||||
y2: newHistoryRecord.buyExecRate
|
||||
};
|
||||
lines.push(newLine);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
/* We allways want to put the last record of the file on the filterd dataset, so as to allways show the latest advance of the bot. */
|
||||
|
||||
i = file.length - 1;
|
||||
|
||||
if (lastRecordPushed !== i) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14]
|
||||
};
|
||||
|
||||
history.push(newHistoryRecord);
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < file.length; i++) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14]
|
||||
};
|
||||
|
||||
}
|
||||
plotElements.push(history);
|
||||
plotLines.push(lines);
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("History Changed", history);
|
||||
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> recalculate -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
plotChart();
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
try {
|
||||
|
||||
let record
|
||||
|
||||
let point = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
let history;
|
||||
|
||||
for (let j = 0; j < plotElements.length; j++) {
|
||||
|
||||
let history = plotElements[j];
|
||||
|
||||
for (let i = 0; i < history.length; i++) {
|
||||
|
||||
record = history[i];
|
||||
record = record
|
||||
|
||||
let timestamp = record.date // Math.trunc(record.date / timeFrame) * timeFrame + timeFrame / 2;
|
||||
|
||||
point = {
|
||||
x: timestamp,
|
||||
y: record.marketRate
|
||||
};
|
||||
|
||||
point = coordinateSystem.transformThisPoint(point);
|
||||
point = transformThisPoint(point, thisObject.container);
|
||||
|
||||
if (point.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { continue; }
|
||||
|
||||
point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point);
|
||||
point = thisObject.fitFunction(point);
|
||||
|
||||
let isCurrentRecord = false;
|
||||
|
||||
if (userPositionDate >= record.date - 30000 && userPositionDate <= record.date + 30000 - 1) {
|
||||
isCurrentRecord = true;
|
||||
}
|
||||
|
||||
let opacity = '1';
|
||||
let currentRadius = 10
|
||||
|
||||
/* Draw a red inverted triangle on exec sell */
|
||||
if (record.sellExecRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let normalSize = 5
|
||||
let extraSize = 0
|
||||
if (isCurrentRecord) { extraSize = 5 }
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
point1 = coordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = coordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = coordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
record.timeFrame = diff
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw a green triangle on exec buy */
|
||||
if (record.buyExecRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let normalSize = 5
|
||||
let extraSize = 0
|
||||
if (isCurrentRecord) { extraSize = 5 }
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
point1 = coordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = coordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = coordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
record.timeFrame = diff
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Draw a red inverted triangle on sell */
|
||||
if (record.lastSellRate > 0) {
|
||||
opacity = '1';
|
||||
|
||||
let normalSize = 5
|
||||
let extraSize = 0
|
||||
if (isCurrentRecord) { extraSize = 5 }
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
point1 = coordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = coordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = coordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
record.timeFrame = diff
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Draw a green triangle on buy */
|
||||
if (record.lastBuyRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let normalSize = 5
|
||||
let extraSize = 0
|
||||
if (isCurrentRecord) { extraSize = 5 }
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timeFrame / 7 * (normalSize + extraSize),
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
point1 = coordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = coordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = coordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
record.timeFrame = diff
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Send the current record to the panel */
|
||||
|
||||
if (userPositionDate >= record.date - 30000 && userPositionDate < record.date + 30000) {
|
||||
thisObject.container.eventHandler.raiseEvent("Current Record Changed", record);
|
||||
}
|
||||
|
||||
/* Circles */
|
||||
|
||||
let radius = 2;
|
||||
currentRadius = 10
|
||||
|
||||
/* Outer Circle */
|
||||
|
||||
if (isCurrentRecord === true) {
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
|
||||
browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true);
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
|
||||
browserCanvasContext.fill();
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Inner Circle */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
|
||||
browserCanvasContext.fill();
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw the lines connecting plot elements. */
|
||||
let lines = plotLines[j];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
|
||||
let line = lines[i];
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let point1 = {
|
||||
x: line.x1,
|
||||
y: line.y1
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: line.x2,
|
||||
y: line.y2
|
||||
};
|
||||
|
||||
point1 = coordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = coordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (line.type === "sell") {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([4, 1])
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> plotChart -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-465
@@ -1,465 +0,0 @@
|
||||
|
||||
function newTradingEnginesPlottersTradingSimulationSimulationExecutionSimulationExecutionPanel() {
|
||||
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
container: undefined,
|
||||
isVisible: true,
|
||||
onRecordChange: onRecordChange,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
};
|
||||
|
||||
let container = newContainer();
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
container.frame.containerName = "Trading Execution";
|
||||
|
||||
let record;
|
||||
let upDownButton
|
||||
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.isVisible = undefined
|
||||
|
||||
record = undefined
|
||||
upDownButton.finalize()
|
||||
upDownButton = undefined
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5;
|
||||
|
||||
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topLeft.x + thisObject.container.frame.width * 4;
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
|
||||
|
||||
upDownButton = newUpDownButton()
|
||||
upDownButton.parentContainer = thisObject.container
|
||||
upDownButton.container.frame.parentFrame = thisObject.container.frame
|
||||
upDownButton.fitFunction = thisObject.fitFunction
|
||||
upDownButton.initialize()
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
let container;
|
||||
|
||||
container = upDownButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onRecordChange(lastCurrentCandle) {
|
||||
|
||||
record = lastCurrentCandle;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function draw() {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
|
||||
|
||||
plot();
|
||||
|
||||
upDownButton.draw()
|
||||
|
||||
}
|
||||
|
||||
|
||||
function plot() {
|
||||
|
||||
if (record === undefined) { return; }
|
||||
|
||||
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
|
||||
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
|
||||
|
||||
const X_AXIS = thisObject.container.frame.width / 2;
|
||||
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
|
||||
|
||||
record = record
|
||||
|
||||
point = {
|
||||
x: X_AXIS,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
point = thisObject.container.frame.frameThisPoint(point);
|
||||
point = thisObject.fitFunction(point);
|
||||
|
||||
let isCurrentRecord = true;
|
||||
|
||||
let radius = 2;
|
||||
let currentRadius = 10
|
||||
let opacity = '1';
|
||||
|
||||
/* Draw a red inverted triangle on exec sell */
|
||||
if (record.sellExecRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let point1 = {
|
||||
x: X_AXIS,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: X_AXIS + record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: X_AXIS - record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw a green triangle on exec buy */
|
||||
if (record.buyExecRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let point1 = {
|
||||
x: X_AXIS,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: X_AXIS + record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: X_AXIS - record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Draw a red inverted triangle on sell */
|
||||
if (record.lastSellRate > 0) {
|
||||
opacity = '1';
|
||||
|
||||
let point1 = {
|
||||
x: X_AXIS,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: X_AXIS + record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: X_AXIS - record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Draw a green triangle on buy */
|
||||
if (record.lastBuyRate > 0) {
|
||||
|
||||
opacity = '1';
|
||||
|
||||
let point1 = {
|
||||
x: X_AXIS,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: X_AXIS + record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: X_AXIS - record.timeFrame / 4,
|
||||
y: Y_AXIS
|
||||
};
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1);
|
||||
point1 = thisObject.fitFunction(point1);
|
||||
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2);
|
||||
point2 = thisObject.fitFunction(point2);
|
||||
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3);
|
||||
point3 = thisObject.fitFunction(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
|
||||
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
browserCanvasContext.lineWidth = 0.25;
|
||||
|
||||
/* Outer Circle */
|
||||
|
||||
if (isCurrentRecord === true) {
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
|
||||
browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true);
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
|
||||
browserCanvasContext.fill();
|
||||
}
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
}
|
||||
|
||||
/* Inner Circle */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')';
|
||||
|
||||
browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
|
||||
browserCanvasContext.fill();
|
||||
}
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
/* put the labels */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
let y;
|
||||
let recordDate = new Date(record.datetime)
|
||||
|
||||
printLabel('Market Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05 / 2, '1');
|
||||
printLabel(record.marketRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10 / 2, '0.50');
|
||||
|
||||
printLabel('Sell Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.15 / 2, '1');
|
||||
printLabel(record.lastSellRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.20 / 2, '0.50');
|
||||
|
||||
printLabel('Sell Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.25 / 2, '1');
|
||||
printLabel(record.sellExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.30 / 2, '0.50');
|
||||
|
||||
printLabel('Buy Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.35 / 2, '1');
|
||||
printLabel(record.lastBuyRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.40 / 2, '0.40');
|
||||
|
||||
printLabel('Buy Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.45 / 2, '1');
|
||||
printLabel(record.buyExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.50 / 2, '0.50');
|
||||
|
||||
printLabel('ROI Asset A', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.25 / 2), '1');
|
||||
printLabel((record.combinedProfitsB).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.30 / 2), '0.50');
|
||||
|
||||
printLabel('ROI Asset B', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.15 / 2), '1');
|
||||
printLabel((record.combinedProfitsA).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.20 / 2), '0.50');
|
||||
|
||||
printLabel('Datetime', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.05 / 2), '1');
|
||||
printLabel(recordDate.toUTCString(), X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.1 / 2), '0.50');
|
||||
|
||||
|
||||
function printLabel(labelToPrint, x, y, opacity) {
|
||||
|
||||
let labelPoint;
|
||||
let fontSize = 10;
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira';
|
||||
|
||||
let label = '' + labelToPrint;
|
||||
if (isNaN(label) === false) {
|
||||
label = Number(label).toLocaleString();
|
||||
}
|
||||
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
|
||||
|
||||
labelPoint = {
|
||||
x: x - xOffset,
|
||||
y: y
|
||||
};
|
||||
|
||||
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
|
||||
labelPoint = thisObject.fitFunction(labelPoint)
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
|
||||
|
||||
}
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
browserCanvasContext.fill();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,718 +0,0 @@
|
||||
function newTradingEnginesPlottersTradingSimulationStrategies() {
|
||||
|
||||
const MODULE_NAME = "Strategies Plotter";
|
||||
const INFO_LOG = false;
|
||||
const ERROR_LOG = true;
|
||||
const INTENSIVE_LOG = false;
|
||||
const logger = newWebDebugLog();
|
||||
logger.fileName = MODULE_NAME;
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setDatetime: setDatetime,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw,
|
||||
|
||||
/* Events declared outside the plotter. */
|
||||
|
||||
onDailyFileLoaded: onDailyFileLoaded,
|
||||
|
||||
// Secondary functions and properties.
|
||||
|
||||
record: undefined
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
let container = newContainer(); // Do not touch this 3 lines, they are just needed.
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
let coordinateSystem
|
||||
|
||||
let timeFrame; // This will hold the current Time Frame the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
let marketFile; // This is the current Market File being plotted.
|
||||
let fileCursor; // This is the current File Cursor being used to retrieve Daily Files.
|
||||
|
||||
let marketFiles; // This object will provide the different Market Files at different Time Frames.
|
||||
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let strategies = [];
|
||||
let strategyImages = [];
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let zoomChangedEventSubscriptionId
|
||||
let offsetChangedEventSubscriptionId
|
||||
let dragFinishedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
let marketFilesUpdatedEventSubscriptionId
|
||||
let dailyFilesUpdatedEventSubscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let userPositionDate
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); }
|
||||
|
||||
/* Stop listening to the necesary events. */
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
|
||||
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
|
||||
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
|
||||
|
||||
/* Destroyd References */
|
||||
|
||||
marketFiles = undefined;
|
||||
dailyFiles = undefined;
|
||||
|
||||
datetime = undefined;
|
||||
timeFrame = undefined;
|
||||
|
||||
marketFile = undefined;
|
||||
fileCursor = undefined;
|
||||
|
||||
strategyImages = undefined;
|
||||
|
||||
thisObject.fitFunction = undefined
|
||||
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = undefined
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
|
||||
|
||||
/* Store the information received. */
|
||||
|
||||
marketFiles = pStorage.marketFiles[0];
|
||||
dailyFiles = pStorage.dailyFiles[0];
|
||||
|
||||
datetime = pDatetime;
|
||||
timeFrame = pTimeFrame;
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
|
||||
|
||||
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
|
||||
|
||||
/* Now we set the right files according to current Period. */
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
/* Listen to the necesary events. */
|
||||
|
||||
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
|
||||
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
|
||||
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
|
||||
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
|
||||
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
|
||||
/* Get ready for plotting. */
|
||||
|
||||
recalculate();
|
||||
|
||||
/* Ready for when dimmension changes. */
|
||||
|
||||
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate();
|
||||
})
|
||||
|
||||
for (let i = 1; i < 15; i++) {
|
||||
let strategyImage = canvas.designSpace.iconByUiObjectType.get('Strategy');
|
||||
strategyImages.push(strategyImage);
|
||||
}
|
||||
|
||||
callBackFunction();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem() {
|
||||
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function finalizeCoordinateSystem() {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
}
|
||||
|
||||
function onScaleChanged() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function onMouseOver(event) {
|
||||
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
|
||||
userPositionDate = userPosition.valueOf()
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); }
|
||||
|
||||
let container;
|
||||
|
||||
/* First we check if this point is inside this space. */
|
||||
|
||||
if (this.container.frame.isThisPointHere(point) === true) {
|
||||
|
||||
return this.container;
|
||||
|
||||
} else {
|
||||
|
||||
/* This point does not belong to this space. */
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onMarketFilesUpdated() {
|
||||
try {
|
||||
let newMarketFile = marketFiles.getFile(timeFrame);
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDailyFilesUpdated() {
|
||||
try {
|
||||
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame(pTimeFrame) {
|
||||
|
||||
try {
|
||||
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
|
||||
timeFrame = pTimeFrame;
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
|
||||
recalculate();
|
||||
|
||||
} else {
|
||||
|
||||
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
|
||||
recalculate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime(pDatetime) {
|
||||
datetime = pDatetime;
|
||||
}
|
||||
|
||||
function setCoordinateSystem(pCoordinateSystem) {
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
}
|
||||
|
||||
function onDailyFileLoaded(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); }
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
|
||||
/* This happens only when all of the files in the cursor have been loaded. */
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
try {
|
||||
|
||||
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
|
||||
|
||||
this.container.frame.draw();
|
||||
|
||||
plotChart();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
|
||||
try {
|
||||
|
||||
strategies = []
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
recalculateUsingMarketFiles();
|
||||
|
||||
} else {
|
||||
|
||||
recalculateUsingDailyFiles();
|
||||
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Strategies Changed", strategies);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingDailyFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); }
|
||||
|
||||
if (fileCursor === undefined) {
|
||||
strategies = [];
|
||||
return;
|
||||
} // We need to wait
|
||||
|
||||
if (fileCursor.files.size === 0) {
|
||||
strategies = [];
|
||||
return;
|
||||
} // We need to wait until there are files in the cursor
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
let currentDate = new Date(farLeftDate.valueOf());
|
||||
|
||||
strategies = [];
|
||||
|
||||
|
||||
|
||||
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
|
||||
|
||||
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2);
|
||||
|
||||
let dailyFile = fileCursor.files.get(stringDate);
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
|
||||
for (let i = 0; i < dailyFile.length; i++) {
|
||||
|
||||
let record = {
|
||||
begin: undefined,
|
||||
end: undefined,
|
||||
status: undefined,
|
||||
number: undefined,
|
||||
beginRate: undefined,
|
||||
endRate: undefined
|
||||
};
|
||||
|
||||
record.begin = dailyFile[i][0];
|
||||
record.end = dailyFile[i][1];
|
||||
record.status = dailyFile[i][2];
|
||||
record.number = dailyFile[i][3];
|
||||
record.beginRate = dailyFile[i][4];
|
||||
record.endRate = dailyFile[i][5];
|
||||
record.situationName = dailyFile[i][6];
|
||||
record.name = dailyFile[i][7];
|
||||
|
||||
if (
|
||||
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
strategies.push(record);
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
|
||||
}
|
||||
|
||||
/* Lests check if all the visible screen is going to be covered by strategies. */
|
||||
|
||||
let lowerEnd = leftDate.valueOf();
|
||||
let upperEnd = rightDate.valueOf();
|
||||
|
||||
setTimeout(recalculate, 2000);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingMarketFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); }
|
||||
|
||||
if (marketFile === undefined) { return; } // Initialization not complete yet.
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
strategies = [];
|
||||
|
||||
for (let i = 0; i < marketFile.length; i++) {
|
||||
|
||||
let record = {
|
||||
begin: undefined,
|
||||
end: undefined,
|
||||
status: undefined,
|
||||
number: undefined,
|
||||
beginRate: undefined,
|
||||
endRate: undefined
|
||||
};
|
||||
|
||||
record.begin = marketFile[i][0];
|
||||
record.end = marketFile[i][1];
|
||||
record.status = marketFile[i][2];
|
||||
record.number = marketFile[i][3];
|
||||
record.beginRate = marketFile[i][4];
|
||||
record.endRate = marketFile[i][5];
|
||||
record.situationName = marketFile[i][6];
|
||||
record.name = marketFile[i][7];
|
||||
|
||||
if (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) {
|
||||
|
||||
strategies.push(record);
|
||||
|
||||
if (
|
||||
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Olivia > recalculateUsingMarketFiles > total strategies generated : " + strategies.length);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
|
||||
try {
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", undefined);
|
||||
|
||||
let record;
|
||||
|
||||
for (let i = 0; i < strategies.length; i++) {
|
||||
|
||||
record = strategies[i];
|
||||
|
||||
let recordPoint1 = {
|
||||
x: record.begin,
|
||||
y: record.beginRate
|
||||
};
|
||||
|
||||
let recordPoint2 = {
|
||||
x: record.end,
|
||||
y: record.endRate
|
||||
};
|
||||
|
||||
let recordPoint3 = {
|
||||
x: record.begin,
|
||||
y: record.beginRate
|
||||
};
|
||||
|
||||
let recordPoint4 = {
|
||||
x: record.end,
|
||||
y: record.endRate
|
||||
};
|
||||
|
||||
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
|
||||
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
|
||||
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
|
||||
recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4);
|
||||
|
||||
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
|
||||
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
|
||||
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
|
||||
recordPoint4 = transformThisPoint(recordPoint4, thisObject.container);
|
||||
|
||||
if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
|
||||
continue;
|
||||
}
|
||||
|
||||
recordPoint3.y = recordPoint1.y + 2000;
|
||||
recordPoint4.y = recordPoint1.y + 2000;
|
||||
|
||||
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
|
||||
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
|
||||
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
|
||||
recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4);
|
||||
|
||||
recordPoint1 = thisObject.fitFunction(recordPoint1, undefined, 30);
|
||||
recordPoint2 = thisObject.fitFunction(recordPoint2, undefined, 30);
|
||||
recordPoint3 = thisObject.fitFunction(recordPoint3, undefined, 30);
|
||||
recordPoint4 = thisObject.fitFunction(recordPoint4, undefined, 30);
|
||||
|
||||
let imageSize = 20;
|
||||
let imageToDraw = strategyImages[record.number];
|
||||
|
||||
|
||||
/* Draw the line that represents the duration of closed strategy */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint3.x + imageSize / 2, recordPoint3.y);
|
||||
browserCanvasContext.lineTo(recordPoint4.x - imageSize / 2, recordPoint4.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)';
|
||||
browserCanvasContext.lineWidth = 1
|
||||
|
||||
if (record.status === 1) {
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
} else {
|
||||
browserCanvasContext.setLineDash([2, 4])
|
||||
}
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
/* Draw the Area */
|
||||
/*
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint1.x, recordPoint2.y);
|
||||
browserCanvasContext.lineTo(recordPoint3.x, recordPoint3.y);
|
||||
browserCanvasContext.lineTo(recordPoint4.x, recordPoint4.y);
|
||||
browserCanvasContext.lineTo(recordPoint2.x, recordPoint2.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT_GREY + ', ' + 0.5 + ')';
|
||||
browserCanvasContext.fill();
|
||||
*/
|
||||
|
||||
/* Situation Name */
|
||||
let digit = record.begin.toFixed(0).substring(7,8)
|
||||
let positionY = recordPoint3.y - (recordPoint3.y - recordPoint1.y) / 2 + (Number(digit) * 10 - 50)
|
||||
|
||||
if (canvas.chartingSpace.viewport.zoomLevel >= ZOOM_OUT_THRESHOLD_FOR_HIDDING_CHARTS_LABELS) {
|
||||
if (record.name !== "New Strategy") {
|
||||
printLabel(record.name, recordPoint1.x + 5, positionY - 15, '1', 12);
|
||||
}
|
||||
if (record.situationName !== "New Situation") {
|
||||
printLabel(record.situationName, recordPoint1.x + 5, positionY, '1', 12);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the Sticks */
|
||||
|
||||
drawStick(recordPoint1, recordPoint3);
|
||||
drawStick(recordPoint2, recordPoint4);
|
||||
|
||||
if (imageToDraw.canDrawIcon === true) {
|
||||
browserCanvasContext.drawImage(imageToDraw, recordPoint3.x - imageSize / 2, recordPoint3.y - imageSize / 2, imageSize, imageSize);
|
||||
if (record.status === 1) {
|
||||
browserCanvasContext.drawImage(imageToDraw, recordPoint4.x - imageSize / 2, recordPoint4.y - imageSize / 2, imageSize, imageSize);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the current record to the panel */
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
let record = {
|
||||
};
|
||||
thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", record);
|
||||
}
|
||||
|
||||
function drawStick(point1, point2) {
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', 0.25)';
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
/* highlight the current record */
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([4, 3])
|
||||
browserCanvasContext.lineWidth = 0.5
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onViewportZoomChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); }
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportPositionChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); }
|
||||
|
||||
if (event !== undefined) {
|
||||
if (event.recalculate === true) {
|
||||
recalculate()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (Math.random() * 100 > 95) {
|
||||
recalculate()
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDragFinished() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); }
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,690 +0,0 @@
|
||||
function newTradingEnginesPlottersTradingSimulationTrades() {
|
||||
|
||||
const MODULE_NAME = "Trades Plotter";
|
||||
const INFO_LOG = false;
|
||||
const ERROR_LOG = true;
|
||||
const INTENSIVE_LOG = false;
|
||||
const logger = newWebDebugLog();
|
||||
logger.fileName = MODULE_NAME;
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setDatetime: setDatetime,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw,
|
||||
|
||||
/* Events declared outside the plotter. */
|
||||
|
||||
onDailyFileLoaded: onDailyFileLoaded,
|
||||
|
||||
// Secondary functions and properties.
|
||||
|
||||
record: undefined
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
let container = newContainer(); // Do not touch this 3 lines, they are just needed.
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
let coordinateSystem
|
||||
|
||||
let timeFrame; // This will hold the current Time Frame the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
let marketFile; // This is the current Market File being plotted.
|
||||
let fileCursor; // This is the current File Cursor being used to retrieve Daily Files.
|
||||
|
||||
let marketFiles; // This object will provide the different Market Files at different Time Frames.
|
||||
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let trades = [];
|
||||
let headers;
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let zoomChangedEventSubscriptionId
|
||||
let offsetChangedEventSubscriptionId
|
||||
let dragFinishedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
let marketFilesUpdatedEventSubscriptionId
|
||||
let dailyFilesUpdatedEventSubscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let userPositionDate
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); }
|
||||
|
||||
/* Stop listening to the necesary events. */
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
|
||||
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
|
||||
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
|
||||
|
||||
/* Destroyd References */
|
||||
|
||||
marketFiles = undefined;
|
||||
dailyFiles = undefined;
|
||||
|
||||
datetime = undefined;
|
||||
timeFrame = undefined;
|
||||
|
||||
marketFile = undefined;
|
||||
fileCursor = undefined;
|
||||
|
||||
thisObject.fitFunction = undefined
|
||||
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = undefined
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
|
||||
|
||||
/* Store the information received. */
|
||||
|
||||
marketFiles = pStorage.marketFiles[0];
|
||||
dailyFiles = pStorage.dailyFiles[0];
|
||||
|
||||
datetime = pDatetime;
|
||||
timeFrame = pTimeFrame;
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
|
||||
|
||||
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
|
||||
|
||||
/* Now we set the right files according to current Period. */
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
/* Listen to the necesary events. */
|
||||
|
||||
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
|
||||
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
|
||||
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
|
||||
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
|
||||
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
|
||||
/* Get ready for plotting. */
|
||||
|
||||
recalculate();
|
||||
|
||||
/* Ready for when dimmension changes. */
|
||||
|
||||
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate();
|
||||
})
|
||||
|
||||
callBackFunction();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem() {
|
||||
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function finalizeCoordinateSystem() {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
}
|
||||
|
||||
function onScaleChanged() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function onMouseOver(event) {
|
||||
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
|
||||
userPositionDate = userPosition.valueOf()
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); }
|
||||
|
||||
let container;
|
||||
|
||||
/* First we check if this point is inside this space. */
|
||||
|
||||
if (this.container.frame.isThisPointHere(point) === true) {
|
||||
|
||||
return this.container;
|
||||
|
||||
} else {
|
||||
|
||||
/* This point does not belong to this space. */
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onMarketFilesUpdated() {
|
||||
try {
|
||||
let newMarketFile = marketFiles.getFile(timeFrame);
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDailyFilesUpdated() {
|
||||
try {
|
||||
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame(pTimeFrame) {
|
||||
|
||||
try {
|
||||
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
|
||||
timeFrame = pTimeFrame;
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
|
||||
recalculate();
|
||||
|
||||
} else {
|
||||
|
||||
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
|
||||
recalculate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime(pDatetime) {
|
||||
datetime = pDatetime;
|
||||
}
|
||||
|
||||
function setCoordinateSystem(pCoordinateSystem) {
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
}
|
||||
|
||||
function onDailyFileLoaded(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); }
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
|
||||
/* This happens only when all of the files in the cursor have been loaded. */
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
try {
|
||||
|
||||
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
|
||||
|
||||
this.container.frame.draw();
|
||||
|
||||
plotChart();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
|
||||
try {
|
||||
|
||||
trades = []
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
recalculateUsingMarketFiles();
|
||||
|
||||
} else {
|
||||
|
||||
recalculateUsingDailyFiles();
|
||||
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Trades Changed", trades);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingDailyFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); }
|
||||
|
||||
if (fileCursor === undefined) {
|
||||
trades = [];
|
||||
return;
|
||||
} // We need to wait
|
||||
|
||||
if (fileCursor.files.size === 0) {
|
||||
trades = [];
|
||||
return;
|
||||
} // We need to wait until there are files in the cursor
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
let currentDate = new Date(farLeftDate.valueOf());
|
||||
|
||||
trades = [];
|
||||
|
||||
|
||||
|
||||
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
|
||||
|
||||
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2);
|
||||
|
||||
let dailyFile = fileCursor.files.get(stringDate);
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
|
||||
for (let i = 0; i < dailyFile.length; i++) {
|
||||
|
||||
let record = {};
|
||||
|
||||
record.begin = dailyFile[i][0];
|
||||
record.end = dailyFile[i][1];
|
||||
record.status = dailyFile[i][2];
|
||||
record.variable_last_position_ROI = dailyFile[i][3];
|
||||
record.beginRate = dailyFile[i][4];
|
||||
record.endRate = dailyFile[i][5];
|
||||
record.exitType = dailyFile[i][6];
|
||||
record.situationName = dailyFile[i][7];
|
||||
|
||||
if (
|
||||
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
trades.push(record);
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
|
||||
}
|
||||
|
||||
/* Lests check if all the visible screen is going to be covered by trades. */
|
||||
|
||||
let lowerEnd = leftDate.valueOf();
|
||||
let upperEnd = rightDate.valueOf();
|
||||
|
||||
setTimeout(recalculate, 2000);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingMarketFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); }
|
||||
|
||||
if (marketFile === undefined) { return; } // Initialization not complete yet.
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
trades = [];
|
||||
|
||||
for (let i = 0; i < marketFile.length; i++) {
|
||||
|
||||
let record = {};
|
||||
|
||||
record.begin = marketFile[i][0];
|
||||
record.end = marketFile[i][1];
|
||||
record.status = marketFile[i][2];
|
||||
record.variable_last_position_ROI = marketFile[i][3];
|
||||
record.beginRate = marketFile[i][4];
|
||||
record.endRate = marketFile[i][5];
|
||||
record.exitType = marketFile[i][6];
|
||||
record.situationName = marketFile[i][7];
|
||||
|
||||
if (
|
||||
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
trades.push(record);
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Olivia > recalculateUsingMarketFiles > total trades generated : " + trades.length);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
|
||||
try {
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", undefined);
|
||||
|
||||
let record;
|
||||
|
||||
for (let i = 0; i < trades.length; i++) {
|
||||
|
||||
record = trades[i];
|
||||
|
||||
/* Send the current record to the panel */
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
let record = {
|
||||
};
|
||||
thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", record);
|
||||
}
|
||||
|
||||
let recordPoint1 = {
|
||||
x: record.begin,
|
||||
y: record.beginRate
|
||||
};
|
||||
|
||||
let recordPoint2 = {
|
||||
x: record.end,
|
||||
y: record.beginRate
|
||||
};
|
||||
|
||||
let recordPoint3 = {
|
||||
x: record.end,
|
||||
y: record.endRate
|
||||
};
|
||||
|
||||
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
|
||||
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
|
||||
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
|
||||
|
||||
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
|
||||
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
|
||||
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
|
||||
|
||||
if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
|
||||
continue;
|
||||
}
|
||||
|
||||
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
|
||||
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
|
||||
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
|
||||
|
||||
recordPoint1 = thisObject.fitFunction(recordPoint1);
|
||||
recordPoint2 = thisObject.fitFunction(recordPoint2);
|
||||
recordPoint3 = thisObject.fitFunction(recordPoint3);
|
||||
|
||||
let line0 = record.situationName
|
||||
let line1 = '';
|
||||
let line2 = '';
|
||||
|
||||
if (line0 === 'New Situation') {
|
||||
line0 = ''
|
||||
}
|
||||
|
||||
if (record.status === 1) {
|
||||
switch (record.exitType) {
|
||||
case 1: {
|
||||
line1 = 'Stop Loss';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
line1 = 'Take Profit';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
line1 = 'Open Position';
|
||||
}
|
||||
|
||||
if (record.variable_last_position_ROI < 0) {
|
||||
line2 = 'ROI:' + (record.variable_last_position_ROI).toFixed(2) + ' %';
|
||||
} else {
|
||||
line2 = 'ROI:' + (record.variable_last_position_ROI).toFixed(2) + ' %';
|
||||
}
|
||||
|
||||
/* Draw the triangle that represents the trade. */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint1.x, recordPoint1.y);
|
||||
browserCanvasContext.lineTo(recordPoint2.x, recordPoint2.y);
|
||||
browserCanvasContext.lineTo(recordPoint3.x, recordPoint3.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
let opacity = '0.25';
|
||||
|
||||
if (record.variable_last_position_ROI > 0) {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
|
||||
}
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')';
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.lineWidth = 1;
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
let point = {}
|
||||
|
||||
if (record.beginRate > record.endRate) {
|
||||
point.x = recordPoint3.x
|
||||
point.y = recordPoint3.y + 100
|
||||
} else {
|
||||
point.x = recordPoint3.x
|
||||
point.y = recordPoint3.y - 100
|
||||
}
|
||||
|
||||
point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point);
|
||||
point = thisObject.fitFunction(point, undefined, 50);
|
||||
|
||||
if (
|
||||
recordPoint3.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 250
|
||||
||
|
||||
recordPoint3.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 250
|
||||
||
|
||||
recordPoint3.y > canvas.chartingSpace.viewport.visibleArea.bottomRight.y - 150
|
||||
||
|
||||
recordPoint3.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y + 150
|
||||
) {
|
||||
// we do not write any text
|
||||
} else {
|
||||
|
||||
if (canvas.chartingSpace.viewport.zoomLevel >= ZOOM_OUT_THRESHOLD_FOR_HIDDING_CHARTS_LABELS) {
|
||||
printLabel(line0, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line1.length * FONT_ASPECT_RATIO, point.y - 15, '1', 12);
|
||||
printLabel(line1, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line1.length * FONT_ASPECT_RATIO, point.y, '1', 12);
|
||||
printLabel(line2, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line2.length * FONT_ASPECT_RATIO, point.y + 15, '1', 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onViewportZoomChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); }
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportPositionChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); }
|
||||
|
||||
if (event !== undefined) {
|
||||
if (event.recalculate === true) {
|
||||
recalculate()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (Math.random() * 100 > 95) {
|
||||
recalculate()
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDragFinished() {
|
||||
|
||||
try {
|
||||
|
||||
if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); }
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,864 +0,0 @@
|
||||
function newTradingEnginesPlottersTradingSimulationTradingSimulation() {
|
||||
|
||||
const MODULE_NAME = "Simulation Plotter";
|
||||
const ERROR_LOG = true;
|
||||
const INTENSIVE_LOG = false;
|
||||
const logger = newWebDebugLog();
|
||||
logger.fileName = MODULE_NAME;
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setDatetime: setDatetime,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw,
|
||||
|
||||
/* Events declared outside the plotter. */
|
||||
|
||||
onDailyFileLoaded: onDailyFileLoaded,
|
||||
|
||||
// Secondary functions and properties.
|
||||
|
||||
record: undefined
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
let container = newContainer(); // Do not touch this 3 lines, they are just needed.
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
let coordinateSystem
|
||||
|
||||
let timeFrame; // This will hold the current Time Frame the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
let marketFile; // This is the current Market File being plotted.
|
||||
let fileCursor; // This is the current File Cursor being used to retrieve Daily Files.
|
||||
|
||||
let marketFiles; // This object will provide the different Market Files at different Time Frames.
|
||||
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let records = []; // Here we keep the records to be ploted every time the Draw() function is called by the AAWebPlatform.
|
||||
|
||||
let imageStopLossPhase;
|
||||
let imageTakeProfitPhase;
|
||||
let imageRecord;
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let zoomChangedEventSubscriptionId
|
||||
let offsetChangedEventSubscriptionId
|
||||
let dragFinishedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
let marketFilesUpdatedEventSubscriptionId
|
||||
let dailyFilesUpdatedEventSubscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let userPositionDate
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
try {
|
||||
|
||||
/* Stop listening to the necesary events. */
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
|
||||
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
|
||||
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
|
||||
|
||||
/* icons */
|
||||
|
||||
imageStopLossPhase = undefined;
|
||||
imageTakeProfitPhase = undefined;
|
||||
imageRecord = undefined;
|
||||
|
||||
/* Destroy References */
|
||||
|
||||
marketFiles = undefined;
|
||||
dailyFiles = undefined;
|
||||
|
||||
datetime = undefined;
|
||||
timeFrame = undefined;
|
||||
|
||||
marketFile = undefined;
|
||||
fileCursor = undefined;
|
||||
|
||||
thisObject.fitFunction = undefined
|
||||
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = undefined
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
/* Store the information received. */
|
||||
|
||||
marketFiles = pStorage.marketFiles[0];
|
||||
dailyFiles = pStorage.dailyFiles[0];
|
||||
|
||||
datetime = pDatetime;
|
||||
timeFrame = pTimeFrame;
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
|
||||
|
||||
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
|
||||
|
||||
/* Now we set the right files according to current Period. */
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
/* Listen to the necesary events. */
|
||||
|
||||
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
|
||||
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
|
||||
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
|
||||
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
|
||||
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
|
||||
/* Get ready for plotting. */
|
||||
|
||||
recalculate();
|
||||
|
||||
/* Ready for when dimmension changes. */
|
||||
|
||||
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate();
|
||||
})
|
||||
|
||||
/* Loading a few icons */
|
||||
|
||||
imageStopLossPhase = canvas.designSpace.iconByUiObjectType.get('Managed Stop Loss');
|
||||
imageTakeProfitPhase = canvas.designSpace.iconByUiObjectType.get('Phase');
|
||||
imageRecord = canvas.designSpace.iconByUiObjectType.get('Trading System');
|
||||
|
||||
callBackFunction();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem() {
|
||||
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function finalizeCoordinateSystem() {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
}
|
||||
|
||||
function onScaleChanged() {
|
||||
recalculate();
|
||||
}
|
||||
|
||||
function onMouseOver(event) {
|
||||
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
|
||||
userPositionDate = userPosition.valueOf()
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
let container;
|
||||
/* First we check if this point is inside this space. */
|
||||
if (this.container.frame.isThisPointHere(point) === true) {
|
||||
return this.container;
|
||||
} else {
|
||||
/* This point does not belong to this space. */
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function onMarketFilesUpdated() {
|
||||
try {
|
||||
let newMarketFile = marketFiles.getFile(timeFrame);
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDailyFilesUpdated() {
|
||||
try {
|
||||
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor;
|
||||
recalculate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame(pTimeFrame) {
|
||||
|
||||
try {
|
||||
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
|
||||
timeFrame = pTimeFrame;
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
marketFile = marketFiles.getFile(pTimeFrame);
|
||||
|
||||
recalculate();
|
||||
|
||||
} else {
|
||||
|
||||
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
|
||||
|
||||
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
|
||||
recalculate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime(pDatetime) {
|
||||
datetime = pDatetime;
|
||||
}
|
||||
|
||||
function setCoordinateSystem(pCoordinateSystem) {
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
}
|
||||
|
||||
function onDailyFileLoaded(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
|
||||
/* This happens only when all of the files in the cursor have been loaded. */
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
try {
|
||||
|
||||
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
|
||||
|
||||
this.container.frame.draw();
|
||||
|
||||
plotChart();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
|
||||
try {
|
||||
|
||||
records = []
|
||||
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
recalculateUsingMarketFiles();
|
||||
|
||||
} else {
|
||||
|
||||
recalculateUsingDailyFiles();
|
||||
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Records Changed", records);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingDailyFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (fileCursor === undefined) {
|
||||
records = [];
|
||||
return;
|
||||
} // We need to wait
|
||||
|
||||
if (fileCursor.files.size === 0) {
|
||||
records = [];
|
||||
return;
|
||||
} // We need to wait until there are files in the cursor
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
let currentDate = new Date(farLeftDate.valueOf());
|
||||
|
||||
records = [];
|
||||
|
||||
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
|
||||
|
||||
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2);
|
||||
|
||||
let dailyFile = fileCursor.files.get(stringDate);
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
|
||||
for (let i = 0; i < dailyFile.length; i++) {
|
||||
|
||||
let record = {};
|
||||
|
||||
record.begin = dailyFile[i][0];
|
||||
record.end = dailyFile[i][1];
|
||||
record.variable_current_balance_baseAsset = dailyFile[i][2];
|
||||
record.variable_current_balance_quotedAsset = dailyFile[i][3];
|
||||
record.variable_episode_stat_profitLoss = dailyFile[i][4];
|
||||
record.variable_last_position_profitLoss = dailyFile[i][5];
|
||||
record.variable_current_position_stopLoss_value = dailyFile[i][6];
|
||||
record.variable_episode_count_positions = dailyFile[i][7];
|
||||
record.variable_episode_count_hits = dailyFile[i][8];
|
||||
record.variable_episode_count_fails = dailyFile[i][9];
|
||||
record.variable_episode_stat_hitRatio = dailyFile[i][10];
|
||||
record.variable_episode_stat_ROI = dailyFile[i][11];
|
||||
record.variable_episode_count_periods = dailyFile[i][12];
|
||||
record.variable_episode_stat_days = dailyFile[i][13];
|
||||
record.variable_episode_stat_anualizedRateOfReturn = dailyFile[i][14];
|
||||
record.variable_current_position_rate = dailyFile[i][15];
|
||||
record.variable_last_position_ROI = dailyFile[i][16];
|
||||
record.strategy = dailyFile[i][17];
|
||||
record.variable_current_position_takeProfit_value = dailyFile[i][18];
|
||||
record.variable_current_position_stopLoss_phase = dailyFile[i][19];
|
||||
record.variable_current_position_takeProfit_phase = dailyFile[i][20];
|
||||
record.variable_current_position_size = dailyFile[i][21]; // 25 is the message for the executor
|
||||
record.variable_episode_parameters_initial_balance_baseAsset = dailyFile[i][22];
|
||||
record.variable_episode_parameters_minimum_balance_baseAsset = dailyFile[i][23];
|
||||
record.variable_episode_parameters_maximum_balance_baseAsset = dailyFile[i][24];
|
||||
record.variable_episode_parameters_initial_balance_quotedAsset = dailyFile[i][25];
|
||||
record.variable_episode_parameters_minimum_balance_quotedAsset = dailyFile[i][26];
|
||||
record.variable_episode_parameters_maximum_balance_quotedAsset = dailyFile[i][27];
|
||||
record.variable_episode_parameters_baseAsset = dailyFile[i][28];
|
||||
record.variable_episode_parameters_quotedAsset = dailyFile[i][29];
|
||||
record.variable_episode_parameters_marketBaseAsset = dailyFile[i][30];
|
||||
record.variable_episode_parameters_marketQuotedAsset = dailyFile[i][31];
|
||||
record.variable_current_position_count_periods = dailyFile[i][32];
|
||||
record.variable_current_position_stat_days = dailyFile[i][33];
|
||||
|
||||
if (
|
||||
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
records.push(record);
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
|
||||
}
|
||||
|
||||
/* Lests check if all the visible screen is going to be covered by records. */
|
||||
|
||||
let lowerEnd = leftDate.valueOf();
|
||||
let upperEnd = rightDate.valueOf();
|
||||
|
||||
setTimeout(recalculate, 2000);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingMarketFiles() {
|
||||
|
||||
try {
|
||||
|
||||
if (marketFile === undefined) { return; } // Initialization not complete yet.
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame);
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
records = [];
|
||||
|
||||
for (let i = 0; i < marketFile.length; i++) {
|
||||
|
||||
let record = {};
|
||||
|
||||
record.begin = marketFile[i][0];
|
||||
record.end = marketFile[i][1];
|
||||
record.variable_current_balance_baseAsset = marketFile[i][2];
|
||||
record.variable_current_balance_quotedAsset = marketFile[i][3];
|
||||
record.variable_episode_stat_profitLoss = marketFile[i][4];
|
||||
record.variable_last_position_profitLoss = marketFile[i][5];
|
||||
record.variable_current_position_stopLoss_value = marketFile[i][6];
|
||||
record.variable_episode_count_positions = marketFile[i][7];
|
||||
record.variable_episode_count_hits = marketFile[i][8];
|
||||
record.variable_episode_count_fails = marketFile[i][9];
|
||||
record.variable_episode_stat_hitRatio = marketFile[i][10];
|
||||
record.variable_episode_stat_ROI = marketFile[i][11];
|
||||
record.variable_episode_count_periods = marketFile[i][12];
|
||||
record.variable_episode_stat_days = marketFile[i][13];
|
||||
record.variable_episode_stat_anualizedRateOfReturn = marketFile[i][14];
|
||||
record.variable_current_position_rate = marketFile[i][15];
|
||||
record.variable_last_position_ROI = marketFile[i][16];
|
||||
record.variable_current_position_takeProfit_value = marketFile[i][17];
|
||||
record.variable_current_position_stopLoss_phase = marketFile[i][18];
|
||||
record.variable_current_position_takeProfit_phase = marketFile[i][19];
|
||||
record.variable_current_position_size = marketFile[i][20];
|
||||
record.variable_episode_parameters_initial_balance_baseAsset = marketFile[i][21];
|
||||
record.variable_episode_parameters_minimum_balance_baseAsset = marketFile[i][22];
|
||||
record.variable_episode_parameters_maximum_balance_baseAsset = marketFile[i][23];
|
||||
record.variable_episode_parameters_initial_balance_quotedAsset = marketFile[i][24];
|
||||
record.variable_episode_parameters_minimum_balance_quotedAsset = marketFile[i][25];
|
||||
record.variable_episode_parameters_maximum_balance_quotedAsset = marketFile[i][26];
|
||||
record.variable_episode_parameters_baseAsset = marketFile[i][27];
|
||||
record.variable_episode_parameters_quotedAsset = marketFile[i][28];
|
||||
record.variable_episode_parameters_marketBaseAsset = marketFile[i][29];
|
||||
record.variable_episode_parameters_marketQuotedAsset = marketFile[i][30];
|
||||
record.variable_current_position_count_periods = marketFile[i][31];
|
||||
record.variable_current_position_stat_days = marketFile[i][32];
|
||||
|
||||
if (
|
||||
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
|
||||
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
|
||||
) {
|
||||
|
||||
records.push(record);
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
|
||||
thisObject.record = record;
|
||||
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.record);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Olivia > recalculateUsingMarketFiles > total records generated : " + records.length);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
|
||||
try {
|
||||
|
||||
let record;
|
||||
|
||||
/* Now we calculate and plot the records */
|
||||
|
||||
for (let i = 0; i < records.length; i++) { // We do not start in 0 so as to be able to read the previous record i - 1
|
||||
|
||||
record = records[i];
|
||||
|
||||
/* Contributing to Auto-Scale*/
|
||||
if (record.variable_current_position_stopLoss_value !== 0) {
|
||||
coordinateSystem.reportYValue(record.variable_current_position_stopLoss_value)
|
||||
}
|
||||
if (record.variable_current_position_takeProfit_value !== 0) {
|
||||
coordinateSystem.reportYValue(record.variable_current_position_takeProfit_value)
|
||||
}
|
||||
|
||||
/* Send the current record to the panel */
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
let eventRecords = {
|
||||
record: record
|
||||
};
|
||||
thisObject.container.eventHandler.raiseEvent("Current Record Changed", eventRecords);
|
||||
}
|
||||
|
||||
let stopLossPhase = -1;
|
||||
let takeProfitPhase = -1;
|
||||
|
||||
if (i > 0) {
|
||||
if (record.variable_current_position_stopLoss_phase !== records[i - 1].variable_current_position_stopLoss_phase) {
|
||||
stopLossPhase = record.variable_current_position_stopLoss_phase;
|
||||
}
|
||||
|
||||
if (record.variable_current_position_takeProfit_phase !== records[i - 1].variable_current_position_takeProfit_phase) {
|
||||
takeProfitPhase = record.variable_current_position_takeProfit_phase;
|
||||
}
|
||||
}
|
||||
|
||||
let recordPoint1 = {
|
||||
x: record.begin + timeFrame / 7 * 1.5,
|
||||
y: thisObject.container.frame.height
|
||||
};
|
||||
|
||||
let recordPoint2 = {
|
||||
x: record.end + timeFrame / 7 * 5.5,
|
||||
y: thisObject.container.frame.height
|
||||
};
|
||||
|
||||
let recordPoint3 = {
|
||||
x: recordPoint1.x + (recordPoint2.x - recordPoint1.x) / 2,
|
||||
y: 0
|
||||
};
|
||||
|
||||
let recordPoint4 = {
|
||||
x: record.begin + timeFrame / 7 * 1.5,
|
||||
y: thisObject.container.frame.height * 100
|
||||
};
|
||||
|
||||
let recordPoint5 = {
|
||||
x: record.begin,
|
||||
y: record.variable_current_position_stopLoss_value
|
||||
};
|
||||
|
||||
let recordPoint6 = {
|
||||
x: record.end,
|
||||
y: record.variable_current_position_stopLoss_value
|
||||
};
|
||||
|
||||
if (record.variable_current_position_stopLoss_value === 0) { // Put these points out of range if stopLoss is zero.
|
||||
|
||||
recordPoint5.x = 0;
|
||||
recordPoint6.x = 0;
|
||||
}
|
||||
|
||||
let recordPoint7 = {
|
||||
x: record.begin,
|
||||
y: record.variable_current_position_rate
|
||||
};
|
||||
|
||||
let recordPoint8 = {
|
||||
x: record.end,
|
||||
y: record.variable_current_position_rate
|
||||
};
|
||||
|
||||
if (record.variable_current_position_rate === 0) { // Put these points out of range if sellRate is zero.
|
||||
|
||||
recordPoint7.x = 0;
|
||||
recordPoint8.x = 0;
|
||||
}
|
||||
|
||||
let recordPoint9 = {
|
||||
x: record.begin,
|
||||
y: record.variable_current_position_takeProfit_value
|
||||
};
|
||||
|
||||
let recordPoint10 = {
|
||||
x: record.end,
|
||||
y: record.variable_current_position_takeProfit_value
|
||||
};
|
||||
|
||||
if (record.variable_current_position_takeProfit_value === 0) { // Put these points out of range if takeProfit is zero.
|
||||
|
||||
recordPoint9.x = 0;
|
||||
recordPoint10.x = 0;
|
||||
}
|
||||
|
||||
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
|
||||
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
|
||||
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
|
||||
recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4);
|
||||
recordPoint5 = coordinateSystem.transformThisPoint(recordPoint5);
|
||||
recordPoint6 = coordinateSystem.transformThisPoint(recordPoint6);
|
||||
recordPoint7 = coordinateSystem.transformThisPoint(recordPoint7);
|
||||
recordPoint8 = coordinateSystem.transformThisPoint(recordPoint8);
|
||||
recordPoint9 = coordinateSystem.transformThisPoint(recordPoint9);
|
||||
recordPoint10 = coordinateSystem.transformThisPoint(recordPoint10);
|
||||
|
||||
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
|
||||
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
|
||||
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
|
||||
recordPoint4 = transformThisPoint(recordPoint4, thisObject.container);
|
||||
recordPoint5 = transformThisPoint(recordPoint5, thisObject.container);
|
||||
recordPoint6 = transformThisPoint(recordPoint6, thisObject.container);
|
||||
recordPoint7 = transformThisPoint(recordPoint7, thisObject.container);
|
||||
recordPoint8 = transformThisPoint(recordPoint8, thisObject.container);
|
||||
recordPoint9 = transformThisPoint(recordPoint9, thisObject.container);
|
||||
recordPoint10 = transformThisPoint(recordPoint10, thisObject.container);
|
||||
|
||||
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
|
||||
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
|
||||
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
|
||||
recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4);
|
||||
recordPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint5);
|
||||
recordPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint6);
|
||||
recordPoint7 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint7);
|
||||
recordPoint8 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint8);
|
||||
recordPoint9 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint9);
|
||||
recordPoint10 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint10);
|
||||
|
||||
recordPoint1 = thisObject.fitFunction(recordPoint1);
|
||||
recordPoint2 = thisObject.fitFunction(recordPoint2);
|
||||
recordPoint3 = thisObject.fitFunction(recordPoint3);
|
||||
recordPoint4 = thisObject.fitFunction(recordPoint4);
|
||||
recordPoint5 = thisObject.fitFunction(recordPoint5);
|
||||
recordPoint6 = thisObject.fitFunction(recordPoint6);
|
||||
recordPoint7 = thisObject.fitFunction(recordPoint7);
|
||||
recordPoint8 = thisObject.fitFunction(recordPoint8);
|
||||
recordPoint9 = thisObject.fitFunction(recordPoint9);
|
||||
recordPoint10 = thisObject.fitFunction(recordPoint10);
|
||||
|
||||
/* Image representing a Record */
|
||||
|
||||
let imageOffset = {
|
||||
x: 0,
|
||||
y: -30
|
||||
}
|
||||
let imageSize = (recordPoint2.x - recordPoint1.x) / 2;
|
||||
let recordLabel = record.variable_episode_count_periods.toLocaleString()
|
||||
if (imageSize < 8) {
|
||||
if (recordLabel[recordLabel.length - 1] !== '0' || imageSize < 4)
|
||||
recordLabel = ''
|
||||
}
|
||||
let labelOffset = {
|
||||
x: 0 - imageSize / 2 + 3 + imageOffset.x,
|
||||
y: -2 - imageOffset.y
|
||||
}
|
||||
|
||||
let imagePosition = {
|
||||
x: recordPoint4.x + imageOffset.x,
|
||||
y: 0
|
||||
}
|
||||
|
||||
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
|
||||
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
|
||||
|
||||
if (imageRecord.canDrawIcon === true) {
|
||||
browserCanvasContext.drawImage(imageRecord, imagePosition.x, imagePosition.y, imageSize, imageSize);
|
||||
browserCanvasContext.save();
|
||||
browserCanvasContext.translate(recordPoint3.x + labelOffset.x, recordPoint3.y + labelOffset.y);
|
||||
browserCanvasContext.rotate(-Math.PI / 2);
|
||||
printLabel(recordLabel, 0, 0, '1', 8, undefined, undefined, undefined, undefined, true);
|
||||
browserCanvasContext.restore();
|
||||
}
|
||||
|
||||
if (recordPoint5.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint5.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Next we are drawing the position Rate */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint7.x, recordPoint7.y);
|
||||
browserCanvasContext.lineTo(recordPoint8.x, recordPoint8.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)';
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.setLineDash([1, 4])
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
browserCanvasContext.lineWidth = 5
|
||||
browserCanvasContext.setLineDash([3, 4])
|
||||
|
||||
/* highlight the current record */
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
|
||||
}
|
||||
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
imageSize = 20;
|
||||
let imageToDraw;
|
||||
|
||||
/* Depending on what is on top, it is also the text and image of the take accumulatedProfitLoss / stop loss */
|
||||
|
||||
let yOffset = 0
|
||||
if (record.variable_current_position_takeProfit_value > record.variable_current_position_stopLoss_value) { yOffset = imageSize * 2 }
|
||||
|
||||
/* Next we are drawing the stopLoss floor / ceilling */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint5.x, recordPoint5.y);
|
||||
browserCanvasContext.lineTo(recordPoint6.x, recordPoint6.y);
|
||||
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
let opacity = 1
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + '';
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
/* highlight the current record */
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
|
||||
}
|
||||
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
imagePosition = {
|
||||
x: recordPoint6.x - imageSize,
|
||||
y: recordPoint6.y - imageSize * 1.25 + yOffset
|
||||
}
|
||||
|
||||
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
|
||||
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
|
||||
|
||||
let labelPosition = {
|
||||
x: recordPoint6.x - imageSize / 2 - 3,
|
||||
y: recordPoint6.y - imageSize * 1.5 + yOffset * 2
|
||||
}
|
||||
labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition)
|
||||
labelPosition = thisObject.fitFunction(labelPosition, undefined, 30)
|
||||
|
||||
if (imageStopLossPhase.canDrawIcon === true && stopLossPhase > -1) {
|
||||
browserCanvasContext.drawImage(imageStopLossPhase, imagePosition.x, imagePosition.y, imageSize, imageSize);
|
||||
printLabel(stopLossPhase, labelPosition.x, labelPosition.y, '1', 9);
|
||||
}
|
||||
|
||||
/* Next we are drawing the Take Profit */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(recordPoint9.x, recordPoint9.y);
|
||||
browserCanvasContext.lineTo(recordPoint10.x, recordPoint10.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
opacity = 1
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + '';
|
||||
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
|
||||
/* highlight the current record */
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
|
||||
}
|
||||
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
imagePosition = {
|
||||
x: recordPoint10.x - imageSize * 2 / 3,
|
||||
y: recordPoint10.y + imageSize / 4 - yOffset
|
||||
}
|
||||
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
|
||||
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
|
||||
|
||||
labelPosition = {
|
||||
x: recordPoint10.x - imageSize * 1 / 3,
|
||||
y: recordPoint10.y + imageSize * 1.9 - yOffset * 2
|
||||
}
|
||||
labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition)
|
||||
labelPosition = thisObject.fitFunction(labelPosition, undefined, 30)
|
||||
|
||||
if (imageTakeProfitPhase.canDrawIcon === true && takeProfitPhase > -1) {
|
||||
browserCanvasContext.drawImage(imageTakeProfitPhase, imagePosition.x, imagePosition.y, imageSize, imageSize);
|
||||
printLabel(takeProfitPhase, labelPosition.x, labelPosition.y, '1', 9);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onViewportZoomChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportPositionChanged(event) {
|
||||
|
||||
try {
|
||||
|
||||
if (event !== undefined) {
|
||||
if (event.recalculate === true) {
|
||||
recalculate()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (Math.random() * 100 > 95) {
|
||||
recalculate()
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onDragFinished() {
|
||||
|
||||
try {
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
|
||||
function newTradingEnginesPlottersTradingSimulationTradingSimulationTradingSimulationAssetBalancesPanel() {
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
onRecordChange: onRecordChange,
|
||||
container: undefined,
|
||||
session: undefined,
|
||||
isVisible: true,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
};
|
||||
|
||||
let container = newContainer();
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
container.frame.containerName = "Simulation Asset Balances Panel";
|
||||
|
||||
let record;
|
||||
let lastRecord;
|
||||
let upDownButton
|
||||
|
||||
const HEIGHT_FACTOR = 1.25
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize() {
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 1;
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * HEIGHT_FACTOR;
|
||||
|
||||
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 1;
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
|
||||
|
||||
upDownButton = newUpDownButton()
|
||||
upDownButton.parentContainer = thisObject.container
|
||||
upDownButton.container.frame.parentFrame = thisObject.container.frame
|
||||
upDownButton.fitFunction = thisObject.fitFunction
|
||||
upDownButton.initialize()
|
||||
|
||||
thisObject.assetBalances = newAssetalances()
|
||||
thisObject.assetBalances.container.connectToParent(thisObject.container, true, true)
|
||||
thisObject.assetBalances.initialize()
|
||||
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
thisObject.session = undefined
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.onRecordChange = undefined
|
||||
thisObject.isVisible = undefined
|
||||
|
||||
record = undefined
|
||||
lastRecord = undefined
|
||||
upDownButton = undefined
|
||||
|
||||
thisObject.assetBalances.finalize()
|
||||
thisObject.assetBalances = undefined
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
let container;
|
||||
|
||||
container = upDownButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onRecordChange(records) {
|
||||
|
||||
record = records.record;
|
||||
lastRecord = records.lastRecord
|
||||
|
||||
}
|
||||
|
||||
|
||||
function draw() {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
|
||||
|
||||
plotCurrentRecordInfo();
|
||||
|
||||
upDownButton.draw()
|
||||
|
||||
/* Define panel name */
|
||||
if (thisObject.session !== undefined) {
|
||||
const MAX_LABEL_LENGTH = 25
|
||||
if (thisObject.session.name.length > MAX_LABEL_LENGTH) {
|
||||
container.frame.containerName = thisObject.session.name.substring(0, MAX_LABEL_LENGTH) + '...'
|
||||
} else {
|
||||
container.frame.containerName = thisObject.session.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function plotCurrentRecordInfo() {
|
||||
|
||||
if (record === undefined) { return; }
|
||||
|
||||
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
|
||||
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
|
||||
|
||||
const X_AXIS = thisObject.container.frame.width / 2;
|
||||
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
|
||||
|
||||
/* put the labels with the records values */
|
||||
|
||||
let y = 0;
|
||||
let increment = 0.024 / HEIGHT_FACTOR * 2.5;
|
||||
|
||||
y = y + increment;
|
||||
y = y + increment;
|
||||
printLabel('Asset Balances', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
/* Parameters */
|
||||
let params
|
||||
let paramsArray = []
|
||||
let positionTaken = false
|
||||
|
||||
if (record.variable_current_position_size > 0) {
|
||||
positionTaken = true
|
||||
}
|
||||
|
||||
let testPoint = {
|
||||
x: 0,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
testPoint = thisObject.container.frame.frameThisPoint(testPoint)
|
||||
let fitPoint = thisObject.fitFunction(testPoint)
|
||||
if (fitPoint.y !== testPoint.y) {
|
||||
return
|
||||
}
|
||||
|
||||
if (record.variable_episode_parameters_baseAsset === record.variable_episode_parameters_marketBaseAsset) {
|
||||
params = {}
|
||||
params.VALUE = record.variable_current_balance_baseAsset;
|
||||
params.INIT_VALUE = record.variable_episode_parameters_initial_balance_baseAsset
|
||||
params.MIN_VALUE = record.variable_episode_parameters_minimum_balance_baseAsset
|
||||
params.MAX_VALUE = record.variable_episode_parameters_maximum_balance_baseAsset
|
||||
params.ASSET_LABEL = 'Base'
|
||||
params.ASSET_NAME = record.variable_episode_parameters_baseAsset
|
||||
params.LEFT_OFFSET = X_AXIS
|
||||
params.POSITION_TAKEN = positionTaken
|
||||
params.BASE_ASSET = record.variable_episode_parameters_baseAsset
|
||||
params.DECIMALS = 4
|
||||
|
||||
paramsArray.push(params)
|
||||
|
||||
params = {}
|
||||
params.VALUE = record.variable_current_balance_quotedAsset;
|
||||
params.MIN_VALUE = record.variable_episode_parameters_minimum_balance_quotedAsset
|
||||
params.INIT_VALUE = record.variable_episode_parameters_initial_balance_quotedAsset
|
||||
params.MAX_VALUE = record.variable_episode_parameters_maximum_balance_quotedAsset
|
||||
params.ASSET_LABEL = 'Quoted'
|
||||
params.ASSET_NAME = record.variable_episode_parameters_quotedAsset
|
||||
params.LEFT_OFFSET = X_AXIS
|
||||
params.POSITION_TAKEN = positionTaken
|
||||
params.BASE_ASSET = record.variable_episode_parameters_baseAsset
|
||||
params.DECIMALS = 2
|
||||
|
||||
paramsArray.push(params)
|
||||
} else {
|
||||
params = {}
|
||||
params.VALUE = record.variable_current_balance_quotedAsset;
|
||||
params.INIT_VALUE = record.variable_episode_parameters_initial_balance_quotedAsset
|
||||
params.MIN_VALUE = record.variable_episode_parameters_minimum_balance_quotedAsset
|
||||
params.MAX_VALUE = record.variable_episode_parameters_maximum_balance_quotedAsset
|
||||
params.ASSET_LABEL = 'Base'
|
||||
params.ASSET_NAME = record.variable_episode_parameters_baseAsset
|
||||
params.LEFT_OFFSET = X_AXIS
|
||||
params.POSITION_TAKEN = positionTaken
|
||||
params.BASE_ASSET = record.variable_episode_parameters_baseAsset
|
||||
params.DECIMALS = 4
|
||||
|
||||
paramsArray.push(params)
|
||||
|
||||
params = {}
|
||||
params.VALUE = record.variable_current_balance_baseAsset;
|
||||
params.MIN_VALUE = record.variable_episode_parameters_minimum_balance_baseAsset
|
||||
params.INIT_VALUE = record.variable_episode_parameters_initial_balance_baseAsset
|
||||
params.MAX_VALUE = record.variable_episode_parameters_maximum_balance_baseAsset
|
||||
params.ASSET_LABEL = 'Quoted'
|
||||
params.ASSET_NAME = record.variable_episode_parameters_quotedAsset
|
||||
params.LEFT_OFFSET = X_AXIS
|
||||
params.POSITION_TAKEN = positionTaken
|
||||
params.BASE_ASSET = record.variable_episode_parameters_baseAsset
|
||||
params.DECIMALS = 2
|
||||
|
||||
paramsArray.push(params)
|
||||
}
|
||||
|
||||
|
||||
thisObject.assetBalances.setParamsArray(paramsArray)
|
||||
thisObject.assetBalances.draw()
|
||||
}
|
||||
}
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
|
||||
|
||||
|
||||
function newTradingEnginesPlottersTradingSimulationTradingSimulationTradingSimulationCurrentPositionPanel() {
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
onRecordChange: onRecordChange,
|
||||
container: undefined,
|
||||
session: undefined,
|
||||
isVisible: true,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
};
|
||||
|
||||
let container = newContainer();
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
container.frame.containerName = "Simulation Current Position Panel";
|
||||
|
||||
let record;
|
||||
let lastRecord;
|
||||
let upDownButton
|
||||
|
||||
const HEIGHT_FACTOR = 1.25
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize() {
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 1;
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * HEIGHT_FACTOR;
|
||||
|
||||
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 1;
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
|
||||
|
||||
upDownButton = newUpDownButton()
|
||||
upDownButton.parentContainer = thisObject.container
|
||||
upDownButton.container.frame.parentFrame = thisObject.container.frame
|
||||
upDownButton.fitFunction = thisObject.fitFunction
|
||||
upDownButton.initialize()
|
||||
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
thisObject.session = undefined
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.onRecordChange = undefined
|
||||
thisObject.isVisible = undefined
|
||||
|
||||
record = undefined
|
||||
lastRecord = undefined
|
||||
upDownButton = undefined
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
let container;
|
||||
|
||||
container = upDownButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onRecordChange(records) {
|
||||
|
||||
record = records.record;
|
||||
lastRecord = records.lastRecord
|
||||
|
||||
}
|
||||
|
||||
|
||||
function draw() {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
|
||||
|
||||
plotCurrentRecordInfo();
|
||||
|
||||
upDownButton.draw()
|
||||
|
||||
/* Define panel name */
|
||||
if (thisObject.session !== undefined) {
|
||||
const MAX_LABEL_LENGTH = 25
|
||||
if (thisObject.session.name.length > MAX_LABEL_LENGTH) {
|
||||
container.frame.containerName = thisObject.session.name.substring(0, MAX_LABEL_LENGTH) + '...'
|
||||
} else {
|
||||
container.frame.containerName = thisObject.session.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function plotCurrentRecordInfo() {
|
||||
|
||||
if (record === undefined) { return; }
|
||||
|
||||
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
|
||||
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
|
||||
|
||||
const X_AXIS = thisObject.container.frame.width / 2;
|
||||
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
|
||||
|
||||
|
||||
/* put the labels with the records values */
|
||||
|
||||
let y = 0;
|
||||
let increment = 0.024 / HEIGHT_FACTOR * 2.5;
|
||||
|
||||
y = y + increment;
|
||||
y = y + increment;
|
||||
printLabel('Current Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Size', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_current_position_size, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Rate', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_current_position_rate, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Stop', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_current_position_stopLoss_value, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Take Profit', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_current_position_takeProfit_value, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_current_position_count_periods), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_current_position_stat_days).toFixed(2), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
y = y + increment;
|
||||
printLabel('Latest Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_last_position_profitLoss), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_last_position_ROI).toFixed(2) + ' % ', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
}
|
||||
}
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
|
||||
function newTradingEnginesPlottersTradingSimulationTradingSimulationTradingSimulationEpisodePanel() {
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
onRecordChange: onRecordChange,
|
||||
container: undefined,
|
||||
session: undefined,
|
||||
isVisible: true,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
};
|
||||
|
||||
let container = newContainer();
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
container.frame.containerName = "Simulation Episode Panel";
|
||||
|
||||
let record;
|
||||
let lastRecord;
|
||||
let upDownButton
|
||||
|
||||
const HEIGHT_FACTOR = 1.25
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize() {
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 1;
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * HEIGHT_FACTOR;
|
||||
|
||||
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 1;
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
|
||||
|
||||
upDownButton = newUpDownButton()
|
||||
upDownButton.parentContainer = thisObject.container
|
||||
upDownButton.container.frame.parentFrame = thisObject.container.frame
|
||||
upDownButton.fitFunction = thisObject.fitFunction
|
||||
upDownButton.initialize()
|
||||
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
thisObject.session = undefined
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.onRecordChange = undefined
|
||||
thisObject.isVisible = undefined
|
||||
|
||||
record = undefined
|
||||
lastRecord = undefined
|
||||
upDownButton = undefined
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
let container;
|
||||
|
||||
container = upDownButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onRecordChange(records) {
|
||||
|
||||
record = records.record;
|
||||
lastRecord = records.lastRecord
|
||||
|
||||
}
|
||||
|
||||
|
||||
function draw() {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
|
||||
|
||||
plotCurrentRecordInfo();
|
||||
|
||||
upDownButton.draw()
|
||||
|
||||
/* Define panel name */
|
||||
if (thisObject.session !== undefined) {
|
||||
const MAX_LABEL_LENGTH = 25
|
||||
if (thisObject.session.name.length > MAX_LABEL_LENGTH) {
|
||||
container.frame.containerName = thisObject.session.name.substring(0, MAX_LABEL_LENGTH) + '...'
|
||||
} else {
|
||||
container.frame.containerName = thisObject.session.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function plotCurrentRecordInfo() {
|
||||
|
||||
if (record === undefined) { return; }
|
||||
|
||||
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
|
||||
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
|
||||
|
||||
const X_AXIS = thisObject.container.frame.width / 2;
|
||||
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
|
||||
|
||||
|
||||
/* put the labels with the records values */
|
||||
|
||||
let y = 0;
|
||||
let increment = 0.024 / HEIGHT_FACTOR * 2.5;
|
||||
|
||||
y = y + increment;
|
||||
y = y + increment;
|
||||
printLabel('Episode', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_stat_profitLoss, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Trades', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_count_positions, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Hits', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_count_hits, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Fails', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_count_fails, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Hit Ratio', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_episode_stat_hitRatio * 100).toFixed(2) + ' %', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_episode_stat_ROI * 100).toFixed(2) + ' %', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_count_periods, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel(record.variable_episode_stat_days, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
y = y + increment;
|
||||
printLabel('Anualized Rate of Return', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60', undefined, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
y = y + increment;
|
||||
printLabel((record.variable_episode_stat_anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14, undefined, true, thisObject.container, thisObject.fitFunction);
|
||||
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"displayName": "Plotters Trading Simulation",
|
||||
"codeName": "PlottersTradingSimulation",
|
||||
"dataMine": "Masters",
|
||||
"modules": [
|
||||
{
|
||||
"codeName": "TradingSimulation",
|
||||
"moduleName": "TradingSimulation",
|
||||
"description": "Plots Bollinger Records.",
|
||||
"profilePicture": "TradingSimulation.png",
|
||||
"panels": [
|
||||
{
|
||||
"codeName": "TradingSimulationPanel",
|
||||
"moduleName": "TradingSimulationPanel",
|
||||
"event": "Current Record Changed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codeName": "Conditions",
|
||||
"moduleName": "Conditions",
|
||||
"description": "Plots Simulation Conditions.",
|
||||
"profilePicture": "Conditions.png",
|
||||
"panels": []
|
||||
},
|
||||
{
|
||||
"codeName": "Strategies",
|
||||
"moduleName": "Strategies",
|
||||
"description": "Plots Strategies.",
|
||||
"profilePicture": "Strategies.png",
|
||||
"panels": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"codeName": "Trades",
|
||||
"moduleName": "Trades",
|
||||
"description": "Plots Trades.",
|
||||
"profilePicture": "Trades.png",
|
||||
"panels": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"codeName": "SimulationExecution",
|
||||
"moduleName": "SimulationExecution",
|
||||
"description": "Plots Simulation Execution.",
|
||||
"profilePicture": "SimulationExecution.png",
|
||||
"panels": [
|
||||
{
|
||||
"codeName": "SimulationExecutionPanel",
|
||||
"moduleName": "SimulationExecutionPanel",
|
||||
"event": "Current Record Changed"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"type":"Workspace","name":"Beta 6","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"8081\"\n}","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","exchangeDataProducts":[{"type":"Exchange Data Products","name":"Binance","singleMarketData":[{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"dd7c8795-bb24-4438-9d77-b37a9000ad7f","savedPayload":{"position":{"x":23357.055360727853,"y":29739.13668562185},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"98e6c917-f4b7-4b71-9215-de3ccaf3dc0c","savedPayload":{"position":{"x":23291.030236843966,"y":29662.131374764274},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"1d7dd537-5f6f-4eb2-8e56-ddba0aef720f","savedPayload":{"position":{"x":23231.77091792585,"y":29579.512296539906},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"221c8d22-8a7f-4014-a7ed-0bafc886b123","savedPayload":{"position":{"x":23179.942027683566,"y":29492.078725813997},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"6929ebe7-6486-4299-add5-ed0282500d91","savedPayload":{"position":{"x":23135.872351370694,"y":29400.352192197388},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4a3ed8eb-a73e-4fb4-835a-681cf47d7373","savedPayload":{"position":{"x":23099.965969252025,"y":29305.112951112584},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"5ffc6a67-e33b-422b-8e36-f9711f93c723","savedPayload":{"position":{"x":23072.49341713892,"y":29207.0453495464},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"48f7953c-f62d-4292-b042-d1ac05f8f80f","savedPayload":{"position":{"x":23053.683458829226,"y":29106.890611883788},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"3b9a3c93-6d41-440e-89a2-2fd1b2dde603","savedPayload":{"position":{"x":23043.701100505998,"y":29005.50001492274},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollLine truncated
|
||||
{"type":"Workspace","name":"Beta 6","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"8081\"\n}","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","exchangeDataProducts":[{"type":"Exchange Data Products","name":"Binance","singleMarketData":[{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"dd7c8795-bb24-4438-9d77-b37a9000ad7f","savedPayload":{"position":{"x":23357.055360727853,"y":29739.13668562185},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"98e6c917-f4b7-4b71-9215-de3ccaf3dc0c","savedPayload":{"position":{"x":23291.030236843966,"y":29662.131374764274},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"1d7dd537-5f6f-4eb2-8e56-ddba0aef720f","savedPayload":{"position":{"x":23231.77091792585,"y":29579.512296539906},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"221c8d22-8a7f-4014-a7ed-0bafc886b123","savedPayload":{"position":{"x":23179.942027683566,"y":29492.078725813997},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"6929ebe7-6486-4299-add5-ed0282500d91","savedPayload":{"position":{"x":23135.872351370694,"y":29400.352192197388},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4a3ed8eb-a73e-4fb4-835a-681cf47d7373","savedPayload":{"position":{"x":23099.965969252025,"y":29305.112951112584},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"5ffc6a67-e33b-422b-8e36-f9711f93c723","savedPayload":{"position":{"x":23072.49341713892,"y":29207.0453495464},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"48f7953c-f62d-4292-b042-d1ac05f8f80f","savedPayload":{"position":{"x":23053.683458829226,"y":29106.890611883788},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"3b9a3c93-6d41-440e-89a2-2fd1b2dde603","savedPayload":{"position":{"x":23043.701100505998,"y":29005.50001492274},"targetPosition":{"x":24208.69903024272,"y":28943.27543890267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollLine truncated
|
||||
@@ -1,152 +0,0 @@
|
||||
exports.newExchangeAPI = function newExchangeAPI(logger, BOT) {
|
||||
|
||||
let MODULE_NAME = "Exchange API";
|
||||
let LOG_INFO = true;
|
||||
let apiClient;
|
||||
let bot = BOT
|
||||
|
||||
let thisObject = {
|
||||
initialize: initialize,
|
||||
getTicker: getTicker,
|
||||
getOpenOrders: getOpenOrders,
|
||||
getOrder: getOrder,
|
||||
createOrder: createOrder,
|
||||
getMaxDecimalPositions: getMaxDecimalPositions
|
||||
};
|
||||
|
||||
let exchangeId
|
||||
let options = {}
|
||||
let exchange
|
||||
|
||||
const ccxt = require('ccxt')
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize() {
|
||||
exchangeId = bot.exchange.toLowerCase()
|
||||
|
||||
let key = process.env.KEY
|
||||
let secret = process.env.SECRET
|
||||
|
||||
if (key === "undefined") { key = undefined }
|
||||
if (secret === "undefined") { secret = undefined }
|
||||
|
||||
const exchangeClass = ccxt[exchangeId]
|
||||
const exchangeConstructorParams = {
|
||||
'apiKey': key,
|
||||
'secret': secret,
|
||||
'timeout': 30000,
|
||||
'enableRateLimit': true,
|
||||
verbose: false,
|
||||
options: options
|
||||
}
|
||||
exchange = new exchangeClass(exchangeConstructorParams)
|
||||
}
|
||||
|
||||
function getMaxDecimalPositions() {
|
||||
return 8
|
||||
}
|
||||
|
||||
async function getTicker(market, callBackFunction) {
|
||||
try {
|
||||
logInfo("getTicker -> Entering function.");
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
let ticker
|
||||
|
||||
if (exchange.has['fetchTicker']) {
|
||||
ticker = await (exchange.fetchTicker(symbol))
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, ticker)
|
||||
} else {
|
||||
logError("getTicker -> Exchange does not support fetchTicker command.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logError("getTicker -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
async function getOpenOrders(market, callBackFunction) {
|
||||
try {
|
||||
logInfo("getOpenOrders -> Entering function. market = " + JSON.stringify(market));
|
||||
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
let openOrders
|
||||
|
||||
if (exchange.has['fetchOpenOrders']) {
|
||||
openOrders = await (exchange.fetchOpenOrders(symbol))
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, openOrders)
|
||||
} else {
|
||||
logError("getOpenOrders -> Exchange does not support fetchOpenOrders command.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logError("getOpenOrders -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
async function getOrder(orderId, market, callBackFunction) {
|
||||
try {
|
||||
logInfo("getOrder -> Entering function. orderId = " + orderId);
|
||||
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
let order
|
||||
|
||||
if (exchange.has['fetchOrder']) {
|
||||
order = await (exchange.fetchOrder(orderId, symbol))
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, order)
|
||||
} else {
|
||||
logError("getOrder -> Exchange does not support fetchOrder command.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
} catch (err) {
|
||||
logError("getOrder -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
async function createOrder(market, side, price, cost, amount, callBackFunction) {
|
||||
try {
|
||||
logInfo("createOrder -> Entering function.");
|
||||
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
|
||||
logInfo("createOrder -> market = " + market.assetA + "_" + market.assetB);
|
||||
logInfo("createOrder -> side = " + side);
|
||||
logInfo("createOrder -> cost = " + cost);
|
||||
logInfo("createOrder -> amount = " + amount);
|
||||
logInfo("createOrder -> price = " + price);
|
||||
|
||||
if (side !== "buy" && side !== "sell") {
|
||||
logError("createOrder -> side must be either 'buy' or 'sell'.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
let type = 'market'
|
||||
let order
|
||||
|
||||
if (exchange.has['createOrder']) {
|
||||
order = await (exchange.createOrder(symbol, type, side, amount))
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, order)
|
||||
} else {
|
||||
logError("createOrder -> Exchange does not support createOrder command.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logError("createOrder -> err = " + JSON.stringify(err.stack) || err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function logInfo(message) {
|
||||
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) }
|
||||
}
|
||||
|
||||
function logError(message) {
|
||||
logger.write(MODULE_NAME, '[ERROR] ' + message)
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
exports.newExchangeAPI = function newExchangeAPI(bot, logger) {
|
||||
|
||||
let MODULE_NAME = "Exchange API";
|
||||
|
||||
let thisObject = {
|
||||
getOrder: getOrder,
|
||||
createOrder: createOrder,
|
||||
cancelOrder: cancelOrder,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
};
|
||||
|
||||
let tradingSystem
|
||||
|
||||
let exchangeId
|
||||
let options = {}
|
||||
let exchange
|
||||
|
||||
const ccxt = require('ccxt')
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize() {
|
||||
tradingSystem = bot.simulationState.tradingSystem
|
||||
|
||||
exchangeId = bot.exchange.toLowerCase()
|
||||
|
||||
let key = bot.KEY
|
||||
let secret = bot.SECRET
|
||||
|
||||
const exchangeClass = ccxt[exchangeId]
|
||||
const exchangeConstructorParams = {
|
||||
'apiKey': key,
|
||||
'secret': secret,
|
||||
'timeout': 30000,
|
||||
'enableRateLimit': true,
|
||||
verbose: false,
|
||||
options: options
|
||||
}
|
||||
exchange = new exchangeClass(exchangeConstructorParams)
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
tradingSystem = undefined
|
||||
exchangeId = undefined
|
||||
options = undefined
|
||||
exchange = undefined
|
||||
}
|
||||
|
||||
async function getOrder(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
let orderId = tradingEngineOrder.exchangeId.value
|
||||
|
||||
/* Basic Logging */
|
||||
logInfo("getOrder -> Entering function. orderId = " + orderId);
|
||||
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
|
||||
/* Basic Validations */
|
||||
if (exchange.has['fetchOrder'] === false) {
|
||||
logError("getOrder -> Exchange does not support fetchOrder command.");
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let order = await exchange.fetchOrder(orderId, symbol)
|
||||
return order
|
||||
} catch (err) {
|
||||
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
|
||||
logError("getOrder -> Error = " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function createOrder(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
let price = tradingEngineOrder.rate.value // CCXT: how much quote currency you are willing to pay for a trade lot of base currency (for limit orders only)
|
||||
let amount = tradingEngineOrder.size.value // CCXT: how much of currency you want to trade
|
||||
let type // CCXT: a string literal type of order, ccxt currently unifies market and limit orders only
|
||||
let side // CCXT: a string literal for the direction of your order, buy or sell
|
||||
let symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset // CCXT: a string literal symbol of the market you wish to trade on, like BTC/USD, ZEC/ETH, DOGE/DASH, etc
|
||||
|
||||
switch (tradingSystemOrder.type) {
|
||||
case 'Market Buy Order': {
|
||||
type = 'market'
|
||||
side = 'buy'
|
||||
break
|
||||
}
|
||||
case 'Market Sell Order': {
|
||||
type = 'market'
|
||||
side = 'sell'
|
||||
break
|
||||
}
|
||||
case 'Limit Buy Order': {
|
||||
type = 'limit'
|
||||
side = 'buy'
|
||||
break
|
||||
}
|
||||
case 'Limit Sell Order': {
|
||||
type = 'limit'
|
||||
side = 'sell'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Basic Logging */
|
||||
logInfo("createOrder -> symbol = " + symbol);
|
||||
logInfo("createOrder -> side = " + side);
|
||||
logInfo("createOrder -> type = " + type);
|
||||
logInfo("createOrder -> amount = " + amount);
|
||||
logInfo("createOrder -> price = " + price);
|
||||
|
||||
/* Basic Validations */
|
||||
if (side !== "buy" && side !== "sell") {
|
||||
logError("createOrder -> side must be either 'buy' or 'sell'.");
|
||||
return
|
||||
}
|
||||
if (exchange.has['createOrder'] === false) {
|
||||
logError("createOrder -> Exchange does not support createOrder command.");
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let order = await (exchange.createOrder(symbol, type, side, amount, price))
|
||||
return order.id
|
||||
} catch (err) {
|
||||
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
|
||||
logError("getOrder -> Error = " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelOrder(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
let orderId = tradingEngineOrder.exchangeId.value
|
||||
|
||||
/* Basic Logging */
|
||||
logInfo("getOrder -> Entering function. orderId = " + orderId);
|
||||
|
||||
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
|
||||
|
||||
/* Basic Validations */
|
||||
if (exchange.has['fetchOrder'] === false) {
|
||||
logError("getOrder -> Exchange does not support fetchOrder command.");
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let order = await exchange.cancelOrder(orderId, symbol)
|
||||
return true
|
||||
} catch (err) {
|
||||
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
|
||||
logError("getOrder -> Error = " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function logInfo(message) {
|
||||
logger.write(MODULE_NAME, '[INFO] ' + message)
|
||||
}
|
||||
|
||||
function logError(message) {
|
||||
logger.write(MODULE_NAME, '[ERROR] ' + message)
|
||||
}
|
||||
};
|
||||
@@ -244,7 +244,7 @@ exports.newSnapshots = function newSnapshots(bot, logger) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
tradingSystem.errors.push(nodeWithCode.id, err.message)
|
||||
tradingSystem.errors.push([nodeWithCode.id, err.message])
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> addCodeToSnapshot -> nodeWithCode.code = ' + nodeWithCode.code)
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> addCodeToSnapshot -> err = ' + err.stack)
|
||||
}
|
||||
|
||||
@@ -190,12 +190,6 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
|
||||
tradingEngine.current.distanceToEvent.createOrder.value++
|
||||
}
|
||||
|
||||
if (
|
||||
tradingEngine.current.distanceToEvent.moveOrder.value > 0
|
||||
) {
|
||||
tradingEngine.current.distanceToEvent.moveOrder.value++
|
||||
}
|
||||
|
||||
if (
|
||||
tradingEngine.current.distanceToEvent.cancelOrder.value > 0
|
||||
) {
|
||||
|
||||
@@ -1,317 +1,584 @@
|
||||
exports.newTradingExecution = function newTradingExecution(bot, logger) {
|
||||
exports.newTradingExecution = function newTradingExecution(bot, logger, tradingEngineModule) {
|
||||
/*
|
||||
The Trading Execution modules manages the execution of orders against the exchanges.
|
||||
*/
|
||||
const MODULE_NAME = 'Trading Execution'
|
||||
|
||||
let thisObject = {
|
||||
takePosition: takePosition,
|
||||
closePosition: closePosition,
|
||||
checkExecution: checkExecution,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let tradingEngine
|
||||
let tradingSystem
|
||||
let sessionParameters
|
||||
|
||||
const EXCHANGE_API_MODULE = require('./ExchangeAPI.js')
|
||||
let exchangeAPIModule = EXCHANGE_API_MODULE.newExchangeAPI(bot, logger)
|
||||
|
||||
const ANNOUNCEMENTS_MODULE = require('./Announcements.js')
|
||||
let announcementsModule = ANNOUNCEMENTS_MODULE.newAnnouncements(bot, logger)
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize() {
|
||||
|
||||
tradingSystem = bot.simulationState.tradingSystem
|
||||
tradingEngine = bot.simulationState.tradingEngine
|
||||
sessionParameters = bot.SESSION.parameters
|
||||
|
||||
exchangeAPIModule.initialize()
|
||||
announcementsModule.initialize()
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
tradingSystem = undefined
|
||||
tradingEngine = undefined
|
||||
sessionParameters = undefined
|
||||
|
||||
exchangeAPIModule.finalize()
|
||||
exchangeAPIModule = undefined
|
||||
|
||||
announcementsModule.finalize()
|
||||
announcementsModule = undefined
|
||||
}
|
||||
|
||||
function takePosition() {
|
||||
/* Check that we are in LIVE or FORWARD MODE */
|
||||
if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') {
|
||||
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> Taking a Position in ' + bot.SESSION.type)
|
||||
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.size.value = ' + tradingEngine.current.position.size.value)
|
||||
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value)
|
||||
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> slippageAmount = ' + slippageAmount)
|
||||
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value)
|
||||
async function checkExecution(executionNode, stageIsOpening, stageIsClosing, stageSizeLimit, stageOrdersSize, stageFilledSize) {
|
||||
|
||||
/* We see if we need to put the actual order at the exchange. */
|
||||
if (variable.executionContext !== undefined) {
|
||||
switch (variable.executionContext.status) {
|
||||
case 'Without a Position': { // We need to put the order because It was not put yet.
|
||||
if (strategy.openStage !== undefined) {
|
||||
if (strategy.openStage.openExecution !== undefined) {
|
||||
putOpeningOrder()
|
||||
return
|
||||
/* Enforcing Precision Limit */
|
||||
stageSizeLimit.value = global.PRECISE(stageSizeLimit.value, 10)
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
|
||||
await checkExecutionAlgorithms(executionNode)
|
||||
|
||||
async function checkExecutionAlgorithms(executionNode) {
|
||||
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
|
||||
let executionAlgorithm = executionNode.executionAlgorithms[i]
|
||||
await checkOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
|
||||
await checkOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
|
||||
await checkOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
|
||||
await checkOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkOrders(orders, executionAlgorithm, executionNode) {
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
|
||||
let tradingSystemOrder = orders[i]
|
||||
|
||||
/* Trading System Validations */
|
||||
if (tradingSystemOrder.config.positionSizePercentage === undefined) { continue }
|
||||
if (tradingSystemOrder.referenceParent === undefined) { continue }
|
||||
|
||||
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
|
||||
|
||||
/* Trading Engine Validations */
|
||||
if (tradingEngineOrder.serialNumber === undefined) { continue }
|
||||
if (tradingEngineOrder.identifier === undefined) { continue }
|
||||
if (tradingEngineOrder.exchangeId === undefined) { continue }
|
||||
if (tradingEngineOrder.begin === undefined) { continue }
|
||||
if (tradingEngineOrder.end === undefined) { continue }
|
||||
if (tradingEngineOrder.rate === undefined) { continue }
|
||||
if (tradingEngineOrder.size === undefined) { continue }
|
||||
if (tradingEngineOrder.status === undefined) { continue }
|
||||
if (tradingEngineOrder.algorithmName === undefined) { continue }
|
||||
if (tradingEngineOrder.orderCounters === undefined) { continue }
|
||||
if (tradingEngineOrder.orderCounters.periods === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.days === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.actualRate === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.feesPaid === undefined) { continue }
|
||||
|
||||
switch (tradingEngineOrder.status.value) {
|
||||
case 'Not Open': {
|
||||
{
|
||||
/* Check if we need to Create this Order */
|
||||
if (stageIsClosing === true) { continue }
|
||||
let situationName = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
|
||||
if (situationName !== undefined) {
|
||||
|
||||
/* Open a new order */
|
||||
await openOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Position Closed': { // Waiting for a confirmation that the position was closed.
|
||||
if (strategy.openStage !== undefined) {
|
||||
if (strategy.openStage.openExecution !== undefined) {
|
||||
putOpeningOrder()
|
||||
return
|
||||
case 'Open': {
|
||||
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
|
||||
tradingEngineOrder.orderCounters.periods.value++
|
||||
tradingEngineOrder.orderStatistics.days.value = tradingEngineOrder.orderCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
|
||||
|
||||
/* Simulate Events that happens at the Exchange, if needed. */
|
||||
simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder)
|
||||
|
||||
/* Check Events that happens at the Exchange, if needed. */
|
||||
await checkExchangeEvents(tradingSystemOrder, tradingEngineOrder)
|
||||
|
||||
/* If the stage is closing or the order is not still Open, we wont be cancelling orders based on defined events */
|
||||
if (stageIsClosing !== true && tradingEngineOrder.status.value === 'Open') {
|
||||
|
||||
/* Check if we need to Cancel this Order */
|
||||
let situationName = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
|
||||
if (situationName !== undefined) {
|
||||
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
|
||||
await exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Taking Position': { // Waiting for a confirmation that the position was taken.
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position.')
|
||||
break
|
||||
}
|
||||
case 'In a Position': { // This should mean that we already put the order at the exchange.
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position.')
|
||||
break
|
||||
}
|
||||
}
|
||||
} else { // The context does not exist so it means we are not in a position.
|
||||
if (strategy.openStage !== undefined) {
|
||||
if (strategy.openStage.openExecution !== undefined) {
|
||||
putOpeningOrder()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Not trading live.')
|
||||
}
|
||||
|
||||
}
|
||||
async function openOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName) {
|
||||
/* Order Size Calculation */
|
||||
tradingEngineOrder.size.value = tradingSystem.formulas.get(executionAlgorithm.positionSize.formula.id) * tradingSystemOrder.config.positionSizePercentage / 100
|
||||
if (stageOrdersSize.value + tradingEngineOrder.size.value > stageSizeLimit.value) {
|
||||
/* We reduce the size to the remaining size of the position. */
|
||||
tradingEngineOrder.size.value = stageSizeLimit.value - stageOrdersSize.value
|
||||
tradingEngineOrder.size.value = global.PRECISE(tradingEngineOrder.size.value, 10)
|
||||
}
|
||||
|
||||
function putOpeningOrder() {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function.')
|
||||
/* Check Order Size */
|
||||
if (tradingEngineOrder.size.value <= 0) { return }
|
||||
|
||||
/* We wont take a position unless we are withing the sessionParameters.timeRange.config.initialDatetime and the sessionParameters.timeRange.config.finalDatetime range */
|
||||
if (sessionParameters.timeRange.config.initialDatetime !== undefined) {
|
||||
if (candle.end < sessionParameters.timeRange.config.initialDatetime) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime)
|
||||
takePositionAtSimulation()
|
||||
return
|
||||
/* Order Rate Calculation */
|
||||
tradingEngineOrder.rate.value = tradingEngine.current.position.rate.value // By default this is the order rate.
|
||||
if (tradingSystemOrder.positionRate !== undefined) {
|
||||
if (tradingSystemOrder.positionRate.formula !== undefined) {
|
||||
tradingEngineOrder.rate.value = tradingSystem.formulas.get(tradingSystemOrder.positionRate.formula.id)
|
||||
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
/* Place Order at the Exchange */
|
||||
let result = await createOrderAtExchange(tradingSystemOrder, tradingEngineOrder)
|
||||
if (result !== true) { return }
|
||||
|
||||
/* Updating Episode Counters */
|
||||
tradingEngine.episode.episodeCounters.orders.value++
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.createOrder.value = 1
|
||||
|
||||
/* Create Order Procedure */
|
||||
tradingEngineOrder.status.value = 'Open'
|
||||
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngineOrder.begin.value = tradingEngine.current.candle.begin.value
|
||||
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
|
||||
tradingEngineOrder.serialNumber.value = tradingEngine.episode.episodeCounters.orders.value
|
||||
tradingEngineOrder.orderName.value = tradingSystemOrder.name
|
||||
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
|
||||
tradingEngineOrder.situationName.value = situationName
|
||||
|
||||
/* Update Stage Orders Size */
|
||||
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
}
|
||||
|
||||
async function createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
return true
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let orderId = await exchangeAPIModule.createOrder(tradingSystemOrder, tradingEngineOrder)
|
||||
|
||||
if (orderId !== undefined) {
|
||||
tradingEngineOrder.exchangeId.value = orderId
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/* We wont take a position if we are past the final datetime */
|
||||
if (sessionParameters.timeRange.config.finalDatetime !== undefined) {
|
||||
if (candle.begin > sessionParameters.timeRange.config.finalDatetime) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime)
|
||||
takePositionAtSimulation()
|
||||
return
|
||||
function simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
return
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
return
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter by what is defined at the Strategy */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return }
|
||||
|
||||
let previousSizeFilled = tradingEngineOrder.orderStatistics.sizeFilled.value
|
||||
let previousPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let previousAmountReceived = tradingEngineOrder.orderStatistics.amountReceived.value
|
||||
let previousFeesPaid = tradingEngineOrder.orderStatistics.feesPaid.value
|
||||
|
||||
actualRateSimulation()
|
||||
feesPaidSimulation()
|
||||
orderFillingSimulation()
|
||||
sizeFilledSimulation()
|
||||
amountReceivedSimulation()
|
||||
|
||||
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid)
|
||||
|
||||
/* If the Stage is Closing and this order is still open, we need to cancel it now */
|
||||
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
|
||||
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
|
||||
}
|
||||
|
||||
function actualRateSimulation() {
|
||||
/* Actual Rate Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) {
|
||||
/* Calculate this only once for this order */
|
||||
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) {
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
/* Actual Rate is simulated based on the Session Paremeters */
|
||||
let slippageAmount = tradingEngineOrder.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100
|
||||
switch (tradingSystemOrder.type) {
|
||||
case 'Market Sell Order': {
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount
|
||||
break
|
||||
}
|
||||
case 'Market Buy Order': {
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
/* In Limit Orders the actual rate is the rate of the order, there is no slippage */
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
|
||||
}
|
||||
|
||||
function feesPaidSimulation() {
|
||||
/* Fees Paid Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
|
||||
if (tradingEngineOrder.orderStatistics.feesPaid.value === tradingEngineOrder.orderStatistics.feesPaid.config.initialValue) {
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Fees are simulated based on the Session Paremeters */
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.maker / 100
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.value, 10)
|
||||
}
|
||||
|
||||
function orderFillingSimulation() {
|
||||
/* Order Filling Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) {
|
||||
|
||||
/* Percentage Filled */
|
||||
let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) {
|
||||
percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
}
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
|
||||
|
||||
/* Check if we need to close */
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Filled'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sizeFilledSimulation() {
|
||||
/* Size Filled */
|
||||
tradingEngineOrder.orderStatistics.sizeFilled.value = tradingEngineOrder.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
tradingEngineOrder.orderStatistics.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.sizeFilled.value, 10)
|
||||
}
|
||||
|
||||
function amountReceivedSimulation() {
|
||||
/* Amount Received */
|
||||
tradingEngineOrder.orderStatistics.amountReceived.value = tradingEngineOrder.orderStatistics.sizeFilled.value * tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate
|
||||
tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
/* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
|
||||
if (variable.executionContext !== undefined) {
|
||||
if (variable.executionContext.periods !== undefined) {
|
||||
if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution.')
|
||||
takePositionAtSimulation()
|
||||
async function checkExchangeEvents(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
return true
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
|
||||
|
||||
if (order === undefined) { return }
|
||||
|
||||
const AT_EXCHANGE_STATUS = {
|
||||
OPEN: 'open',
|
||||
CLOSED: 'closed',
|
||||
CANCELLED: 'canceled'
|
||||
}
|
||||
|
||||
/* Status Checks */
|
||||
if (order.remaining === 0 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Filled'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
if (order.remaining > 0 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Closed at the Exchange'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
if (order.status === AT_EXCHANGE_STATUS.CANCELLED) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Cancelled at the Exchange'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
|
||||
syncWithExchange(tradingSystemOrder, tradingEngineOrder, order)
|
||||
|
||||
/* Forced Cancellation Check */
|
||||
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
|
||||
await exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
|
||||
}
|
||||
}
|
||||
|
||||
function syncWithExchange(tradingSystemOrder, tradingEngineOrder, order) {
|
||||
|
||||
let previousSizeFilled = tradingEngineOrder.orderStatistics.sizeFilled.value
|
||||
let previousPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let previousAmountReceived = tradingEngineOrder.orderStatistics.amountReceived.value
|
||||
let previousFeesPaid = tradingEngineOrder.orderStatistics.feesPaid.value
|
||||
|
||||
/* Fees Paid */
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value - order.amount
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.value, 10)
|
||||
|
||||
/* Percentage Filled */
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
|
||||
|
||||
/* Size Filled */
|
||||
tradingEngineOrder.orderStatistics.sizeFilled.value = order.filled
|
||||
tradingEngineOrder.orderStatistics.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.sizeFilled.value, 10)
|
||||
|
||||
/* Amount Received */
|
||||
tradingEngineOrder.orderStatistics.amountReceived.value = order.cost
|
||||
tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10)
|
||||
|
||||
/* Actual Rate */
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = order.price
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
|
||||
|
||||
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid)
|
||||
}
|
||||
|
||||
function doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid) {
|
||||
|
||||
/* Unaccount for the previous Stage Filled Size */
|
||||
stageFilledSize.value = stageFilledSize.value - tradingEngineOrder.size.value * previousPercentageFilled / 100
|
||||
/* Account for the current Stage Filled Size */
|
||||
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
|
||||
|
||||
|
||||
/* Balances Update */
|
||||
switch (true) {
|
||||
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
|
||||
|
||||
/* Undo the previous accounting */
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - previousFeesPaid - previousSizeFilled
|
||||
/* Account the current filling and fees */
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngineOrder.orderStatistics.sizeFilled.value + tradingEngineOrder.orderStatistics.feesPaid.value
|
||||
|
||||
/* Undo the previous accounting */
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value + previousAmountReceived
|
||||
/* Account the current filling and fees */
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngineOrder.orderStatistics.amountReceived.value
|
||||
|
||||
break
|
||||
}
|
||||
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
|
||||
|
||||
/* Undo the previous accounting */
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + previousFeesPaid + previousSizeFilled
|
||||
/* Account the current filling and fees */
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngineOrder.orderStatistics.sizeFilled.value - tradingEngineOrder.orderStatistics.feesPaid.value
|
||||
|
||||
/* Undo the previous accounting */
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value - previousAmountReceived
|
||||
/* Account the current filling and fees */
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngineOrder.orderStatistics.amountReceived.value
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
return
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
return
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = exitType
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
/* Since the order was cancelled, we remove the unfilled amount of the order from here */
|
||||
let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100
|
||||
stageOrdersSize.value = stageOrdersSize.value - unfilledSize
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
}
|
||||
|
||||
async function exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
return
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */
|
||||
if (currentDay) {
|
||||
let today = new Date(Math.trunc((new Date().valueOf()) / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS)
|
||||
let processDay = new Date(Math.trunc(currentDay.valueOf() / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS)
|
||||
if (today.valueOf() !== processDay.valueOf()) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading.')
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> today = ' + today)
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = ' + processDay)
|
||||
takePositionAtSimulation()
|
||||
return
|
||||
/* Check if we can cancel the order at the Exchange. */
|
||||
let result = await exchangeAPIModule.cancelOrder(tradingSystemOrder, tradingEngineOrder)
|
||||
if (result === true) {
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = exitType
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
/* Since the order was cancelled, we remove the unfilled amount of the order from here */
|
||||
let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100
|
||||
stageOrdersSize.value = stageOrdersSize.value - unfilledSize
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
|
||||
/*
|
||||
Perhaps the order was filled a bit more between the last time we checked and when it was cancelled.
|
||||
To sync our accounting, we need to check the order one last time and if it changed, fix it.
|
||||
*/
|
||||
|
||||
let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
|
||||
|
||||
if (order === undefined) { return }
|
||||
|
||||
syncWithExchange(tradingSystemOrder, tradingEngineOrder, order)
|
||||
}
|
||||
}
|
||||
|
||||
let orderPrice
|
||||
let amountA
|
||||
let amountB
|
||||
let orderSide
|
||||
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
orderSide = 'sell'
|
||||
|
||||
orderPrice = tradingEngine.current.position.rate.value
|
||||
|
||||
amountA = tradingEngine.current.position.size.value * orderPrice
|
||||
amountB = tradingEngine.current.position.size.value
|
||||
} else {
|
||||
orderSide = 'buy'
|
||||
|
||||
orderPrice = tradingEngine.current.position.rate.value
|
||||
|
||||
amountA = tradingEngine.current.position.size.value
|
||||
amountB = tradingEngine.current.position.size.value / orderPrice
|
||||
}
|
||||
|
||||
variable.executionContext = {
|
||||
status: 'Taking Position',
|
||||
periods: tradingEngine.episode.episodeCounters.periods.value
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order.')
|
||||
exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated)
|
||||
|
||||
function onOrderCreated(err, order) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function.')
|
||||
|
||||
try {
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ')
|
||||
variable.executionContext = {
|
||||
status: 'In a Position',
|
||||
periods: tradingEngine.episode.episodeCounters.periods.value,
|
||||
amountA: amountA,
|
||||
amountB: amountB,
|
||||
orderId: order.id
|
||||
}
|
||||
takePositionAtSimulation()
|
||||
return
|
||||
function checkOrderEvent(event, order, executionAlgorithm, executionNode) {
|
||||
if (event !== undefined) {
|
||||
for (let k = 0; k < event.situations.length; k++) {
|
||||
let situation = event.situations[k]
|
||||
let passed
|
||||
if (situation.conditions.length > 0) {
|
||||
passed = true
|
||||
}
|
||||
case global.DEFAULT_FAIL_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ')
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message)
|
||||
strategy.openStage.openExecution.error = err.message
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ')
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message)
|
||||
strategy.openStage.openExecution.error = err.message
|
||||
afterLoop()
|
||||
return
|
||||
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
tradingSystem.highlights.push(situation.id)
|
||||
tradingSystem.highlights.push(event.id)
|
||||
tradingSystem.highlights.push(order.id)
|
||||
tradingSystem.highlights.push(executionAlgorithm.id)
|
||||
tradingSystem.highlights.push(executionNode.id)
|
||||
|
||||
announcementsModule.makeAnnoucements(event)
|
||||
return situation.name // if the event is triggered, we return the name of the situation that passed
|
||||
}
|
||||
}
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> err = ' + err.stack)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closePosition() {
|
||||
/* Check that we are in LIVE MODE */
|
||||
if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') {
|
||||
/* We see if we need to put the actual order at the exchange. */
|
||||
if (variable.executionContext !== undefined) {
|
||||
switch (variable.executionContext.status) {
|
||||
case 'Without a Position': { // No way to close anything at the exchange.
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position.')
|
||||
break
|
||||
}
|
||||
case 'In a Position': { // This should mean that we already put the order at the exchange.
|
||||
if (strategy.closeStage !== undefined) {
|
||||
if (strategy.closeStage.closeExecution !== undefined) {
|
||||
putClosingOrder()
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Closing Position': { // Waiting for a confirmation that the position was taken.
|
||||
if (strategy.closeStage !== undefined) {
|
||||
if (strategy.closeStage.closeExecution !== undefined) {
|
||||
putClosingOrder()
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'Position Closed': { //
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed.')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function putClosingOrder() {
|
||||
/* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
|
||||
if (variable.executionContext !== undefined) {
|
||||
if (variable.executionContext.periods !== undefined) {
|
||||
if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution.')
|
||||
closePositionAtSimulation()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let orderPrice
|
||||
let amountA
|
||||
let amountB
|
||||
let orderSide
|
||||
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
orderSide = 'buy'
|
||||
|
||||
orderPrice = ticker.last + 100 // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
|
||||
|
||||
amountA = tradingEngine.current.balance.quotedAsset.value
|
||||
amountB = tradingEngine.current.balance.quotedAsset.value / orderPrice
|
||||
} else {
|
||||
orderSide = 'sell'
|
||||
|
||||
orderPrice = ticker.last - 100 // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
|
||||
|
||||
amountA = tradingEngine.current.balance.baseAsset.value * orderPrice
|
||||
amountB = tradingEngine.current.balance.baseAsset.value
|
||||
}
|
||||
|
||||
variable.executionContext = {
|
||||
status: 'Closing Position',
|
||||
periods: tradingEngine.episode.episodeCounters.periods.value
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange.')
|
||||
exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated)
|
||||
|
||||
function onOrderCreated(err, order) {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function.')
|
||||
|
||||
try {
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ')
|
||||
variable.executionContext = {
|
||||
status: 'Position Closed',
|
||||
periods: tradingEngine.episode.episodeCounters.periods.value,
|
||||
amountA: amountA,
|
||||
amountB: amountB,
|
||||
orderId: order.id
|
||||
}
|
||||
closePositionAtSimulation()
|
||||
return
|
||||
}
|
||||
case global.DEFAULT_FAIL_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ')
|
||||
/* We will assume that the problem is temporary, and expect that it will work at the next execution. */
|
||||
strategy.closeStage.closeExecution.error = err.message
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ')
|
||||
strategy.closeStage.closeExecution.error = err.message
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
}
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> err = ' + err.stack)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
updateStopLoss: updateStopLoss,
|
||||
updateTakeProfit: updateTakeProfit,
|
||||
updateSizeAndRate: updateSizeAndRate,
|
||||
applySlippageToRate: applySlippageToRate,
|
||||
updateEnds: updateEnds,
|
||||
updateStatus: updateStatus,
|
||||
updateCounters: updateCounters,
|
||||
@@ -44,6 +43,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
function openPosition(situationName) {
|
||||
tradingEngine.current.position.status.value = 'Open'
|
||||
tradingEngine.current.position.serialNumber.value = tradingEngine.episode.episodeCounters.positions.value
|
||||
tradingEngine.current.position.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngine.current.position.begin.value = tradingEngine.current.candle.begin.value
|
||||
tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value
|
||||
|
||||
@@ -94,16 +94,8 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
function updateSizeAndRate(size, rate) {
|
||||
tradingEngine.current.position.size.value = global.PRECISE(size, 10)
|
||||
tradingEngine.current.position.rate.value = global.PRECISE(rate, 10)
|
||||
}
|
||||
|
||||
function applySlippageToRate() {
|
||||
let slippageAmount = tradingEngine.current.position.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100
|
||||
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value - slippageAmount
|
||||
} else {
|
||||
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value + slippageAmount
|
||||
}
|
||||
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value
|
||||
}
|
||||
|
||||
function preventStopLossDistortion() {
|
||||
|
||||
@@ -119,7 +119,14 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
the Root Node specifically for this property only.
|
||||
*/
|
||||
if (recordProperty.config.nodePath !== undefined) {
|
||||
propertyRootNode = eval(recordProperty.config.nodePath)
|
||||
try {
|
||||
propertyRootNode = eval(recordProperty.config.nodePath)
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> recordProperty.name = " + recordProperty.name);
|
||||
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> recordProperty.config.nodePath = " + recordProperty.config.nodePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> err.stack = " + err.stack);
|
||||
throw 'Can not continue with a Definition Error like this.'
|
||||
}
|
||||
}
|
||||
/*
|
||||
The Target Node is the node from where we are going to exctract the value.
|
||||
|
||||
@@ -42,10 +42,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER
|
||||
|
||||
/* These are the Modules we will need to run the Simulation */
|
||||
const EXCHANGE_API = require('../ExchangeAPI');
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, bot);
|
||||
exchangeAPI.initialize();
|
||||
|
||||
const TRADING_ENGINE_MODULE = require('./TradingEngine.js')
|
||||
let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger)
|
||||
tradingEngineModule.initialize()
|
||||
@@ -112,7 +108,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
loop()
|
||||
}
|
||||
|
||||
function loop() {
|
||||
async function loop() {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Processing candle # ' + tradingEngine.current.candle.index.value) }
|
||||
let candle = candles[tradingEngine.current.candle.index.value] // This is the current candle the Simulation is working at.
|
||||
|
||||
@@ -150,13 +146,13 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingSystemModule.triggerStage()
|
||||
|
||||
/* Run the Open Stage */
|
||||
tradingSystemModule.openStage()
|
||||
await tradingSystemModule.openStage()
|
||||
|
||||
/* Run the Manage Stage */
|
||||
tradingSystemModule.manageStage()
|
||||
|
||||
/* Run the Close Stage */
|
||||
tradingSystemModule.closeStage()
|
||||
await tradingSystemModule.closeStage()
|
||||
|
||||
/* Add new records to the process output */
|
||||
tradingRecordsModule.appendRecords()
|
||||
|
||||
@@ -30,6 +30,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
|
||||
function openStrategy(index, situationName, strategyName) {
|
||||
tradingEngine.current.strategy.status.value = 'Open'
|
||||
tradingEngine.current.strategy.serialNumber.value = tradingEngine.episode.episodeCounters.strategies.value
|
||||
tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngine.current.strategy.begin.value = tradingEngine.current.candle.begin.value
|
||||
tradingEngine.current.strategy.end.value = tradingEngine.current.candle.end.value
|
||||
tradingEngine.current.strategy.beginRate.value = tradingEngine.current.candle.min.value
|
||||
|
||||
@@ -18,29 +18,25 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
let chart
|
||||
|
||||
let tradingSystem
|
||||
let tradingEngine
|
||||
let sessionParameters
|
||||
|
||||
let conditions = new Map()
|
||||
let formulas = new Map()
|
||||
|
||||
const TRADING_STRATEGY_MODULE = require('./TradingStrategy.js')
|
||||
let tradingStrategyModule = TRADING_STRATEGY_MODULE.newTradingStrategy(bot, logger, tradingEngineModule)
|
||||
|
||||
const TRADING_POSITION_MODULE = require('./TradingPosition.js')
|
||||
let tradingPositionModule = TRADING_POSITION_MODULE.newTradingPosition(bot, logger, tradingEngineModule)
|
||||
|
||||
const TRADING_ENGINE_EXECUTION = require('./TradingExecution.js')
|
||||
let tradingExecutionModule = TRADING_ENGINE_EXECUTION.newTradingExecution(bot, logger, tradingEngineModule)
|
||||
|
||||
const ANNOUNCEMENTS_MODULE = require('./Announcements.js')
|
||||
let announcementsModule = ANNOUNCEMENTS_MODULE.newAnnouncements(bot, logger)
|
||||
|
||||
const SNAPSHOTS_MODULE = require('./Snapshots.js')
|
||||
let snapshotsModule = SNAPSHOTS_MODULE.newSnapshots(bot, logger)
|
||||
|
||||
const TRADING_ENGINE_EXECUTION = require('./TradingExecution.js')
|
||||
let tradingExecutionModule = TRADING_ENGINE_EXECUTION.newTradingExecution(bot, logger)
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize() {
|
||||
@@ -48,11 +44,35 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
tradingEngine = bot.simulationState.tradingEngine
|
||||
sessionParameters = bot.SESSION.parameters
|
||||
|
||||
tradingSystem.conditions = new Map()
|
||||
tradingSystem.formulas = new Map()
|
||||
|
||||
tradingStrategyModule.initialize()
|
||||
tradingPositionModule.initialize()
|
||||
announcementsModule.initialize()
|
||||
snapshotsModule.initialize()
|
||||
tradingExecutionModule.initialize()
|
||||
|
||||
/* Adding Functions used elsewhere to Trading System Definition */
|
||||
tradingSystem.checkConditions = function (situation, passed) {
|
||||
/* Given a Situation, we check if all children conditions are true or not */
|
||||
for (let m = 0; m < situation.conditions.length; m++) {
|
||||
|
||||
let condition = situation.conditions[m]
|
||||
let value = false
|
||||
if (tradingSystem.conditions.get(condition.id) !== undefined) {
|
||||
value = tradingSystem.conditions.get(condition.id)
|
||||
}
|
||||
if (value === true) {
|
||||
tradingSystem.highlights.push(condition.id)
|
||||
}
|
||||
else {
|
||||
passed = false
|
||||
}
|
||||
tradingSystem.values.push([condition.id, value])
|
||||
}
|
||||
return passed
|
||||
}
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
@@ -73,8 +93,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
|
||||
chart = undefined
|
||||
|
||||
conditions = undefined
|
||||
formulas = undefined
|
||||
tradingSystem.conditions = undefined
|
||||
tradingSystem.formulas = undefined
|
||||
|
||||
tradingSystem.highlights = undefined
|
||||
tradingSystem.errors = undefined
|
||||
@@ -263,7 +283,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
conditions.set(node.id, value)
|
||||
tradingSystem.conditions.set(node.id, value)
|
||||
|
||||
if (value === true) {
|
||||
tradingSystem.highlights.push(node.id)
|
||||
@@ -304,7 +324,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
formulas.set(node.id, value)
|
||||
tradingSystem.formulas.set(node.id, value)
|
||||
|
||||
if (error !== undefined) {
|
||||
tradingSystem.errors.push([node.id, error])
|
||||
@@ -354,7 +374,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -407,7 +427,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -445,7 +465,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -483,7 +503,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
function openStage() {
|
||||
async function openStage() {
|
||||
/* Abort Open Stage Check */
|
||||
if (tradingEngine.current.strategy.openStageStatus.value === 'Open' && tradingEngine.current.strategy.closeStageStatus.value === 'Open') {
|
||||
/*
|
||||
@@ -496,13 +516,14 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategy.openStageStatus.value === 'Opening') {
|
||||
/* Reset the Exchange Orders data structure to its initial value */
|
||||
tradingEngine.exchangeOrders.initialize(tradingEngine.exchangeOrders)
|
||||
|
||||
/* 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 */
|
||||
tradingEngine.exchangeOrders.initialize(tradingEngine.exchangeOrders)
|
||||
|
||||
evalConditions(stageNode, 'Initial Definition')
|
||||
evalFormulas(stageNode, 'Initial Definition')
|
||||
|
||||
@@ -511,13 +532,15 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
calculateTakeProfit()
|
||||
calculateStopLoss()
|
||||
|
||||
takePosition()
|
||||
/* Remember the balance we had before taking the position to later calculate profit or loss */
|
||||
tradingEngine.previous.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value
|
||||
tradingEngine.previous.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value
|
||||
|
||||
/* Check Execution in opening stage mode */
|
||||
/* Check Execution in opening stage node */
|
||||
evalConditions(stageNode, 'Open Execution')
|
||||
evalFormulas(stageNode, 'Open Execution')
|
||||
|
||||
checkExecution(executionNode, true, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
await tradingExecutionModule.checkExecution(executionNode, true, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
|
||||
/* From here on, the state is officially Open */
|
||||
tradingStrategyModule.updateStageStatus('Open Stage', 'Open')
|
||||
@@ -537,7 +560,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
evalConditions(stageNode, 'Open Execution')
|
||||
evalFormulas(stageNode, 'Open Execution')
|
||||
|
||||
checkExecution(executionNode, false, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
await tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
|
||||
/*
|
||||
The Open is finished when the fillSize reaches the Position Size.
|
||||
@@ -552,7 +575,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
/* Closing Status Procedure */
|
||||
if (tradingEngine.current.strategy.openStageStatus.value === 'Closing') {
|
||||
/*
|
||||
During the closing status, we do not place new orders, just check
|
||||
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
|
||||
@@ -565,7 +588,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
evalConditions(stageNode, 'Open Execution')
|
||||
evalFormulas(stageNode, 'Open Execution')
|
||||
|
||||
checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
await tradingExecutionModule.checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -584,9 +607,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
/* Position size and rate */
|
||||
tradingPositionModule.updateSizeAndRate(getPositionSize(), getPositionRate())
|
||||
|
||||
/* We take what was calculated at the formula of position rate and apply the slippage. */
|
||||
tradingPositionModule.applySlippageToRate()
|
||||
|
||||
function getPositionSize() {
|
||||
let balance
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
@@ -602,7 +622,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
if (strategy.openStage.initialDefinition.positionSize === undefined) return DEFAULT_VALUE
|
||||
if (strategy.openStage.initialDefinition.positionSize.formula === undefined) return DEFAULT_VALUE
|
||||
|
||||
let value = formulas.get(strategy.openStage.initialDefinition.positionSize.formula.id)
|
||||
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionSize.formula.id)
|
||||
if (value === undefined) return DEFAULT_VALUE
|
||||
return value
|
||||
}
|
||||
@@ -616,30 +636,11 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
if (strategy.openStage.initialDefinition.positionRate === undefined) return DEFAULT_VALUE
|
||||
if (strategy.openStage.initialDefinition.positionRate.formula === undefined) return DEFAULT_VALUE
|
||||
|
||||
let value = formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
|
||||
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
|
||||
if (value === undefined) return DEFAULT_VALUE
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function takePosition() {
|
||||
tradingEngine.previous.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value
|
||||
tradingEngine.previous.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value
|
||||
|
||||
let feePaid = 0
|
||||
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
feePaid = tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value - feePaid
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.current.position.size.value
|
||||
} else {
|
||||
feePaid = tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value - feePaid
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.current.position.size.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function manageStage() {
|
||||
@@ -714,7 +715,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -744,7 +745,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -821,7 +822,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -851,7 +852,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
passed = tradingSystem.checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
@@ -927,7 +928,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
function closeStage() {
|
||||
async function closeStage() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategy.closeStageStatus.value === 'Opening') {
|
||||
/*
|
||||
@@ -951,7 +952,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
evalConditions(stageNode, 'Close Execution')
|
||||
evalFormulas(stageNode, 'Close Execution')
|
||||
|
||||
checkExecution(executionNode, false, false, tradingEngine.current.position.openStageFilledSize, tradingEngine.current.position.closeStageOrdersSize, tradingEngine.current.position.closeStageFilledSize)
|
||||
await tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.openStageFilledSize, tradingEngine.current.position.closeStageOrdersSize, tradingEngine.current.position.closeStageFilledSize)
|
||||
|
||||
/* Close the Stage Validation */
|
||||
if (tradingEngine.current.position.closeStageFilledSize.value === tradingEngine.current.position.openStageFilledSize.value) {
|
||||
@@ -976,18 +977,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
|
||||
function exitPositionAndStrategy() {
|
||||
|
||||
/* New Balances Calculation */
|
||||
let feePaid = 0
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
feePaid = tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value - feePaid
|
||||
tradingEngine.current.balance.quotedAsset.value = 0
|
||||
} else {
|
||||
feePaid = tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value - feePaid
|
||||
tradingEngine.current.balance.baseAsset.value = 0
|
||||
}
|
||||
|
||||
/* Needed for Episode Statistics */
|
||||
tradingPositionModule.updateStatistics()
|
||||
|
||||
@@ -1064,7 +1053,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
if (phase !== undefined) {
|
||||
if (phase.formula !== undefined) {
|
||||
let previousValue = tradingEngine.current.position.stopLoss.value
|
||||
tradingPositionModule.applyStopLossFormula(formulas, phase.formula.id)
|
||||
tradingPositionModule.applyStopLossFormula(tradingSystem.formulas, phase.formula.id)
|
||||
|
||||
if (tradingEngine.current.position.stopLoss.value !== previousValue) {
|
||||
announcementsModule.makeAnnoucements(phase)
|
||||
@@ -1096,7 +1085,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
if (phase !== undefined) {
|
||||
if (phase.formula !== undefined) {
|
||||
let previousValue = tradingEngine.current.position.takeProfit.value
|
||||
tradingPositionModule.applyTakeProfitFormula(formulas, phase.formula.id)
|
||||
tradingPositionModule.applyTakeProfitFormula(tradingSystem.formulas, phase.formula.id)
|
||||
|
||||
if (tradingEngine.current.position.takeProfit.value !== previousValue) {
|
||||
announcementsModule.makeAnnoucements(phase)
|
||||
@@ -1105,278 +1094,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
function checkExecution(executionNode, stageIsOpening, stageIsClosing, stageSizeLimit, stageOrdersSize, stageFilledSize) {
|
||||
|
||||
/* Enforcing Precision Limit */
|
||||
stageSizeLimit.value = global.PRECISE(stageSizeLimit.value, 10)
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
|
||||
|
||||
checkExecutionAlgorithms(executionNode)
|
||||
|
||||
function checkExecutionAlgorithms(executionNode) {
|
||||
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
|
||||
let executionAlgorithm = executionNode.executionAlgorithms[i]
|
||||
checkOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
|
||||
checkOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
|
||||
checkOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
|
||||
checkOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
|
||||
}
|
||||
}
|
||||
|
||||
function checkOrders(orders, executionAlgorithm, executionNode) {
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
let tradingSystemOrder = orders[i]
|
||||
|
||||
/* Basic Validations */
|
||||
if (tradingSystemOrder.config.positionSizePercentage === undefined) { continue }
|
||||
if (tradingSystemOrder.referenceParent === undefined) { continue }
|
||||
|
||||
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
|
||||
|
||||
if (tradingEngineOrder.identifier === undefined) { continue }
|
||||
if (tradingEngineOrder.begin === undefined) { continue }
|
||||
if (tradingEngineOrder.end === undefined) { continue }
|
||||
if (tradingEngineOrder.rate === undefined) { continue }
|
||||
if (tradingEngineOrder.size === undefined) { continue }
|
||||
if (tradingEngineOrder.status === undefined) { continue }
|
||||
if (tradingEngineOrder.algorithmName === undefined) { continue }
|
||||
if (tradingEngineOrder.orderCounters === undefined) { continue }
|
||||
if (tradingEngineOrder.orderCounters.periods === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.days === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.actualRate === undefined) { continue }
|
||||
if (tradingEngineOrder.orderStatistics.feesPaid === undefined) { continue }
|
||||
|
||||
switch (tradingEngineOrder.status.value) {
|
||||
case 'Not Open': {
|
||||
{
|
||||
/* Check if we need to Create this Order */
|
||||
if (stageIsClosing === true) { continue }
|
||||
let situationName = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
|
||||
if (situationName !== undefined) {
|
||||
|
||||
/* Order Size Calculation */
|
||||
tradingEngineOrder.size.value = formulas.get(executionAlgorithm.positionSize.formula.id) * tradingSystemOrder.config.positionSizePercentage / 100
|
||||
if (stageOrdersSize.value + tradingEngineOrder.size.value > stageSizeLimit.value) {
|
||||
/* We reduce the size to the remaining size of the position. */
|
||||
tradingEngineOrder.size.value = stageSizeLimit.value - stageOrdersSize.value
|
||||
tradingEngineOrder.size.value = global.PRECISE(tradingEngineOrder.size.value, 10)
|
||||
}
|
||||
|
||||
if (tradingEngineOrder.size.value <= 0) { continue }
|
||||
|
||||
/* Updating Episode Counters */
|
||||
tradingEngine.episode.episodeCounters.orders.value++
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.createOrder.value = 1
|
||||
|
||||
/* Create Order Procedure */
|
||||
tradingEngineOrder.status.value = 'Open'
|
||||
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngineOrder.begin.value = tradingEngine.current.candle.begin.value
|
||||
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
|
||||
tradingEngineOrder.serialNumber.value = tradingEngine.episode.episodeCounters.orders.value
|
||||
tradingEngineOrder.orderName.value = tradingSystemOrder.name
|
||||
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
|
||||
tradingEngineOrder.situationName.value = situationName
|
||||
|
||||
/* Order Rate Calculation */
|
||||
tradingEngineOrder.rate.value = tradingEngine.current.position.rate.value // By default this is the order rate.
|
||||
if (tradingSystemOrder.positionRate !== undefined) {
|
||||
if (tradingSystemOrder.positionRate.formula !== undefined) {
|
||||
tradingEngineOrder.rate.value = formulas.get(tradingSystemOrder.positionRate.formula.id)
|
||||
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
/* Update State Orders Size */
|
||||
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Open': {
|
||||
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
|
||||
tradingEngineOrder.orderCounters.periods.value++
|
||||
tradingEngineOrder.orderStatistics.days.value = tradingEngineOrder.orderCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
|
||||
|
||||
/* If the stage is closing we wont be moving or cancelling orders based on defined events */
|
||||
if (stageIsClosing !== true) {
|
||||
|
||||
/* Check if we need to Cancel this Order */
|
||||
let situationName = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
|
||||
if (situationName !== undefined) {
|
||||
|
||||
/* Since the order was cancelled, we remove the unfilled amount of the order from here */
|
||||
let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100
|
||||
stageOrdersSize.value = stageOrdersSize.value - unfilledSize
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
|
||||
/* Cancel the Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Cancelled by Event'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.cancelOrder.value = 1
|
||||
break
|
||||
}
|
||||
|
||||
/* Check if we need to Move this Order */
|
||||
if (tradingSystemOrder.moveOrderEvents !== undefined) {
|
||||
for (let j = 0; j < tradingSystemOrder.moveOrderEvents.length; j++) {
|
||||
let moveOrderEvent = tradingSystemOrder.moveOrderEvents[j]
|
||||
let situationName = checkOrderEvent(moveOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
|
||||
if (situationName !== undefined) {
|
||||
if (moveOrderEvent.positionRate !== undefined) {
|
||||
if (moveOrderEvent.positionRate.formula !== undefined) {
|
||||
|
||||
/* Move Order to a different Rate */
|
||||
tradingEngineOrder.rate.value = formulas.get(moveOrderEvent.positionRate.formula.id)
|
||||
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.moveOrder.value = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Simulate Events that happens at the Exchange, if needed. */
|
||||
if (stageIsOpening === false) {
|
||||
simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder) {
|
||||
|
||||
/* Filter by Session Type */
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
return
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
return
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter by what is defined at the Strategy */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return }
|
||||
|
||||
/* Partial Fill Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) {
|
||||
|
||||
/* Percentage Filled Calculation */
|
||||
let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) {
|
||||
percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
}
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Filled'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
|
||||
/* Filled Size Calculation */
|
||||
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * percentageFilled / 100
|
||||
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
|
||||
|
||||
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 0) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Cancelled by Closing Stage'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
if (tradingEngineOrder.orderStatistics.percentageFilled.value > 0 && tradingEngineOrder.orderStatistics.percentageFilled.value < 100) {
|
||||
|
||||
/* Close this Order */
|
||||
tradingEngineOrder.status.value = 'Closed'
|
||||
tradingEngineOrder.exitType.value = 'Partially Filled'
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.distanceToEvent.closeOrder.value = 1
|
||||
}
|
||||
|
||||
/* Since the order was cancelled, we remove the unfilled amount of the order from here */
|
||||
let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100
|
||||
stageOrdersSize.value = stageOrdersSize.value - unfilledSize
|
||||
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Actual Rate Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) {
|
||||
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) {
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fees Paid Simulation */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
|
||||
if (tradingEngineOrder.orderStatistics.feesPaid.value === tradingEngineOrder.orderStatistics.feesPaid.config.initialValue) {
|
||||
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkOrderEvent(event, order, executionAlgorithm, executionNode) {
|
||||
if (event !== undefined) {
|
||||
for (let k = 0; k < event.situations.length; k++) {
|
||||
let situation = event.situations[k]
|
||||
let passed
|
||||
if (situation.conditions.length > 0) {
|
||||
passed = true
|
||||
}
|
||||
|
||||
passed = checkConditions(situation, passed)
|
||||
|
||||
tradingSystem.values.push([situation.id, passed])
|
||||
if (passed) {
|
||||
tradingSystem.highlights.push(situation.id)
|
||||
tradingSystem.highlights.push(event.id)
|
||||
tradingSystem.highlights.push(order.id)
|
||||
tradingSystem.highlights.push(executionAlgorithm.id)
|
||||
tradingSystem.highlights.push(executionNode.id)
|
||||
|
||||
announcementsModule.makeAnnoucements(event)
|
||||
return situation.name // if the event is triggered, we return the name of the situation that passed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isItInside(elementWithTimestamp, elementWithBeginEnd) {
|
||||
/*
|
||||
The function is to allow in Conditions and Formulas to easily know when an element with timestamp (like the ones of single files) are inside the time range
|
||||
@@ -1388,25 +1105,5 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function checkConditions(situation, passed) {
|
||||
/* Given a Situation, we check if all children conditions are true or not */
|
||||
for (let m = 0; m < situation.conditions.length; m++) {
|
||||
|
||||
let condition = situation.conditions[m]
|
||||
let value = false
|
||||
if (conditions.get(condition.id) !== undefined) {
|
||||
value = conditions.get(condition.id)
|
||||
}
|
||||
if (value === true) {
|
||||
tradingSystem.highlights.push(condition.id)
|
||||
}
|
||||
else {
|
||||
passed = false
|
||||
}
|
||||
tradingSystem.values.push([condition.id, value])
|
||||
}
|
||||
return passed
|
||||
}
|
||||
}
|
||||
|
||||
+5
-9
@@ -225,18 +225,14 @@
|
||||
/* Bot Type */
|
||||
botConfig.type = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.type
|
||||
|
||||
/* Setting the Key to use */
|
||||
process.env.KEY = undefined
|
||||
process.env.SECRET = undefined
|
||||
|
||||
if (botConfig.processNode) {
|
||||
if (botConfig.processNode.marketReference) {
|
||||
if (botConfig.processNode.marketReference.keyInstance !== undefined) {
|
||||
if (botConfig.processNode.marketReference.keyInstance.referenceParent !== undefined) {
|
||||
let key = botConfig.processNode.marketReference.keyInstance.referenceParent
|
||||
if (botConfig.processNode.marketReference.keyReference !== undefined) {
|
||||
if (botConfig.processNode.marketReference.keyReference.referenceParent !== undefined) {
|
||||
let key = botConfig.processNode.marketReference.keyReference.referenceParent
|
||||
|
||||
process.env.KEY = key.config.codeName
|
||||
process.env.SECRET = key.config.secret
|
||||
botConfig.KEY = key.config.codeName
|
||||
botConfig.SECRET = key.config.secret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-25
@@ -161,41 +161,46 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) { // if the initialDatetime is missing we create a default one.
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date()).valueOf()
|
||||
} else {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()
|
||||
}
|
||||
if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) { // if the finalDatetime is missing we create a default one.
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date()).valueOf()
|
||||
} else {
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()
|
||||
}
|
||||
|
||||
/* Session Type Forced Values */
|
||||
let today = new Date((new Date()).valueOf())
|
||||
let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS
|
||||
let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Live Trading Session': {
|
||||
if (bot.SESSION.parameters.timeRange.config.finalDatetime.valueOf() > (new Date()).valueOf()) {
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date()
|
||||
}
|
||||
case 'Backtesting Session': {
|
||||
useDefaultDatetimes(aYearAgo, today)
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
|
||||
useDefaultDatetimes(today, aYearFromNow)
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
|
||||
useDefaultDatetimes(today, aYearFromNow)
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
|
||||
useDefaultDatetimes(today, aYearFromNow)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function useDefaultDatetimes(initialDefault, finalDefault) {
|
||||
/* Initial Datetime */
|
||||
if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault
|
||||
} else {
|
||||
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()
|
||||
}
|
||||
|
||||
/* Final Datetime */
|
||||
if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) {
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = finalDefault
|
||||
} else {
|
||||
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()
|
||||
}
|
||||
}
|
||||
|
||||
/* Time Frame */
|
||||
if (bot.SESSION.parameters.timeFrame === undefined) {
|
||||
let errorMessage = "Session Parameters Node with no Time Frame."
|
||||
@@ -300,18 +305,18 @@
|
||||
}
|
||||
|
||||
function startLiveTrading(message) {
|
||||
if (process.env.KEY === undefined || process.env.SECRET === undefined) { // TODO Remove this from Process .env
|
||||
if (bot.KEY === undefined || bot.SECRET === undefined) {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> Key 'codeName' or 'secret' not provided. Plese check that and try again."); }
|
||||
return false
|
||||
}
|
||||
|
||||
bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf())
|
||||
pProcessConfig.liveWaitTime = sessionParameters.timeFrame.config.value
|
||||
pProcessConfig.liveWaitTime = bot.SESSION.parameters.timeFrame.config.value
|
||||
return true
|
||||
}
|
||||
|
||||
function startFordwardTesting(message) {
|
||||
if (process.env.KEY === undefined || process.env.SECRET === undefined) {
|
||||
if (bot.KEY === undefined || bot.SECRET === undefined) {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startFordwardTesting -> Key name or Secret not provided, not possible to run the process in Forward Testing mode."); }
|
||||
console.log("Key 'codeName' or 'secret' not provided. Plese check that and try again.")
|
||||
return false
|
||||
@@ -335,14 +340,14 @@
|
||||
bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100
|
||||
bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100
|
||||
|
||||
pProcessConfig.normalWaitTime = sessionParameters.timeFrame.config.value
|
||||
pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function startPaperTrading(message) {
|
||||
bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf())
|
||||
pProcessConfig.normalWaitTime = sessionParameters.timeFrame.config.value
|
||||
pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -47,12 +47,14 @@ global.PRECISE = function (floatNumber, precision) {
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('[ERROR] Task Server -> server -> uncaughtException -> err.message = ' + err.message)
|
||||
console.log('[ERROR] Task Server -> server -> uncaughtException -> err.stack = ' + err.stack)
|
||||
console.log(err.stack)
|
||||
global.EXIT_NODE_PROCESS()
|
||||
})
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('[ERROR] Task Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason))
|
||||
console.log('[ERROR] Task Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p))
|
||||
console.log(reason.stack)
|
||||
global.EXIT_NODE_PROCESS()
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user