Market Orders Being Saved

This commit is contained in:
Luis Fernando Molina committed 2020-07-13 14:45:15 +02:00
1 parent 549ee50cd9
commit 55c1c67e26
6 files changed
+196 -146

No files matched your search

+9
View File
@@ -5,6 +5,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
*/
const MODULE_NAME = 'Trading Engine'
let thisObject = {
getNodeById: getNodeById,
cloneValues: cloneValues,
setCurrentCandle: setCurrentCandle,
updateEpisodeCountersAndStatistics: updateEpisodeCountersAndStatistics,
@@ -15,6 +16,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
let tradingEngine
let sessionParameters
let nodesMap = new Map()
return thisObject
@@ -34,6 +36,11 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
function finalize() {
tradingEngine = undefined
sessionParameters = undefined
nodesMap = undefined
}
function getNodeById(NodeId) {
return nodesMap.get(NodeId)
}
function setCurrentCandle(candle) {
@@ -90,6 +97,8 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
function initializeNode(node) {
if (node === undefined) { return }
/* Add to Node Map */
nodesMap.set(node.id, node)
/* Here we initialize the node value */
if (node.config !== undefined) {
+1 -4
View File
@@ -1,4 +1,4 @@
exports.newTradingPosition = function newTradingPosition(bot, logger) {
exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEngineModule) {
/*
This module packages all functions related to Positions.
*/
@@ -60,9 +60,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger) {
/*
Now that the position is closed, it is the right time to move this position from current to last at the Trading Engine data structure.
*/
const TRADING_ENGINE_MODULE = require('./TradingEngine.js')
let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger)
tradingEngineModule.cloneValues(tradingEngine.current.position, tradingEngine.last.position)
}
+146 -104
View File
@@ -45,85 +45,107 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
let outputDatasetArray = outputDatasetsMap.get(product.config.codeName)
if (bot.processingDailyFiles === true && dataset.config.type === 'Daily Files') {
persistRecord()
persistRecords()
}
if (bot.processingDailyFiles === false && dataset.config.type === 'Market Files') {
persistRecord()
persistRecords()
}
function persistRecord() {
pruneOutputFile(product, outputDatasetArray)
let record = scanRecordDefinition(product)
function persistRecords() {
if (product.config.saveAsObjects === true) {
/* For saving objects we need to take care of a different set of rules. */
for (let j = 0; j < product.record.properties.length; j++) {
let recordProperty = product.record.properties[j]
if (recordProperty.config.codeName === product.config.propertyNameThatDefinesObject) {
let propertyValue = record[j]
/*
We check that the existing file does not contain at the end a record that
does not signal that the object is closed.
If it does, we replace it with the new one until at some point it will signal
does the object is closed.
/* Clean the file from information of previous executions */
pruneOutputFile(product, outputDatasetArray)
/*
The product root can be a node or a node property of type array.
*/
let productRoot = eval(product.config.nodePath)
if (product.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]
let record = scanRecordDefinition(product, productRootNode, index)
persistIndividualRecord(record, product, outputDatasetArray)
}
} 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
let record = scanRecordDefinition(product, productRootNode)
persistIndividualRecord(record, product, outputDatasetArray)
}
}
}
function persistIndividualRecord(record, product, outputDatasetArray) {
if (product.config.saveAsObjects === true) {
/* For saving objects we need to take care of a different set of rules. */
for (let j = 0; j < product.record.properties.length; j++) {
let recordProperty = product.record.properties[j]
if (recordProperty.config.codeName === product.config.propertyNameThatDefinesObject) {
let propertyValue = record[j]
/* Remove Open Records */
spliceOpenRecords(j, product, outputDatasetArray)
if (bot.processingDailyFiles) {
/*
When dealing with Daily Files, we need to avoid to write an open object at the last 'candle' of the day,
since the object will be duplicated on the next day. How do we know we are positioned at the last candle
of the day? Easy: the end of the candle must be 1 millisecod before the next day. That happens at any
time frame.
*/
let lastRecord = outputDatasetArray[outputDatasetArray.length - 1]
if (lastRecord !== undefined) {
let lastRecordValue = lastRecord[j]
if (lastRecordValue !== product.config.propertyValueThatClosesObject) {
outputDatasetArray.splice(outputDatasetArray.length - 1, 1)
}
}
if (bot.processingDailyFiles) {
let currentDay = new Date(tradingEngine.current.candle.end.value)
let nextDay = new Date(tradingEngine.current.candle.end.value + 1)
if (currentDay.getUTCDate() !== nextDay.getUTCDate()) {
/*
When dealing with Daily Files, we need to avoid to write an open object at the last 'candle' of the day,
since the object will be duplicated on the next day. How do we know we are positioned at the last candle
of the day? Easy: the end of the candle must be 1 millisecod before the next day. That happens at any
time frame.
We will save the object only if it is closed, becasuse we are at the last candle of the day.
*/
let currentDay = new Date(tradingEngine.current.candle.end.value)
let nextDay = new Date(tradingEngine.current.candle.end.value + 1)
if (currentDay.getUTCDate() !== nextDay.getUTCDate()) {
/*
We will save the object only if it is closed, becasuse we are at the last candle of the day.
*/
if (propertyValue === product.config.propertyValueThatClosesObject) {
outputDatasetArray.push(record)
}
} else {
/*
When we are not at the end of the day, we will save the object normally, like in market files.
*/
if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) {
outputDatasetArray.push(record)
}
if (propertyValue === product.config.propertyValueThatClosesObject) {
outputDatasetArray.push(record)
}
}
else {
} else {
/*
For Market Files we will add a record everytime that proeprty value does not match this
When we are not at the end of the day, we will save the object normally, like in market files.
*/
if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) {
outputDatasetArray.push(record)
}
}
break
}
else {
/*
For Market Files we will add a record everytime that proeprty value does not match this
*/
if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) {
outputDatasetArray.push(record)
}
}
break
}
} else {
/* When we are not dealing with objects, we add every record to the existing file. */
outputDatasetArray.push(record)
}
} else {
/* When we are not dealing with objects, we add every record to the existing file. */
outputDatasetArray.push(record)
}
}
function scanRecordDefinition(product) {
/*
The Product Root Node is the root of the node hiriarchy branch that we need to look at
in order to fnd the node where we are going to extract the value.
*/
let productRootNode = eval(product.config.nodePath)
function scanRecordDefinition(product, productRootNode, index) {
let record = []
for (let j = 0; j < product.record.properties.length; j++) {
let recordProperty = product.record.properties[j]
@@ -201,64 +223,84 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
}
return record
}
}
function safeNumericValue(value) {
/*
The purpose of this function is to check that value variable does not have a value that
will later break the JSON format of files where this is going to be stored at.
*/
if (value === Infinity) {
value = Number.MAX_SAFE_INTEGER
function safeNumericValue(value) {
/*
The purpose of this function is to check that value variable does not have a value that
will later break the JSON format of files where this is going to be stored at.
*/
if (value === Infinity) {
value = Number.MAX_SAFE_INTEGER
}
if (value === undefined) {
value = 0
}
if (value === null) {
value = 0
}
if (isNaN(value)) {
value = 0
}
return value
}
if (value === undefined) {
value = 0
}
if (value === null) {
value = 0
}
if (isNaN(value)) {
value = 0
}
return value
}
function safeStringValue(value) {
/*
The purpose of this function is to check that value variable does not have a value that
will later break the JSON format of files where this is going to be stored at.
*/
if (value === undefined) {
value = ''
function safeStringValue(value) {
/*
The purpose of this function is to check that value variable does not have a value that
will later break the JSON format of files where this is going to be stored at.
*/
if (value === undefined) {
value = ''
}
if (value === null) {
value = ''
}
return value
}
if (value === null) {
value = ''
function pruneOutputFile(product, outputFile) {
if (outputFile.isPrunned === true) { return }
/*
When a session is resumed, we will be potentially reading output files belonging to a previous session execution.
For that reason we need to prune all the records that are beyond the current candle. We do not delete everything
because we might be resuming a stopped session, which is fine.
*/
for (let i = 0; i < outputFile.length; i++) {
let record = outputFile[i]
for (let j = 0; j < product.record.properties.length; j++) {
let recordProperty = product.record.properties[j]
if (recordProperty.config.codeName === 'end') {
let end = record[j]
if (end >= tradingEngine.current.candle.end.value) {
outputFile.splice(i, 1)
pruneOutputFile(product, outputFile)
return
}
}
}
}
outputFile.isPrunned = true
}
return value
}
function pruneOutputFile(product, outputFile) {
if (outputFile.isPrunned === true) { return }
/*
When a session is resumed, we will be potentially reading output files belonging to a previous session execution.
For that reason we need to prune all the records that are beyond the current candle. We do not delete everything
because we might be resuming a stopped session, which is fine.
*/
for (let i = 0; i < outputFile.length; i++) {
let record = outputFile[i]
function spliceOpenRecords(j, product, outputDatasetArray) {
/*
Before adding records to the output file, we need to remove all open records,
because we will be adding the same records later with potentially
new or updated information.
*/
for (let j = 0; j < product.record.properties.length; j++) {
let recordProperty = product.record.properties[j]
if (recordProperty.config.codeName === 'end') {
let end = record[j]
if (end >= tradingEngine.current.candle.end.value) {
outputFile.splice(i, 1)
pruneOutputFile(product, outputFile)
for (let i = 0; i < outputDatasetArray.length; i++) {
let dataRecord = outputDatasetArray[i]
if (dataRecord !== undefined) {
let dataRecordValue = dataRecord[j]
if (dataRecordValue !== product.config.propertyValueThatClosesObject) {
outputDatasetArray.splice(i, 1)
spliceOpenRecords(j, product, outputDatasetArray)
return
}
}
}
}
outputFile.isPrunned = true
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
tradingRecordsModule.initialize(outputDatasetsMap)
const TRADING_SYSTEM_MODULE = require('./TradingSystem.js')
let tradingSystemModule = TRADING_SYSTEM_MODULE.newTradingSystem(bot, logger)
let tradingSystemModule = TRADING_SYSTEM_MODULE.newTradingSystem(bot, logger, tradingEngineModule)
tradingSystemModule.initialize()
/* Setting up the candles array: The whole simulation is based on the array of candles at the time-frame defined at the session parameters. */
+1 -3
View File
@@ -1,4 +1,4 @@
exports.newTradingStrategy = function newTradingStrategy(bot, logger) {
exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEngineModule) {
/*
This module packages all functions related to Strategies.
*/
@@ -48,8 +48,6 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger) {
/*
Now that the strategy is closed, it is the right time to move this strategy from current to last at the Trading Engine data structure.
*/
const TRADING_ENGINE_MODULE = require('./TradingEngine.js')
let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger)
tradingEngineModule.cloneValues(tradingEngine.current.strategy, tradingEngine.last.strategy)
}
+38 -34
View File
@@ -1,4 +1,4 @@
exports.newTradingSystem = function newTradingSystem(bot, logger) {
exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineModule) {
/*
The Trading System is the user defined set of rules compliant with the Trading Protocol that
defines the trading logic to be applied during each cycle of the Simulation.
@@ -26,10 +26,10 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) {
let formulas = new Map()
const TRADING_STRATEGY_MODULE = require('./TradingStrategy.js')
let tradingStrategyModule = TRADING_STRATEGY_MODULE.newTradingStrategy(bot, logger)
let tradingStrategyModule = TRADING_STRATEGY_MODULE.newTradingStrategy(bot, logger, tradingEngineModule)
const TRADING_POSITION_MODULE = require('./TradingPosition.js')
let tradingPositionModule = TRADING_POSITION_MODULE.newTradingPosition(bot, logger)
let tradingPositionModule = TRADING_POSITION_MODULE.newTradingPosition(bot, logger, tradingEngineModule)
const ANNOUNCEMENTS_MODULE = require('./Announcements.js')
let announcementsModule = ANNOUNCEMENTS_MODULE.newAnnouncements(bot, logger)
@@ -1008,37 +1008,41 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) {
function checkOrders(orders, executionAlgorithm, executionNode) {
for (let i = 0; i < orders.length; i++) {
let order = orders[i]
let tradingSystemOrder = orders[i]
/* Basic Validations */
if (order.referenceParent === undefined) { continue }
if (order.referenceParent.identifier === undefined) { continue }
if (order.referenceParent.begin === undefined) { continue }
if (order.referenceParent.end === undefined) { continue }
if (order.referenceParent.rate === undefined) { continue }
if (order.referenceParent.size === undefined) { continue }
if (order.referenceParent.status === undefined) { continue }
if (order.referenceParent.algorithmName === undefined) { continue }
if (order.referenceParent.orderCounters === undefined) { continue }
if (order.referenceParent.orderCounters.periods === undefined) { continue }
if (order.positionSize === undefined) { continue }
if (order.positionSize.formula === undefined) { continue }
if (tradingSystemOrder.positionSize === undefined) { continue }
if (tradingSystemOrder.positionSize.formula === undefined) { continue }
if (tradingSystemOrder.referenceParent === undefined) { continue }
switch (order.referenceParent.status.value) {
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 }
switch (tradingEngineOrder.status.value) {
case 'Open': {
order.referenceParent.end.value = tradingEngine.current.candle.end.value
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
let mustCancelOrder = checkOrderEvent(order.cancelOrderEvent, order, executionAlgorithm, executionNode)
let mustCancelOrder = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (mustCancelOrder === true) {
// Cancel Order
order.referenceParent.status.value = 'Closed'
order.referenceParent.exitType.value = 'Cancelled'
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Cancelled'
}
let mustMoveOrder = checkOrderEvent(order.moveOrderEvent, order, executionAlgorithm, executionNode)
if (mustMoveOrder === true && order.referenceParent.status.value === 'Open') {
let mustMoveOrder = checkOrderEvent(tradingSystemOrder.moveOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (mustMoveOrder === true && tradingEngineOrder.status.value === 'Open') {
// Move Order
order.referenceParent.status.value = 'Open'
tradingEngineOrder.status.value = 'Open'
}
}
break
@@ -1047,18 +1051,18 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) {
}
break
default: {
order.referenceParent.status.value = 'Not Open'
let mustCreateOrder = checkOrderEvent(order.createOrderEvent, order, executionAlgorithm, executionNode)
tradingEngineOrder.status.value = 'Not Open'
let mustCreateOrder = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (mustCreateOrder === true) {
// Create Order
order.referenceParent.identifier.value = global.UNIQUE_ID()
order.referenceParent.begin.value = tradingEngine.current.candle.begin.value
order.referenceParent.end.value = tradingEngine.current.candle.end.value
order.referenceParent.rate.value = tradingEngine.current.candle.end.value
order.referenceParent.size.value = formulas.get(order.positionSize.formula.id)
order.referenceParent.status.value = 'Open'
order.referenceParent.algorithmName.value = executionAlgorithm.name
order.referenceParent.orderCounters.periods++
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
tradingEngineOrder.begin.value = tradingEngine.current.candle.begin.value
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
tradingEngineOrder.rate.value = tradingEngine.current.position.rate.value
tradingEngineOrder.size.value = formulas.get(tradingSystemOrder.positionSize.formula.id)
tradingEngineOrder.status.value = 'Open'
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
tradingEngineOrder.orderCounters.periods.value++
}
}
}