A lot of bugs fixed
This commit is contained in:
1 parent
049e0d20a0
commit
593e53fea3
18 files changed
+765
-734
No files matched your search
@@ -6,9 +6,11 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
|
||||
const MODULE_NAME = 'Trading Engine'
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
getNodeById: getNodeById,
|
||||
cloneValues: cloneValues,
|
||||
setCurrentCandle: setCurrentCandle,
|
||||
setCurrentCycle: setCurrentCycle,
|
||||
initializeNode: initializeNode,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
@@ -46,6 +48,10 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
|
||||
updateDistanceToEventsCounters()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
|
||||
}
|
||||
|
||||
function getNodeById(NodeId) {
|
||||
return nodesMap.get(NodeId)
|
||||
}
|
||||
@@ -59,6 +65,32 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
|
||||
tradingEngine.current.episode.candle.max.value = candle.max
|
||||
}
|
||||
|
||||
function setCurrentCycle(cycle) {
|
||||
switch (cycle) {
|
||||
case 'First': {
|
||||
tradingEngine.current.episode.cycle.begin.value =
|
||||
tradingEngine.current.episode.candle.begin.value +
|
||||
sessionParameters.timeFrame.config.value
|
||||
tradingEngine.current.episode.cycle.end.value =
|
||||
tradingEngine.current.episode.candle.begin.value +
|
||||
sessionParameters.timeFrame.config.value +
|
||||
sessionParameters.timeFrame.config.value / 2 - 1
|
||||
break
|
||||
}
|
||||
case 'Second': {
|
||||
tradingEngine.current.episode.cycle.begin.value =
|
||||
tradingEngine.current.episode.candle.begin.value +
|
||||
sessionParameters.timeFrame.config.value +
|
||||
sessionParameters.timeFrame.config.value / 2
|
||||
tradingEngine.current.episode.cycle.end.value =
|
||||
tradingEngine.current.episode.candle.begin.value +
|
||||
sessionParameters.timeFrame.config.value +
|
||||
sessionParameters.timeFrame.config.value * 4 / 4 - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cloneValues(originNode, destinationNode) {
|
||||
if (originNode === undefined) { return }
|
||||
if (destinationNode === undefined) { return }
|
||||
|
||||
@@ -5,6 +5,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
|
||||
const MODULE_NAME = 'Trading Episode'
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
openEpisode: openEpisode,
|
||||
updateExitType: updateExitType,
|
||||
closeEpisode: closeEpisode,
|
||||
@@ -33,12 +34,15 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
|
||||
}
|
||||
|
||||
function mantain() {
|
||||
resetTradingEngineDataStructure()
|
||||
updateCounters()
|
||||
updateStatistics()
|
||||
updateEnds()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
resetTradingEngineDataStructure()
|
||||
}
|
||||
|
||||
function openEpisode() {
|
||||
/*
|
||||
This function is called each time the simulation starts. That does not mean the Episode
|
||||
@@ -132,12 +136,12 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
|
||||
|
||||
tradingEngine.current.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.ROI.value, 10)
|
||||
|
||||
/* Updating Anualized Rate Of Return */
|
||||
tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value =
|
||||
/* Updating Annualized Rate Of Return */
|
||||
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value =
|
||||
tradingEngine.current.episode.episodeStatistics.ROI.value /
|
||||
tradingEngine.current.episode.episodeStatistics.days.value * 365
|
||||
|
||||
tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.anualizedRateOfReturn.value, 10)
|
||||
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value, 10)
|
||||
|
||||
/* Updating Hit or Fail */
|
||||
if (tradingEngine.current.episode.episodeStatistics.profitLoss.value > 0) {
|
||||
@@ -197,17 +201,17 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
|
||||
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.hitRatio.value, 10)
|
||||
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value, 10)
|
||||
|
||||
/* Updating Anualized Rate Of Return */
|
||||
tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value =
|
||||
/* Updating Annualized Rate Of Return */
|
||||
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value =
|
||||
tradingEngine.current.episode.episodeBaseAsset.ROI.value /
|
||||
tradingEngine.current.episode.episodeStatistics.days.value * 365
|
||||
|
||||
tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value =
|
||||
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value =
|
||||
tradingEngine.current.episode.episodeQuotedAsset.ROI.value /
|
||||
tradingEngine.current.episode.episodeStatistics.days.value * 365
|
||||
|
||||
tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.anualizedRateOfReturn.value, 10)
|
||||
tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.anualizedRateOfReturn.value, 10)
|
||||
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value, 10)
|
||||
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value, 10)
|
||||
|
||||
/* Updating Hit or Fail */
|
||||
if (tradingEngine.current.episode.episodeBaseAsset.profitLoss.value > 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
|
||||
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
runExecution: runExecution,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
@@ -41,6 +42,10 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
|
||||
tradingOrdersModule.mantain()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
tradingOrdersModule.reset()
|
||||
}
|
||||
|
||||
async function runExecution(
|
||||
executionNode,
|
||||
tradingEngineStage
|
||||
|
||||
@@ -6,6 +6,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
checkOrders: checkOrders,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
@@ -45,6 +46,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
function mantain() {
|
||||
mantainReset(true, false)
|
||||
}
|
||||
|
||||
function reset() {
|
||||
mantainReset(false, true)
|
||||
}
|
||||
|
||||
function mantainReset(mantain, reset) {
|
||||
|
||||
if (tradingEngine.current.strategy.index.value === tradingEngine.current.strategy.index.config.initialValue) { return }
|
||||
|
||||
@@ -53,42 +62,46 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
executionNode = stageNode.openExecution
|
||||
mantainExecutionNode(executionNode, tradingEngine.current.strategyOpenStage.status.value)
|
||||
processExecutionNode(executionNode, tradingEngine.current.strategyOpenStage.status.value)
|
||||
|
||||
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
|
||||
executionNode = stageNode.closeExecution
|
||||
mantainExecutionNode(executionNode, tradingEngine.current.strategyCloseStage.status.value)
|
||||
processExecutionNode(executionNode, tradingEngine.current.strategyCloseStage.status.value)
|
||||
|
||||
function mantainExecutionNode(executionNode, stageStatus) {
|
||||
function processExecutionNode(executionNode, stageStatus) {
|
||||
|
||||
mantainExecutionAlgorithms(executionNode)
|
||||
preocessExecutionAlgorithms(executionNode)
|
||||
|
||||
function mantainExecutionAlgorithms(executionNode) {
|
||||
function preocessExecutionAlgorithms(executionNode) {
|
||||
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
|
||||
let executionAlgorithm = executionNode.executionAlgorithms[i]
|
||||
mantainOrders(executionAlgorithm.marketBuyOrders)
|
||||
mantainOrders(executionAlgorithm.marketSellOrders)
|
||||
mantainOrders(executionAlgorithm.limitBuyOrders)
|
||||
mantainOrders(executionAlgorithm.limitSellOrders)
|
||||
processOrders(executionAlgorithm.marketBuyOrders)
|
||||
processOrders(executionAlgorithm.marketSellOrders)
|
||||
processOrders(executionAlgorithm.limitBuyOrders)
|
||||
processOrders(executionAlgorithm.limitSellOrders)
|
||||
}
|
||||
}
|
||||
|
||||
function mantainOrders(orders) {
|
||||
function processOrders(orders) {
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
let tradingSystemOrder = orders[i]
|
||||
if (tradingSystemOrder.referenceParent === undefined) { continue }
|
||||
if (tradingSystemOrder.referenceParent === undefined) {
|
||||
badDefinitionUnhandledException(undefined, 'tradingSystemOrder.referenceParent === undefined', tradingSystemOrder)
|
||||
}
|
||||
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
|
||||
try {
|
||||
if (tradingEngineOrder.status === undefined) { continue }
|
||||
} catch (err) {
|
||||
console.log(err.stack)
|
||||
|
||||
if (tradingEngineOrder.status === undefined) {
|
||||
badDefinitionUnhandledException(undefined, 'tradingEngineOrder.status === undefined', tradingEngineOrder)
|
||||
}
|
||||
|
||||
|
||||
updateCounters(tradingEngineOrder)
|
||||
updateEnds(tradingEngineOrder)
|
||||
updateStatistics(tradingEngineOrder)
|
||||
resetTradingEngineDataStructure(tradingEngineOrder, tradingSystemOrder, stageStatus)
|
||||
if (mantain === true) {
|
||||
updateCounters(tradingEngineOrder)
|
||||
updateEnds(tradingEngineOrder)
|
||||
updateStatistics(tradingEngineOrder)
|
||||
}
|
||||
if (reset === true) {
|
||||
resetTradingEngineDataStructure(tradingEngineOrder, tradingSystemOrder, stageStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +164,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
/* Cancel the order at the Exchange, if needed. */
|
||||
await exchangeCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
|
||||
|
||||
updateEndsWithCycle(tradingEngineOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,8 +231,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
/* Create Order Procedure */
|
||||
tradingEngineOrder.status.value = 'Open'
|
||||
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngineOrder.begin.value = tradingEngine.current.episode.candle.begin.value
|
||||
tradingEngineOrder.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngineOrder.begin.value = tradingEngine.current.episode.cycle.begin.value
|
||||
tradingEngineOrder.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
tradingEngineOrder.serialNumber.value = tradingEngine.current.episode.episodeCounters.orders.value
|
||||
tradingEngineOrder.orderName.value = tradingSystemOrder.name
|
||||
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
|
||||
@@ -253,24 +268,42 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) }
|
||||
if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) }
|
||||
|
||||
/* Size in Base Asset */
|
||||
tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
|
||||
/*
|
||||
The Size calculation depends on how the user defined the size of the position.
|
||||
The user could have defined the size of the position in Base Asset or Quoted Asset.
|
||||
*/
|
||||
switch (tradingEngineStage.stageDefinedIn.value) {
|
||||
case 'Base Asset': {
|
||||
/* Size in Base Asset */
|
||||
tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
|
||||
|
||||
/* Size in Quoted Asset */
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
|
||||
/* Check that the Size calculated would not surpass Stage Target Size */
|
||||
if (
|
||||
tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value >
|
||||
tradingEngineStage.stageBaseAsset.targetSize.value
|
||||
) {
|
||||
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value
|
||||
}
|
||||
|
||||
/* Check that the Size calculated would not surpass Stage Target Size */
|
||||
if (
|
||||
tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value >
|
||||
tradingEngineStage.stageBaseAsset.targetSize.value
|
||||
) {
|
||||
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value
|
||||
}
|
||||
if (
|
||||
tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value >
|
||||
tradingEngineStage.stageQuotedAsset.targetSize.value
|
||||
) {
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value
|
||||
/* Size in Quoted Asset */
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value
|
||||
break
|
||||
}
|
||||
case 'Quoted Asset': {
|
||||
/* Size in Quoted Asset */
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100
|
||||
|
||||
if (
|
||||
tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value >
|
||||
tradingEngineStage.stageQuotedAsset.targetSize.value
|
||||
) {
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value
|
||||
}
|
||||
|
||||
/* Size in Base Asset */
|
||||
tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.size.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.size.value, 10)
|
||||
@@ -333,8 +366,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value
|
||||
|
||||
actualRateSimulation()
|
||||
feesPaidSimulation()
|
||||
percentageFilledSimulation()
|
||||
feesPaidSimulation()
|
||||
sizeFilledSimulation()
|
||||
|
||||
doTheAccounting(
|
||||
@@ -397,67 +430,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
|
||||
}
|
||||
|
||||
function feesPaidSimulation() {
|
||||
/* Fees Paid Simulation */
|
||||
let calculatedBasedOnTradingSystem = false
|
||||
|
||||
/* Based on the Trading System Definition */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
|
||||
if (tradingEngineOrder.orderBaseAsset.feesPaid.value === tradingEngineOrder.orderBaseAsset.feesPaid.config.initialValue) {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
|
||||
|
||||
calculatedBasedOnTradingSystem = true
|
||||
}
|
||||
|
||||
if (tradingEngineOrder.orderQuotedAsset.feesPaid.value === tradingEngineOrder.orderQuotedAsset.feesPaid.config.initialValue) {
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100
|
||||
|
||||
calculatedBasedOnTradingSystem = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on the Session Parameters Definition */
|
||||
if (calculatedBasedOnTradingSystem === false) {
|
||||
/* Fees are simulated based on the Session Paremeters */
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.taker / 100
|
||||
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.maker / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.maker / 100
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
|
||||
}
|
||||
|
||||
function percentageFilledSimulation() {
|
||||
/*
|
||||
Order Filling Simulation: the way we are going to handle this is the following.
|
||||
@@ -481,6 +453,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Market Buy Order': {
|
||||
orderWasHit = true
|
||||
break
|
||||
}
|
||||
case 'Market Sell Order': {
|
||||
orderWasHit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (orderWasHit === true) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
|
||||
@@ -507,10 +487,86 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
updateEndsWithCycle(tradingEngineOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function feesPaidSimulation() {
|
||||
/*
|
||||
The way the fess paid is calculated is as follows: From the order size we apply the % of fee,
|
||||
that would give us the total fees for that order. To that we apply the % filled of the orders,
|
||||
that give use the final result of the fees paid for the current % filled.
|
||||
|
||||
But before that, we will see if there is an specific definition on how to simulate the fees for
|
||||
this order, or if there is not, we use the fees session parameters definitions.
|
||||
*/
|
||||
let calculatedBasedOnTradingSystem = false
|
||||
|
||||
/* Based on the Trading System Definition */
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
|
||||
if (tradingEngineOrder.orderBaseAsset.feesPaid.value === tradingEngineOrder.orderBaseAsset.feesPaid.config.initialValue) {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
calculatedBasedOnTradingSystem = true
|
||||
}
|
||||
|
||||
if (tradingEngineOrder.orderQuotedAsset.feesPaid.value === tradingEngineOrder.orderQuotedAsset.feesPaid.config.initialValue) {
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
calculatedBasedOnTradingSystem = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on the Session Parameters Definition */
|
||||
if (calculatedBasedOnTradingSystem === false) {
|
||||
/* Fees are simulated based on the Session Paremeters */
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.taker / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.taker / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.maker / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
bot.SESSION.parameters.feeStructure.config.maker / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
|
||||
}
|
||||
|
||||
function sizeFilledSimulation() {
|
||||
/* Size Filled */
|
||||
tradingEngineOrder.orderBaseAsset.sizeFilled.value =
|
||||
@@ -563,6 +619,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
updateEndsWithCycle(tradingEngineOrder)
|
||||
}
|
||||
if (order.remaining > 0 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
|
||||
|
||||
@@ -572,6 +630,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
updateEndsWithCycle(tradingEngineOrder)
|
||||
}
|
||||
if (order.status === AT_EXCHANGE_STATUS.CANCELLED) {
|
||||
|
||||
@@ -581,6 +641,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
/* Initialize this */
|
||||
tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1
|
||||
|
||||
updateEndsWithCycle(tradingEngineOrder)
|
||||
}
|
||||
|
||||
syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
|
||||
@@ -891,6 +953,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
function updateEndsWithCycle(tradingEngineOrder) {
|
||||
tradingEngineOrder.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
}
|
||||
|
||||
function updateCounters(tradingEngineOrder) {
|
||||
if (tradingEngineOrder.status.value === 'Open') {
|
||||
tradingEngineOrder.orderCounters.periods.value++
|
||||
|
||||
@@ -5,6 +5,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
const MODULE_NAME = 'Trading Position'
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
openPosition: openPosition,
|
||||
closingPosition: closingPosition,
|
||||
closePosition: closePosition,
|
||||
@@ -39,34 +40,39 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
}
|
||||
|
||||
function mantain() {
|
||||
resetTradingEngineDataStructure()
|
||||
updateCounters()
|
||||
updateEnds()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
resetTradingEngineDataStructure()
|
||||
}
|
||||
|
||||
function openPosition(situationName) {
|
||||
|
||||
/* Recording the opening at the Trading Engine Data Structure */
|
||||
tradingEngine.current.position.status.value = 'Open'
|
||||
tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value
|
||||
tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value + 1
|
||||
tradingEngine.current.position.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.begin.value
|
||||
tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.position.beginRate.value = tradingEngine.current.episode.candle.close.value
|
||||
tradingEngine.current.position.positionBaseAsset.beginBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value
|
||||
tradingEngine.current.position.positionQuotedAsset.beginBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value
|
||||
tradingEngine.current.position.situationName.value = situationName
|
||||
|
||||
/* Initializing Stop and Take Profit Stage / Phase */
|
||||
tradingEngine.current.position.stopLoss.stopLossStage.value = 'Open Stage'
|
||||
tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Open Stage'
|
||||
tradingEngine.current.position.stopLoss.stopLossPhase.value = 0
|
||||
tradingEngine.current.position.takeProfit.takeProfitPhase.value = 0
|
||||
/* Initializing Stop and Take Phase */
|
||||
tradingEngine.current.position.stopLoss.stopLossPhase.value = 1
|
||||
tradingEngine.current.position.takeProfit.takeProfitPhase.value = 1
|
||||
|
||||
/* Updating Episode Counters */
|
||||
tradingEngine.current.episode.episodeCounters.positions.value++
|
||||
|
||||
/* Inicializing this counter */
|
||||
tradingEngine.current.episode.distanceToEvent.takePosition.value = 1
|
||||
|
||||
/* Remember the balance we had before taking the position to later calculate profit or loss */
|
||||
tradingEngine.current.position.positionBaseAsset.beginBalance = tradingEngine.current.episode.episodeBaseAsset.balance.value
|
||||
tradingEngine.current.position.positionQuotedAsset.beginBalance = tradingEngine.current.episode.episodeQuotedAsset.balance.value
|
||||
}
|
||||
|
||||
function closingPosition(exitType) {
|
||||
@@ -98,47 +104,45 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
tradingEngine.current.position.takeProfit.value = formulas.get(formulaId)
|
||||
}
|
||||
|
||||
function updateStopLoss(phase, stage) {
|
||||
function updateStopLoss(phase) {
|
||||
tradingEngine.current.position.stopLoss.stopLossPhase.value = phase
|
||||
tradingEngine.current.position.stopLoss.stopLossStage.value = stage
|
||||
}
|
||||
|
||||
function updateTakeProfit(phase, stage) {
|
||||
function updateTakeProfit(phase) {
|
||||
tradingEngine.current.position.takeProfit.takeProfitPhase.value = phase
|
||||
tradingEngine.current.position.takeProfit.takeProfitStage.value = stage
|
||||
}
|
||||
|
||||
function initialTargets(stageNode) {
|
||||
function initialTargets(tradingSystemStageNode, tradingEngineStageNode) {
|
||||
|
||||
if (stageNode.initialTargets === undefined) {
|
||||
if (tradingSystemStageNode.initialTargets === undefined) {
|
||||
const message = 'Stage without Initial Targets node. Add one please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode)
|
||||
}
|
||||
|
||||
setTargetRate()
|
||||
setTargetSize()
|
||||
|
||||
function setTargetRate() {
|
||||
if (stageNode.initialTargets.targetRate === undefined) {
|
||||
if (tradingSystemStageNode.initialTargets.targetRate === undefined) {
|
||||
const message = 'Target Rate Node not Found. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets)
|
||||
}
|
||||
if (stageNode.initialTargets.targetRate.formula === undefined) {
|
||||
if (tradingSystemStageNode.initialTargets.targetRate.formula === undefined) {
|
||||
const message = 'Formula of Target Rate Node not Found. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate)
|
||||
}
|
||||
|
||||
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetRate.formula.id)
|
||||
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetRate.formula.id)
|
||||
if (value === undefined) {
|
||||
const message = 'Target Rate can not be undefined. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate.formula)
|
||||
}
|
||||
if (isNaN(value)) {
|
||||
const message = 'Target Rate must be a number. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetRate.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate.formula)
|
||||
}
|
||||
|
||||
switch (stageNode.type) {
|
||||
switch (tradingSystemStageNode.type) {
|
||||
case 'Open Stage': {
|
||||
tradingEngine.current.position.entryTargetRate.value = global.PRECISE(value, 10)
|
||||
break
|
||||
@@ -153,27 +157,27 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
function setTargetSize() {
|
||||
/* Basic Validation */
|
||||
if (
|
||||
stageNode.initialTargets.targetSizeInBaseAsset !== undefined &&
|
||||
stageNode.initialTargets.targetSizeInQuotedAsset !== undefined
|
||||
tradingSystemStageNode.initialTargets.targetSizeInBaseAsset !== undefined &&
|
||||
tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset !== undefined
|
||||
) {
|
||||
const message = 'Only Target Size In Base Asset or Target Size In Quoted Asset is allowed. Remove one of them please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode)
|
||||
}
|
||||
|
||||
/* Position In Base Asset */
|
||||
if (stageNode.initialTargets.targetSizeInBaseAsset !== undefined) {
|
||||
if (stageNode.initialTargets.targetSizeInBaseAsset.formula !== undefined) {
|
||||
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetSizeInBaseAsset.formula.id)
|
||||
if (tradingSystemStageNode.initialTargets.targetSizeInBaseAsset !== undefined) {
|
||||
if (tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula !== undefined) {
|
||||
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula.id)
|
||||
if (value === undefined) {
|
||||
const message = 'Target Size In Base Asset cannot be undefined. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
|
||||
}
|
||||
if (value === undefined) {
|
||||
const message = 'Target Size In Base Asset cannot be zero. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
|
||||
}
|
||||
|
||||
switch (stageNode.type) {
|
||||
switch (tradingSystemStageNode.type) {
|
||||
case 'Open Stage': {
|
||||
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value = global.PRECISE(value, 10)
|
||||
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value =
|
||||
@@ -187,25 +191,28 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember how the end user defined this stage. */
|
||||
tradingEngineStageNode.stageDefinedIn.value = 'Base Asset'
|
||||
} else {
|
||||
const message = 'You need to specify a Formula for this.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInBaseAsset)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset)
|
||||
}
|
||||
}
|
||||
|
||||
/* Position In Quoted Asset */
|
||||
if (stageNode.initialTargets.targetSizeInQuotedAsset !== undefined) {
|
||||
if (stageNode.initialTargets.targetSizeInQuotedAsset.formula !== undefined) {
|
||||
let value = tradingSystem.formulas.get(stageNode.initialTargets.targetSizeInQuotedAsset.formula.id)
|
||||
if (tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset !== undefined) {
|
||||
if (tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula !== undefined) {
|
||||
let value = tradingSystem.formulas.get(tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula.id)
|
||||
if (value === undefined) {
|
||||
const message = 'Target Size In Quoted Asset cannot be undefined. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
}
|
||||
if (value === undefined) {
|
||||
const message = 'Target Size In Quoted Asset cannot be zero. Fix this please.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
}
|
||||
switch (stageNode.type) {
|
||||
switch (tradingSystemStageNode.type) {
|
||||
case 'Open Stage': {
|
||||
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value = global.PRECISE(value, 10)
|
||||
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value =
|
||||
@@ -219,9 +226,12 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember how the end user defined this stage. */
|
||||
tradingEngineStageNode.stageDefinedIn.value = 'Quoted Asset'
|
||||
} else {
|
||||
const errorText = 'You need to specify a Formula for this.'
|
||||
badDefinitionUnhandledException(undefined, message, stageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
outputDatasetsMap = undefined
|
||||
}
|
||||
|
||||
function appendRecords() {
|
||||
function appendRecords(cycle) {
|
||||
/*
|
||||
Here we add records to the output files. At the product config property nodePath
|
||||
we have a pointer to the node that have the information we need to extract.
|
||||
@@ -53,6 +53,14 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
}
|
||||
|
||||
function persistRecords() {
|
||||
/*
|
||||
There are a few products configured to be saved only at an specific cycle.
|
||||
*/
|
||||
if (product.config.saveAtCycle !== undefined) {
|
||||
if (cycle !== product.config.saveAtCycle) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean the file from information of previous executions */
|
||||
pruneOutputFile(product, outputDatasetArray)
|
||||
@@ -329,7 +337,12 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
let end = record[j]
|
||||
if (end >= tradingEngine.current.episode.candle.end.value) {
|
||||
outputFile.splice(i, 1)
|
||||
pruneOutputFile(product, outputFile)
|
||||
/*
|
||||
This will execute the next prune call in the next iteration of the NodeJs event loop
|
||||
allowing for other callbacks to be executed. It also prevents the error
|
||||
'Maximum call stack size exceeded', since the call is not placed at the call stack.
|
||||
*/
|
||||
setImmediate(pruneOutputFile, product, outputFile)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,16 +137,43 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
/* The chart was recalculated based on the current candle. */
|
||||
tradingSystemModule.updateChart(chart)
|
||||
|
||||
/* Do the stuff needed previous to the run */
|
||||
/*
|
||||
Do the stuff needed previous to the run like
|
||||
Episode Counters and Statistics update
|
||||
*/
|
||||
tradingSystemModule.mantain()
|
||||
|
||||
/* Episode Counters and Statistics update */
|
||||
tradingEpisodeModule.mantain()
|
||||
|
||||
/* Run your maintaince procedure */
|
||||
tradingEngineModule.mantain()
|
||||
|
||||
/* Run one cycle of the Trading System*/
|
||||
/* Reset Data Structures */
|
||||
tradingSystemModule.reset()
|
||||
tradingEpisodeModule.reset()
|
||||
tradingEngineModule.reset()
|
||||
|
||||
/*
|
||||
Run the first cycle of the Trading System. In this first cycle we
|
||||
give some room so that orders can be canceled or filled and we can
|
||||
write those records into the output memory.
|
||||
*/
|
||||
tradingEngineModule.setCurrentCycle('First')
|
||||
await tradingSystemModule.run()
|
||||
|
||||
/* Add new records to the process output */
|
||||
tradingRecordsModule.appendRecords('First')
|
||||
|
||||
/* Reset Data Structures */
|
||||
tradingSystemModule.reset()
|
||||
tradingEpisodeModule.reset()
|
||||
tradingEngineModule.reset()
|
||||
|
||||
/*
|
||||
Run the second cycle of the Trading System. During this second run
|
||||
some new orders might be created at slots freed up during the first
|
||||
run. This allows for example for an Limit Order to be cancelled during the
|
||||
first run, and the same Limit Order definition to spawn a new order
|
||||
without the need to wait until the next candle.
|
||||
*/
|
||||
tradingEngineModule.setCurrentCycle('Second')
|
||||
await tradingSystemModule.run()
|
||||
|
||||
controlLoop()
|
||||
@@ -171,11 +198,16 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
if (tradingEngine.current.episode.candle.index.value + 1 < candles.length) {
|
||||
|
||||
/* Add new records to the process output */
|
||||
tradingRecordsModule.appendRecords()
|
||||
tradingRecordsModule.appendRecords('Second')
|
||||
|
||||
tradingEngine.current.episode.candle.index.value++
|
||||
|
||||
setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed.
|
||||
/*
|
||||
This will execute the next loop in the next iteration of the NodeJs event loop
|
||||
allowing for other callbacks to be executed. It also prevents the error
|
||||
'Maximum call stack size exceeded', since the call is not placed at the call stack.
|
||||
*/
|
||||
setImmediate(loop)
|
||||
} else {
|
||||
updateEpisode('All Candles Processed')
|
||||
}
|
||||
@@ -186,7 +218,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingEpisodeModule.closeEpisode()
|
||||
|
||||
/* Add new records to the process output */
|
||||
tradingRecordsModule.appendRecords()
|
||||
tradingRecordsModule.appendRecords('Second')
|
||||
|
||||
afterLoop()
|
||||
}
|
||||
@@ -195,7 +227,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingEpisodeModule.updateExitType(exitType)
|
||||
|
||||
/* Add new records to the process output */
|
||||
tradingRecordsModule.appendRecords()
|
||||
tradingRecordsModule.appendRecords('Second')
|
||||
|
||||
afterLoop()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
let thisObject = {
|
||||
updateChart: updateChart,
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
runTriggerStage: runTriggerStage,
|
||||
runOpenStage: runOpenStage,
|
||||
runManageStage: runManageStage,
|
||||
@@ -88,11 +89,18 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingStrategyModule.mantain()
|
||||
tradingExecutionModule.mantain()
|
||||
|
||||
resetTradingEngineDataStructure()
|
||||
updateCounters()
|
||||
updateEnds()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
tradingPositionModule.reset()
|
||||
tradingStrategyModule.reset()
|
||||
tradingExecutionModule.reset()
|
||||
|
||||
resetTradingEngineDataStructure()
|
||||
}
|
||||
|
||||
function runTriggerStage() {
|
||||
/*
|
||||
We check if we will be triggering on, off or taking position.
|
||||
@@ -251,149 +259,119 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
async function runOpenStage() {
|
||||
/* Abort Open Stage Check */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Open' && tradingEngine.current.strategyCloseStage.status.value === 'Open') {
|
||||
/*
|
||||
if the Close stage is opened while the open stage is still open that means that
|
||||
we need to stop placing orders, check what happened to the orders already placed,
|
||||
and cancel all unfilled limit orders. We call this the Closing status of the Open Stage.
|
||||
*/
|
||||
changeStageStatus('Open Stage', 'Closing')
|
||||
}
|
||||
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Opening') {
|
||||
checkIfWeNeedToAbortTheStage()
|
||||
runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpen()
|
||||
await runWhenStatusIsClosing()
|
||||
|
||||
/* This procedure is intended to run only once */
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let executionNode = stageNode.openExecution
|
||||
|
||||
/* Reset the Exchange Orders data structure to its initial value */
|
||||
tradingEngineModule.initializeNode(tradingEngine.exchangeOrders)
|
||||
|
||||
tradingSystem.evalConditions(stageNode, 'Initial Definition')
|
||||
tradingSystem.evalFormulas(stageNode, 'Initial Definition')
|
||||
|
||||
/* Remember the balance we had before taking the position to later calculate profit or loss */
|
||||
tradingEngine.current.position.positionBaseAsset.beginBalance = tradingEngine.current.episode.episodeBaseAsset.balance.value
|
||||
tradingEngine.current.position.positionQuotedAsset.beginBalance = tradingEngine.current.episode.episodeQuotedAsset.balance.value
|
||||
|
||||
/* Entry Position size and rate */
|
||||
tradingSystem.evalFormulas(stageNode, 'Initial Targets')
|
||||
tradingPositionModule.initialTargets(stageNode)
|
||||
initializeStageTargetSize()
|
||||
|
||||
/* Check Execution at open stage node */
|
||||
tradingSystem.evalConditions(stageNode, 'Open Execution')
|
||||
tradingSystem.evalFormulas(stageNode, 'Open Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
|
||||
/* From here on, the state is officialy Open */
|
||||
changeStageStatus('Open Stage', 'Open')
|
||||
return
|
||||
}
|
||||
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
|
||||
/*
|
||||
While the Open Stage is Open, we do our regular stuff: place orders and check
|
||||
what happened to the orders already placed.
|
||||
*/
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let executionNode = stageNode.openExecution
|
||||
|
||||
/* Every Loop Cycle Activity */
|
||||
tradingSystem.evalConditions(stageNode, 'Open Execution')
|
||||
tradingSystem.evalFormulas(stageNode, 'Open Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
/*
|
||||
The Open is finished when the placedSize reaches the targetSize in any of the
|
||||
stage assets, and the fillSize + feesPaid reaches the placedSize also in any
|
||||
of the stage Assets.
|
||||
This can happens at any time when we update the sizeFilled and the feesPaid values
|
||||
when we see at the exchange that orders were filled.
|
||||
*/
|
||||
if (
|
||||
(
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced >=
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.targetSize
|
||||
|
||||
||
|
||||
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced >=
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.targetSize
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
|
||||
|
||||
||
|
||||
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
|
||||
)
|
||||
) {
|
||||
changeStageStatus('Open Stage', 'Closed', 'Position Size Filled')
|
||||
} else {
|
||||
/* Check the Close Stage Event */
|
||||
tradingSystem.evalConditions(stageNode, 'Close Stage Event')
|
||||
if (checkStopStageEvent(stageNode) === true) {
|
||||
changeStageStatus('Open Stage', 'Closing', 'Close Stage Event')
|
||||
}
|
||||
function checkIfWeNeedToAbortTheStage() {
|
||||
/* Abort Open Stage Check */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Open' && tradingEngine.current.strategyCloseStage.status.value === 'Open') {
|
||||
/*
|
||||
if the Close stage is opened while the open stage is still open that means that
|
||||
we need to stop placing orders, check what happened to the orders already placed,
|
||||
and cancel all unfilled limit orders. We call this the Closing status of the Open Stage.
|
||||
*/
|
||||
changeStageStatus('Open Stage', 'Closing')
|
||||
}
|
||||
}
|
||||
|
||||
/* Closing Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Closing') {
|
||||
/*
|
||||
During the closing stage status, we do not place new orders, just check
|
||||
if the ones placed were filled, and cancel the ones not filled.
|
||||
*/
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let executionNode = stageNode.openExecution
|
||||
function runWhenStatusIsOpening() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Opening') {
|
||||
|
||||
/*
|
||||
Check if there are unfilled orders, we will check if they were executed,
|
||||
and cancel the ones that were not.
|
||||
*/
|
||||
tradingSystem.evalConditions(stageNode, 'Open Execution')
|
||||
tradingSystem.evalFormulas(stageNode, 'Open Execution')
|
||||
/* This procedure is intended to run only once */
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let tradingEngineStage = tradingEngine.current.strategyOpenStage
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
/* Reset the Exchange Orders data structure to its initial value */
|
||||
tradingEngineModule.initializeNode(tradingEngine.exchangeOrders)
|
||||
|
||||
/*
|
||||
The Closing is finished when the fillSize + feesPaid reaches the ordersSize.
|
||||
This can happens either because we update the sizeFilled value when we see
|
||||
at the exchange that orders were filled, or we reduce the ordersSize when
|
||||
we cancel not yet filled orders. Here it does not matter the targetSize since
|
||||
this status represent a force closure of this stage.
|
||||
*/
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
|
||||
/* Entry Position size and rate */
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
tradingPositionModule.initialTargets(tradingSystemStage, tradingEngineStage)
|
||||
initializeStageTargetSize()
|
||||
|
||||
||
|
||||
/* From here on, the stage is officialy Open */
|
||||
changeStageStatus('Open Stage', 'Open')
|
||||
}
|
||||
}
|
||||
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
|
||||
) {
|
||||
changeStageStatus('Open Stage', 'Closed')
|
||||
async function runWhenStatusIsOpen() {
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
|
||||
/*
|
||||
While the Open Stage is Open, we do our regular stuff: place orders and check
|
||||
what happened to the orders already placed.
|
||||
*/
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let tradingEngineStage = tradingEngine.current.strategyOpenStage
|
||||
let executionNode = tradingSystemStage.openExecution
|
||||
|
||||
/* Evaluate conditions and formulas so they are ready during the execution run */
|
||||
tradingSystem.evalConditions(executionNode, 'Open Execution')
|
||||
tradingSystem.evalFormulas(executionNode, 'Open Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngineStage
|
||||
)
|
||||
checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Open Stage')
|
||||
}
|
||||
}
|
||||
|
||||
async function runWhenStatusIsClosing() {
|
||||
/* Closing Status Procedure */
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Closing') {
|
||||
/*
|
||||
During the closing stage status, we do not place new orders, just check
|
||||
if the ones placed were filled, and cancel the ones not filled.
|
||||
*/
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
let executionNode = tradingSystemStage.openExecution
|
||||
|
||||
/*
|
||||
Check if there are unfilled orders, we will check if they were executed,
|
||||
and cancel the ones that were not.
|
||||
*/
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Open Execution')
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Open Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
|
||||
/*
|
||||
The Closing is finished when the sizeFilled + feesPaid reaches the sizePlaced.
|
||||
This can happens either because we update the sizeFilled value when we see
|
||||
at the exchange that orders were filled, or we reduce the sizePlaced when
|
||||
we cancel not yet filled orders. Here it does not matter the targetSize since
|
||||
this status represent a force closure of this stage.
|
||||
*/
|
||||
switch (tradingEngine.current.strategyOpenStage.stageDefinedIn.value) {
|
||||
case 'Base Asset': {
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
|
||||
) {
|
||||
changeStageStatus('Open Stage', 'Closed')
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Quoted Asset': {
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
|
||||
) {
|
||||
changeStageStatus('Open Stage', 'Closed')
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,57 +388,50 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
function runManageStage() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyManageStage.status.value === 'Opening') {
|
||||
/* We jump the management at the loop cycle where the position is being taken. */
|
||||
changeStageStatus('Manage Stage', 'Open')
|
||||
return
|
||||
|
||||
runWhenStatusIsOpening()
|
||||
runWhenStatusIsOpen()
|
||||
|
||||
function runWhenStatusIsOpening() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyManageStage.status.value === 'Opening') {
|
||||
|
||||
/* Now we switch to the Open status. */
|
||||
changeStageStatus('Manage Stage', 'Open')
|
||||
}
|
||||
}
|
||||
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let manageStage = strategy.manageStage
|
||||
tradingSystem.evalConditions(manageStage, 'Manage Stage')
|
||||
tradingSystem.evalFormulas(manageStage, 'Manage Stage')
|
||||
function runWhenStatusIsOpen() {
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let manageStage = strategy.manageStage
|
||||
|
||||
if (
|
||||
tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' ||
|
||||
tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage') {
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
|
||||
tradingSystem.evalConditions(stageNode, 'Initial Definition')
|
||||
tradingSystem.evalFormulas(stageNode, 'Initial Definition')
|
||||
/* Evaluate all the stage conditions and formulas to have them ready */
|
||||
tradingSystem.evalConditions(manageStage, 'Manage Stage')
|
||||
tradingSystem.evalFormulas(manageStage, 'Manage Stage')
|
||||
|
||||
/* Stop Loss Management */
|
||||
checkStopPhasesEvents()
|
||||
calculateStopLoss()
|
||||
calculateStopLossPosition()
|
||||
|
||||
/* Take Profit Management */
|
||||
checkTakeProfitPhaseEvents()
|
||||
calculateTakeProfit()
|
||||
calculateTakeProfitPosition()
|
||||
|
||||
/* Checking if Stop or Take Profit were hit */
|
||||
checkStopLossOrTakeProfitWasHit()
|
||||
}
|
||||
|
||||
/* Stop Loss Management */
|
||||
checkStopPhasesEvents()
|
||||
calculateStopLoss()
|
||||
calculateStopLossPosition()
|
||||
|
||||
/* Take Profit Management */
|
||||
checkTakeProfitPhaseEvents()
|
||||
calculateTakeProfit()
|
||||
calculateTakeProfitPosition()
|
||||
|
||||
/* Checking if Stop or Take Profit were hit */
|
||||
checkStopLossOrTakeProfitWasHit()
|
||||
}
|
||||
|
||||
function calculateStopLoss() {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let openStage = strategy.openStage
|
||||
let manageStage = strategy.manageStage
|
||||
let phase
|
||||
|
||||
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' && openStage !== undefined) {
|
||||
if (openStage.initialDefinition !== undefined) {
|
||||
if (openStage.initialDefinition.initialStopLoss !== undefined) {
|
||||
phase = openStage.initialDefinition.initialStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Manage Stage' && manageStage !== undefined) {
|
||||
if (manageStage !== undefined) {
|
||||
if (manageStage.managedStopLoss !== undefined) {
|
||||
phase = manageStage.managedStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value - 1]
|
||||
}
|
||||
@@ -480,19 +451,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
|
||||
function calculateTakeProfit() {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let openStage = strategy.openStage
|
||||
let manageStage = strategy.manageStage
|
||||
let phase
|
||||
|
||||
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage' && openStage !== undefined) {
|
||||
if (openStage.initialDefinition !== undefined) {
|
||||
if (openStage.initialDefinition.initialTakeProfit !== undefined) {
|
||||
phase = openStage.initialDefinition.initialTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Manage Stage' && manageStage !== undefined) {
|
||||
if (manageStage !== undefined) {
|
||||
if (manageStage.managedTakeProfit !== undefined) {
|
||||
phase = manageStage.managedTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1]
|
||||
}
|
||||
@@ -513,7 +475,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
function calculateStopLossPosition() {
|
||||
/*
|
||||
The position of the Stop Loss (above or below the price) is needed in order to know
|
||||
later if the price hit the Stop Loss or not at every Simulation cycle. When we get
|
||||
later if the price hit the Stop Loss or not at every Simulation candle. When we get
|
||||
the first values of the simulation of the Stop Loss we check if it is above or below
|
||||
the Position Rate, and we assign the values Above or Below to it.
|
||||
*/
|
||||
@@ -529,7 +491,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
function calculateTakeProfitPosition() {
|
||||
/*
|
||||
The position of the Take Profit (above or below the price) is needed in order to know
|
||||
later if the price hit the Take Profit or not at every Simulation cycle. When we get
|
||||
later if the price hit the Take Profit or not at every Simulation candle. When we get
|
||||
the first values of the simulation of the Take Profits we check if it is above or below
|
||||
the Position Rates, and we assign the values Above or Below to it.
|
||||
*/
|
||||
@@ -544,28 +506,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
|
||||
function checkStopPhasesEvents() {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let openStage = strategy.openStage
|
||||
let manageStage = strategy.manageStage
|
||||
let parentNode
|
||||
let phaseIndex
|
||||
let phase
|
||||
let stopLoss
|
||||
let stage
|
||||
|
||||
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Open Stage' && openStage !== undefined) {
|
||||
stage = openStage
|
||||
if (openStage.initialDefinition !== undefined) {
|
||||
if (openStage.initialDefinition.initialStopLoss !== undefined) {
|
||||
parentNode = openStage.initialDefinition
|
||||
phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value
|
||||
stopLoss = openStage.initialDefinition.initialStopLoss
|
||||
phase = stopLoss.phases[phaseIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tradingEngine.current.position.stopLoss.stopLossStage.value === 'Manage Stage' && manageStage !== undefined) {
|
||||
stage = manageStage
|
||||
if (manageStage !== undefined) {
|
||||
if (manageStage.managedStopLoss !== undefined) {
|
||||
parentNode = manageStage
|
||||
phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value - 1
|
||||
@@ -596,9 +543,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingSystem.highlights.push(phase.id)
|
||||
tradingSystem.highlights.push(stopLoss.id)
|
||||
tradingSystem.highlights.push(parentNode.id)
|
||||
tradingSystem.highlights.push(stage.id)
|
||||
tradingSystem.highlights.push(manageStage.id)
|
||||
|
||||
tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1, 'Manage Stage')
|
||||
tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1)
|
||||
|
||||
announcementsModule.makeAnnoucements(nextPhaseEvent)
|
||||
return // only one event can pass at the time
|
||||
@@ -626,13 +573,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingSystem.highlights.push(phase.id)
|
||||
tradingSystem.highlights.push(stopLoss.id)
|
||||
tradingSystem.highlights.push(parentNode.id)
|
||||
tradingSystem.highlights.push(stage.id)
|
||||
tradingSystem.highlights.push(manageStage.id)
|
||||
|
||||
let moveToPhase = moveToPhaseEvent.referenceParent
|
||||
if (moveToPhase !== undefined) {
|
||||
for (let q = 0; q < stopLoss.phases.length; q++) {
|
||||
if (stopLoss.phases[q].id === moveToPhase.id) {
|
||||
tradingPositionModule.updateStopLoss(q + 1, 'Manage Stage')
|
||||
tradingPositionModule.updateStopLoss(q + 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -657,22 +604,8 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
let phaseIndex
|
||||
let phase
|
||||
let takeProfit
|
||||
let stage
|
||||
|
||||
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Open Stage' && openStage !== undefined) {
|
||||
stage = openStage
|
||||
if (openStage.initialDefinition !== undefined) {
|
||||
if (openStage.initialDefinition.initialTakeProfit !== undefined) {
|
||||
parentNode = openStage.initialDefinition
|
||||
phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value
|
||||
takeProfit = openStage.initialDefinition.initialTakeProfit
|
||||
phase = takeProfit.phases[phaseIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tradingEngine.current.position.takeProfit.takeProfitStage.value === 'Manage Stage' && manageStage !== undefined) {
|
||||
stage = manageStage
|
||||
if (manageStage !== undefined) {
|
||||
if (manageStage.managedTakeProfit !== undefined) {
|
||||
parentNode = manageStage
|
||||
phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1
|
||||
@@ -703,9 +636,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingSystem.highlights.push(phase.id)
|
||||
tradingSystem.highlights.push(takeProfit.id)
|
||||
tradingSystem.highlights.push(parentNode.id)
|
||||
tradingSystem.highlights.push(stage.id)
|
||||
tradingSystem.highlights.push(manageStage.id)
|
||||
|
||||
tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1, 'Manage Stage')
|
||||
tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1)
|
||||
|
||||
announcementsModule.makeAnnoucements(nextPhaseEvent)
|
||||
return // only one event can pass at the time
|
||||
@@ -733,13 +666,13 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingSystem.highlights.push(phase.id)
|
||||
tradingSystem.highlights.push(takeProfit.id)
|
||||
tradingSystem.highlights.push(parentNode.id)
|
||||
tradingSystem.highlights.push(stage.id)
|
||||
tradingSystem.highlights.push(manageStage.id)
|
||||
|
||||
let moveToPhase = moveToPhaseEvent.referenceParent
|
||||
if (moveToPhase !== undefined) {
|
||||
for (let q = 0; q < takeProfit.phases.length; q++) {
|
||||
if (takeProfit.phases[q].id === moveToPhase.id) {
|
||||
tradingPositionModule.updateTakeProfit(q + 1, 'Manage Stage')
|
||||
tradingPositionModule.updateTakeProfit(q + 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -809,105 +742,87 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
async function runCloseStage() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyCloseStage.status.value === 'Opening') {
|
||||
/*
|
||||
This will happen only once, as soon as the Take Profit or Stop was hit.
|
||||
We wil not be placing orders at this time because we do not know
|
||||
the total filled size of the Open Stage. By skiping execution now
|
||||
we allow the open stage to get a final value for sizeFilled that we can use.
|
||||
*/
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
|
||||
|
||||
/* Exit Position size and rate */
|
||||
tradingSystem.evalFormulas(stageNode, 'Initial Targets')
|
||||
tradingPositionModule.initialTargets(stageNode)
|
||||
runWhenStatusIsOpening()
|
||||
await runWhenStatusIsOpen()
|
||||
|
||||
initializeStageTargetSize()
|
||||
changeStageStatus('Close Stage', 'Open')
|
||||
return
|
||||
}
|
||||
function runWhenStatusIsOpening() {
|
||||
/* Opening Status Procedure */
|
||||
if (tradingEngine.current.strategyCloseStage.status.value === 'Opening') {
|
||||
/*
|
||||
This will happen only once, as soon as the Take Profit or Stop was hit.
|
||||
*/
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
|
||||
|
||||
/* Open Status Procedure */
|
||||
if (tradingEngine.current.strategyCloseStage.status.value === 'Open') {
|
||||
/*
|
||||
This will happen as long as the Close Stage is Open.
|
||||
*/
|
||||
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
|
||||
let executionNode = stageNode.closeExecution
|
||||
/* Exit Position size and rate */
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Initial Targets')
|
||||
tradingPositionModule.initialTargets(tradingSystemStage, tradingEngine.current.strategyCloseStage)
|
||||
|
||||
tradingSystem.evalConditions(stageNode, 'Close Execution')
|
||||
tradingSystem.evalFormulas(stageNode, 'Close Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyCloseStage
|
||||
)
|
||||
|
||||
/*
|
||||
The Open is finished when the placedSize reaches the targetSize in any of the
|
||||
stage assets, and the fillSize + feesPaid reaches the placedSize also in any
|
||||
of the stage Assets.
|
||||
This can happens at any time when we update the sizeFilled and the feesPaid values
|
||||
when we see at the exchange that orders were filled.
|
||||
*/
|
||||
if (
|
||||
(
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizePlaced >=
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.targetSize
|
||||
|
||||
||
|
||||
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizePlaced >=
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizePlaced.value
|
||||
|
||||
||
|
||||
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.sizePlaced.value
|
||||
)
|
||||
) {
|
||||
changeStageStatus('Close Stage', 'Closed', 'Size Placed Filled')
|
||||
} else {
|
||||
/* Check the Close Stage Event */
|
||||
tradingSystem.evalConditions(stageNode, 'Close Stage Event')
|
||||
if (checkStopStageEvent(stageNode) === true) {
|
||||
changeStageStatus('Close Stage', 'Closing', 'Close Stage Event')
|
||||
}
|
||||
initializeStageTargetSize()
|
||||
changeStageStatus('Close Stage', 'Open')
|
||||
}
|
||||
}
|
||||
|
||||
/* Exit Position Validation */
|
||||
if (
|
||||
async function runWhenStatusIsOpen() {
|
||||
/*
|
||||
The following code will run only when the Open Stage is closed.
|
||||
The reason for that is that we need the final filled size of the Open Stage
|
||||
to be known before we start placing orders at the Close Stage.
|
||||
*/
|
||||
if (
|
||||
tradingEngine.current.strategyCloseStage.status.value === 'Open' &&
|
||||
(
|
||||
tradingEngine.current.strategyTriggerStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyTriggerStage.status.value === tradingEngine.current.strategyTriggerStage.status.config.initialValue
|
||||
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyOpenStage.status.value == tradingEngine.current.strategyOpenStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyOpenStage.status.value === tradingEngine.current.strategyOpenStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyManageStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyManageStage.status.value === tradingEngine.current.strategyManageStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyCloseStage.status.value === 'Closed'
|
||||
)
|
||||
) {
|
||||
) {
|
||||
/*
|
||||
This will happen as long as the Close Stage is Open.
|
||||
*/
|
||||
let tradingSystemStage = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
|
||||
let tradingEngineStage = tradingEngine.current.strategyCloseStage
|
||||
let executionNode = tradingSystemStage.closeExecution
|
||||
|
||||
/* Exiting Everything now. */
|
||||
exitPositionAndStrategy()
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Close Execution')
|
||||
tradingSystem.evalFormulas(tradingSystemStage, 'Close Execution')
|
||||
|
||||
await tradingExecutionModule.runExecution(
|
||||
executionNode,
|
||||
tradingEngine.current.strategyCloseStage
|
||||
)
|
||||
|
||||
checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, 'Close Stage')
|
||||
}
|
||||
|
||||
/*
|
||||
Exit Position Validation: we need al the previous stages to be either closed
|
||||
or at their initial default value. The last is becasue after Closed objects are
|
||||
initialized to their defualts.
|
||||
*/
|
||||
if (
|
||||
(
|
||||
tradingEngine.current.strategyTriggerStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyTriggerStage.status.value === tradingEngine.current.strategyTriggerStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyOpenStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyOpenStage.status.value === tradingEngine.current.strategyOpenStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyManageStage.status.value === 'Closed' ||
|
||||
tradingEngine.current.strategyManageStage.status.value === tradingEngine.current.strategyManageStage.status.config.initialValue
|
||||
)
|
||||
&&
|
||||
(
|
||||
tradingEngine.current.strategyCloseStage.status.value === 'Closed'
|
||||
)
|
||||
) {
|
||||
|
||||
/* Closing Everything now. */
|
||||
closePositionAndStrategy()
|
||||
}
|
||||
}
|
||||
|
||||
function initializeStageTargetSize() {
|
||||
@@ -921,8 +836,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize.value = global.PRECISE(tradingEngine.current.strategyCloseStage.stageQuotedAsset.targetSize.value, 10)
|
||||
}
|
||||
|
||||
function exitPositionAndStrategy() {
|
||||
|
||||
function closePositionAndStrategy() {
|
||||
/* Taking Position Snapshot */
|
||||
if (bot.SESSION.type === 'Backtesting Session') {
|
||||
if (sessionParameters.snapshots !== undefined) {
|
||||
@@ -957,8 +871,53 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
}
|
||||
}
|
||||
|
||||
function checkStopStageEvent(stage) {
|
||||
function checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, stageName) {
|
||||
/*
|
||||
The Stage is closed when the placedSize reaches the targetSize, and the fillSize
|
||||
+ feesPaid reaches the placedSize. This depends on how the user defined the stage:
|
||||
either on Base Asset or Quoted Asset.
|
||||
This can happens at any time when we update the sizeFilled and the feesPaid values
|
||||
when we see at the exchange that orders were filled.
|
||||
*/
|
||||
switch (tradingEngineStage.stageDefinedIn.value) {
|
||||
case 'Base Asset': {
|
||||
if (
|
||||
tradingEngineStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngineStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngineStage.stageBaseAsset.targetSize.value
|
||||
) {
|
||||
positionFilled()
|
||||
} else {
|
||||
checkCloseStageEvent(tradingSystemStage)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Quoted Asset': {
|
||||
if (
|
||||
tradingEngineStage.stageQuotedAsset.sizeFilled.value +
|
||||
tradingEngineStage.stageQuotedAsset.feesPaid.value >=
|
||||
tradingEngineStage.stageQuotedAsset.targetSize.value
|
||||
) {
|
||||
positionFilled()
|
||||
} else {
|
||||
checkCloseStageEvent(tradingSystemStage)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
function positionFilled() {
|
||||
changeStageStatus(stageName, 'Closed', 'Position Size Filled')
|
||||
}
|
||||
function checkCloseStageEvent(tradingSystemStage) {
|
||||
/* Check the Close Stage Event */
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Close Stage Event')
|
||||
if (checkStopStageEvent(tradingSystemStage) === true) {
|
||||
changeStageStatus(stageName, 'Closing', 'Close Stage Event')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkStopStageEvent(stage) {
|
||||
/* Check the Close Stage Event. */
|
||||
let stopStageEvent = stage.stopStageEvent
|
||||
if (stopStageEvent !== undefined) {
|
||||
@@ -1017,33 +976,33 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
|
||||
function openStage(stage) {
|
||||
/* Recording the opening at the Trading Engine Data Structure */
|
||||
stage.begin.value = tradingEngine.current.episode.candle.begin.value
|
||||
stage.begin.value = tradingEngine.current.episode.cycle.begin.value
|
||||
stage.beginRate.value = tradingEngine.current.episode.candle.close.value
|
||||
stage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
stage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
}
|
||||
|
||||
function closeStage(stage) {
|
||||
/* Recording the closing at the Trading Engine Data Structure */
|
||||
stage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
stage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
stage.endRate.value = tradingEngine.current.episode.candle.close.value
|
||||
}
|
||||
}
|
||||
|
||||
function updateEnds() {
|
||||
if (tradingEngine.current.strategyTriggerStage.status.value === 'Open') {
|
||||
tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
tradingEngine.current.strategyTriggerStage.endRate.value = tradingEngine.current.episode.candle.close.value
|
||||
}
|
||||
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
|
||||
tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
tradingEngine.current.strategyOpenStage.endRate.value = tradingEngine.current.episode.candle.close.value
|
||||
}
|
||||
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
|
||||
tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
tradingEngine.current.strategyManageStage.endRate.value = tradingEngine.current.episode.candle.close.value
|
||||
}
|
||||
if (tradingEngine.current.strategyCloseStage.status.value === 'Open') {
|
||||
tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.episode.cycle.end.value
|
||||
tradingEngine.current.strategyCloseStage.endRate.value = tradingEngine.current.episode.candle.close.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
|
||||
const MODULE_NAME = 'Trading Strategy'
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
openStrategy: openStrategy,
|
||||
closeStrategy: closeStrategy,
|
||||
initialize: initialize,
|
||||
@@ -24,16 +25,19 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
|
||||
}
|
||||
|
||||
function mantain() {
|
||||
resetTradingEngineDataStructure()
|
||||
updateCounters()
|
||||
updateEnds()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
resetTradingEngineDataStructure()
|
||||
}
|
||||
|
||||
function openStrategy(index, situationName, strategyName) {
|
||||
tradingEngine.current.strategy.status.value = 'Open'
|
||||
tradingEngine.current.strategy.serialNumber.value = tradingEngine.current.episode.episodeCounters.strategies.value
|
||||
tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID()
|
||||
tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.begin.value
|
||||
tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value
|
||||
tradingEngine.current.strategy.beginRate.value = tradingEngine.current.episode.candle.min.value
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
const MODULE_NAME = 'Trading System'
|
||||
let thisObject = {
|
||||
mantain: mantain,
|
||||
reset: reset,
|
||||
run: run,
|
||||
updateChart: updateChart,
|
||||
initialize: initialize,
|
||||
@@ -97,19 +98,19 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
}
|
||||
|
||||
function mantain() {
|
||||
reset()
|
||||
tradingStagesModule.mantain()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
/* This function helps reset data structures at every cycle of the simulation loop/=. */
|
||||
tradingSystem.highlights = []
|
||||
tradingSystem.errors = []
|
||||
tradingSystem.values = []
|
||||
tradingSystem.status = []
|
||||
tradingSystem.progress = []
|
||||
tradingSystem.running = []
|
||||
tradingSystem.announcements = []
|
||||
}
|
||||
function reset() {
|
||||
tradingStagesModule.reset()
|
||||
|
||||
tradingSystem.highlights = []
|
||||
tradingSystem.errors = []
|
||||
tradingSystem.values = []
|
||||
tradingSystem.status = []
|
||||
tradingSystem.progress = []
|
||||
tradingSystem.running = []
|
||||
tradingSystem.announcements = []
|
||||
}
|
||||
|
||||
function updateChart(pChart) {
|
||||
|
||||
@@ -160,6 +160,20 @@
|
||||
finalDatetime: (new Date()).valueOf()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Check that we received valid dates */
|
||||
if(isNaN(new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) {
|
||||
let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date."
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage)
|
||||
bot.sessionError(bot.SESSION.parameters, errorMessage)
|
||||
return false
|
||||
}
|
||||
if(isNaN(new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) {
|
||||
let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date."
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage)
|
||||
bot.sessionError(bot.SESSION.parameters, errorMessage)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/* Session Type Forced Values */
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
/* Callbacks default responses. */
|
||||
global.DEFAULT_OK_RESPONSE = {
|
||||
result: "Ok",
|
||||
|
||||
Reference in new issue
Block a user