Market Orders Being Saved
This commit is contained in:
1 parent
549ee50cd9
commit
55c1c67e26
6 files changed
+196
-146
No files matched your search
@@ -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 +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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,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)
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user