Improvements on Live Trading

This commit is contained in:
Luis Fernando Molina committed 2020-08-22 17:16:26 +02:00
1 parent b588db88dc
commit 092219ec47
12 files changed
+371 -259

No files matched your search

+1 -1
View File
@@ -8646,7 +8646,7 @@
"compatibleTypes": "->Execution Finished Event->"
},
"chainAttachesTo": {
"compatibleTypes": "->Process Definition->"
"compatibleTypes": "->Process Definition->Trading Process Instance->"
},
"icon": "execution-started-event",
"propertyNameAtParent": "executionStartedEvent"
+4 -2
View File
@@ -110,6 +110,7 @@ function newUiObject() {
let errorMessage = ''
let currentValue = 0
let valueMinDecimals = undefined
let currentPercentage = ''
let currentStatus = ''
let rightDragging = false
@@ -745,10 +746,11 @@ function newUiObject() {
hasError = false
}
function setValue(value, counter) {
function setValue(value, counter, minDecimals) {
if (value !== undefined) {
currentValue = value
hasValue = true
valueMinDecimals = minDecimals
if (counter !== undefined) {
valueCounter = counter
} else {
@@ -1438,7 +1440,7 @@ function newUiObject() {
label = currentValue
if (!isNaN(currentValue)) {
if (currentValue.toFixed !== undefined) {
label = dynamicDecimals(currentValue)
label = dynamicDecimals(currentValue, valueMinDecimals)
}
}
+4 -4
View File
@@ -95,14 +95,14 @@ function newRecordValues() {
pointing to the same property at the Target Node.
*/
if (property.config.childProperty !== undefined) {
applyValue(targetNode[property.config.childProperty], value)
applyValue(targetNode[property.config.childProperty], value, property.config.decimals)
} else {
applyValue(targetNode[property.config.codeName], value)
applyValue(targetNode[property.config.codeName], value, property.config.decimals)
}
}
}
function applyValue(node, value) {
function applyValue(node, value, minDecimals) {
if (canvas.chartingSpace.visible !== true) { return }
if (node === undefined) { return }
if (node === undefined) { return }
@@ -110,6 +110,6 @@ function newRecordValues() {
if (node.payload.uiObject === undefined) { return }
if (value === true) { value = 'true' }
if (value === false) { value = 'false' }
node.payload.uiObject.setValue(value, 1)
node.payload.uiObject.setValue(value, 1, minDecimals)
}
}
+8 -7
View File
@@ -7,14 +7,15 @@ function dynamicDecimals(value, minDecimals) {
if (minDecimals === undefined) {
minDecimals = 0
}
let decimals = 2
if (value < 1) { decimals = 4 }
if (Math.abs(Math.trunc(value * 100)) < 1) { decimals = 6 }
if (Math.abs(Math.trunc(value * 10000)) < 1) { decimals = 8 }
if (Math.abs(Math.trunc(value * 1000000)) < 1) { decimals = 10 }
if (Math.abs(Math.trunc(value * 100000000)) < 1) { decimals = 12 }
let decimals = 0
if (value < 1) { decimals = 2 }
if (Math.abs(Math.trunc(value * 100)) < 1) { decimals = 4 }
if (Math.abs(Math.trunc(value * 10000)) < 1) { decimals = 6 }
if (Math.abs(Math.trunc(value * 1000000)) < 1) { decimals = 8 }
if (Math.abs(Math.trunc(value * 100000000)) < 1) { decimals = 10 }
if (Math.abs(Math.trunc(value * 10000000000)) < 1) { value = 0; decimals = 0 }
if (Math.trunc(value) === value) { decimals = minDecimals }
if (decimals < minDecimals) {decimals = minDecimals}
let returnValue = Number(value).toFixed(decimals)
return returnValue
}
+1 -1
View File
@@ -1 +1 @@
{"type":"Data Mine","name":"Masters-Trading","config":"{ \n\"codeName\": \"Masters-Trading\"\n}","sensorBots":[],"indicatorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\"\n}","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","id":"6fc3054e-5034-4fc7-bddf-02360e0a6acf","savedPayload":{"position":{"x":15126.709841795062,"y":14115.844903867037},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"db063d7b-db05-4bf6-941f-999e1ccb38fc"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","id":"c3c2240f-514b-40de-825f-674037117d0b","savedPayload":{"position":{"x":15060.50960648652,"y":13958.543096746436},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"0764aaaf-d970-4f23-9df8-55fe5b7dd273"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","id":"1dfdfc0d-842f-4e63-a591-256ac8a06b0f","savedPayload":{"position":{"x":15000.170158581612,"y":13798.909615340754},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","id":"39360c55-5ba8-413b-b0bd-cac3789ebf5f","savedPayload":{"position":{"x":14945.771166150153,"y":13637.156840896287},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Current Candle","id":"ba614415-6e4f-4979-97a5-34c72a59e67c","savedPayload":{"position":{"x":14897.386505301713,"y":13473.814926987687},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Current Candle","id":"1a2e3461-33e6-45b1-bcc1-c56a8c5d275c","savedPayload":{"position":{"x":14855.08212849534,"y":13309.762335468542},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":14818.915848183393,"y":13144.277773603595},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":14788.936996026083,"y":12977.582492132336},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"213b8fe8-f772-41ea-a3f5-97867be555f7"}}},{"type":"Output Dataset","name":"Multi-Period-Market Distance To Event","id":"a30e7306-7be9-41b6-a465-558fdfe2a903","savedPayload":{"position":{"x":14765.186501949274,"y":12809.903263371778},"targetPosition":{"x":19338.91372893721,"y"Line truncated
{"type":"Data Mine","name":"Masters-Trading","config":"{ \n\"codeName\": \"Masters-Trading\"\n}","sensorBots":[],"indicatorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\"\n}","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","id":"6fc3054e-5034-4fc7-bddf-02360e0a6acf","savedPayload":{"position":{"x":15126.709841795062,"y":14115.844903867037},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"db063d7b-db05-4bf6-941f-999e1ccb38fc"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","id":"c3c2240f-514b-40de-825f-674037117d0b","savedPayload":{"position":{"x":15060.50960648652,"y":13958.543096746436},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"0764aaaf-d970-4f23-9df8-55fe5b7dd273"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","id":"1dfdfc0d-842f-4e63-a591-256ac8a06b0f","savedPayload":{"position":{"x":15000.170158581612,"y":13798.909615340754},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","id":"39360c55-5ba8-413b-b0bd-cac3789ebf5f","savedPayload":{"position":{"x":14945.771166150153,"y":13637.156840896287},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Current Candle","id":"ba614415-6e4f-4979-97a5-34c72a59e67c","savedPayload":{"position":{"x":14897.386505301713,"y":13473.814926987687},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Current Candle","id":"1a2e3461-33e6-45b1-bcc1-c56a8c5d275c","savedPayload":{"position":{"x":14855.08212849534,"y":13309.762335468542},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","id":"567e4dc9-4deb-41e4-9ac9-dde943fb91ba","savedPayload":{"position":{"x":14818.915848183393,"y":13144.277773603595},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","id":"e94b9bb3-9f0c-4550-94e1-09de444780a8","savedPayload":{"position":{"x":14788.936996026083,"y":12977.582492132336},"targetPosition":{"x":19338.91372893721,"y":12256.290080293493},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"213b8fe8-f772-41ea-a3f5-97867be555f7"}}},{"type":"Output Dataset","name":"Multi-Period-Market Distance To Event","id":"a30e7306-7be9-41b6-a465-558fdfe2a903","savedPayload":{"position":{"x":14765.186501949274,"y":12809.903263371778},"targetPosition":{"x":19338.91372893721,"y"Line truncated
@@ -124,7 +124,7 @@
return order.id
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
logError("createOrder -> Error = " + err.message);
}
}
@@ -133,13 +133,13 @@
let orderId = tradingEngineOrder.exchangeId.value
/* Basic Logging */
logInfo("getOrder -> Entering function. orderId = " + orderId);
logInfo("cancelOrder -> Entering function. orderId = " + orderId);
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
/* Basic Validations */
if (exchange.has['fetchOrder'] === false) {
logError("getOrder -> Exchange does not support fetchOrder command.");
logError("cancelOrder -> Exchange does not support fetchOrder command.");
return
}
@@ -148,7 +148,7 @@
return true
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
logError("cancelOrder -> Error = " + err.message);
}
}
@@ -9,7 +9,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
openEpisode: openEpisode,
updateExitType: updateExitType,
closeEpisode: closeEpisode,
calculateStatistics: calculateStatistics,
cycleBasedStatistics: cycleBasedStatistics,
initialize: initialize,
finalize: finalize
}
@@ -113,37 +113,24 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
global.ONE_DAY_IN_MILISECONDS
}
function calculateStatistics() {
function cycleBasedStatistics() {
calculateAssetsStatistics()
calculateEpisodeStatistics()
function calculateAssetsStatistics() {
/* Updating Hits & Fails */
if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hits.value++
} else {
tradingEngine.current.episode.episodeBaseAsset.fails.value++
}
if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hits.value++
} else {
tradingEngine.current.episode.episodeQuotedAsset.fails.value++
}
/* Updating Profit Loss */
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
sessionParameters.sessionBaseAsset.config.initialBalance
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
sessionParameters.sessionQuotedAsset.config.initialBalance
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.profitLoss.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value, 10)
/*
Updating ROI
@@ -151,55 +138,57 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
*/
tradingEngine.current.episode.episodeBaseAsset.ROI.value =
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value /
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeQuotedAsset.ROI.value =
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value /
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.ROI.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.ROI.value, 10)
/* Updating Hit Ratio */
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value =
tradingEngine.current.episode.episodeBaseAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value =
tradingEngine.current.episode.episodeQuotedAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
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)
if (tradingEngine.current.episode.episodeCounters.positions.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value =
tradingEngine.current.episode.episodeBaseAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value =
tradingEngine.current.episode.episodeQuotedAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
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 Annualized Rate Of Return
https://www.investopedia.com/terms/a/annualized-rate.asp
*/
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value
) / tradingEngine.current.episode.episodeBaseAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value
) / tradingEngine.current.episode.episodeBaseAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) / tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
Math.pow(
(
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) / tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
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) {
tradingEngine.current.episode.episodeBaseAsset.hitFail.value = 'Hit'
@@ -218,9 +207,9 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
tradingEngine.current.episode.episodeStatistics.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.episode.candle.close.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
tradingEngine.current.episode.episodeStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.profitLoss.value, 10)
/*
Updating ROI
@@ -234,32 +223,32 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
) * 100
tradingEngine.current.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.ROI.value, 10)
/*
Updating Annualized Rate Of Return
https://www.investopedia.com/terms/a/annualized-rate.asp
*/
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) /
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
)
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) /
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
)
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
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) {
tradingEngine.current.episode.episodeStatistics.hitFail.value = 'Hit'
@@ -76,6 +76,8 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
}
} catch (err) {
tradingSystem.errors.push([executionNode.id, err.message])
if (typeof err === 'string' || err instanceof String) {
logger.write(MODULE_NAME, '[ERROR] runExecution -> err = ' + err)
}
+233 -135
View File
@@ -117,9 +117,9 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
switch (tradingEngineOrder.status.value) {
case 'Not Open': {
{
{
/* During the First cycle we can not create new orders. That is reserved for the Second cycle. */
if (tradingEngine.current.episode.cycle.value === 'First') {continue}
if (tradingEngine.current.episode.cycle.value === 'First') { continue }
/* When the stage is closing we can not create new orders */
if (tradingEngineStage.status.value === 'Closing') { continue }
/*
@@ -150,14 +150,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
await checkExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder)
/* During the Second cycle we can not cancel orders. That is reserved for the First cycle. */
if (tradingEngine.current.episode.cycle.value === 'Second') {continue}
if (tradingEngine.current.episode.cycle.value === 'Second') { continue }
/*
In the previous steps, we might have discovered that the order was cancelled
at the exchange, or filled, so the order might still not be Open.
If the stage is closing or the order is not Open, we wont be cancelling orders
based on defined events.
*/
if (tradingEngineStage.status.value !== 'Closing' && tradingEngineOrder.status.value === 'Open') {
if (tradingEngineStage.status.value !== 'Closing' && tradingEngineOrder.status.value === 'Open') {
/* Check if we need to Cancel this Order */
let situationName = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
@@ -248,7 +248,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
if (tradingSystemOrder.type === 'Limit Buy Order' || tradingSystemOrder.type === 'Limit Sell Order') {
if (tradingSystemOrder.orderRate === undefined) { badDefinitionUnhandledException(undefined, 'tradingSystemOrder.orderRate === undefined', tradingSystemOrder) }
if (tradingSystemOrder.orderRate.formula === undefined) { badDefinitionUnhandledException(undefined, 'tradingSystemOrder.orderRate.formula === undefined', tradingSystemOrder) }
/* Extract the rate value from the user-defined formula */
tradingEngineOrder.rate.value = tradingSystem.formulas.get(tradingSystemOrder.orderRate.formula.id)
@@ -257,7 +257,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
if (isNaN(tradingEngineOrder.rate.value) === true) { badDefinitionUnhandledException(undefined, 'isNaN(tradingEngineOrder.rate.value) === true', tradingSystemOrder) }
if (tradingEngineOrder.rate.value <= 0) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.rate.value <= 0', tradingSystemOrder) }
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
} else {
/*
For Market Orders, the rate is irrelevant, since it is not sent to the Exchange.
@@ -367,6 +367,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
}
}
let previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value
let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value
let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value
let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value
@@ -416,7 +417,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
switch (tradingEngineOrder.type) {
case 'Market Order': {
/* Actual Rate is simulated based on the Session Paremeters */
let slippageAmount = tradingEngineOrder.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100
let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100
switch (tradingSystemOrder.type) {
case 'Market Sell Order': {
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount
@@ -437,6 +438,24 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
}
}
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
/*
Recalculate the Quouted Asset Size based on the newly calculated Actual Rate.
*/
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.orderStatistics.actualRate.value
tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10)
/*
Recaulculate the Size Placed Based on the newly calculated Quoted Asset Size.
*/
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
previousQuotedAssetSize
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
tradingEngineOrder.orderQuotedAsset.size.value
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
}
function percentageFilledSimulation() {
@@ -463,13 +482,13 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
break
}
case 'Market Buy Order': {
orderWasHit = true
orderWasHit = true
break
}
case 'Market Sell Order': {
orderWasHit = true
break
}
break
}
}
if (orderWasHit === true) {
let calculatedBasedOnTradingSystem = false
@@ -515,88 +534,47 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
function feesPaidSimulation() {
/*
The way the fess paid is calculated is as follows: From the order size we apply the % of fee,
The way the Fess Paid are 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 !== undefined) {
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
}
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage)
return
}
}
}
/* 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
}
/*
Another way to simulate the Fees Paid is by using the Session Paremeters configuration for Fees.
*/
switch (tradingEngineOrder.type) {
case 'Market Order': {
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker)
break
}
case 'Limit Order': {
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker)
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 =
(tradingEngineOrder.orderBaseAsset.size.value - tradingEngineOrder.orderBaseAsset.feesPaid.value) *
tradingEngineOrder.orderBaseAsset.size.value *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderQuotedAsset.sizeFilled.value =
(tradingEngineOrder.orderQuotedAsset.size.value - tradingEngineOrder.orderQuotedAsset.feesPaid.value) *
tradingEngineOrder.orderQuotedAsset.size.value *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10)
@@ -667,7 +645,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
updateEndsWithCycle(tradingEngineOrder)
}
syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
/*
If the order happens to be at least partially filled, there is a syncronization work
we need to do, that includes discovering which is the Actual Rate the order is being filled,
the Fees Paid and many other thing we need to account for.
*/
if (order.filled > 0) {
syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
}
/* Forced Cancellation Check */
if (tradingEngineStage.status.value === 'Closing' && tradingEngineOrder.status.value !== 'Closed') {
@@ -677,44 +662,16 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
function syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order) {
let previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value
let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value
let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value
let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value
let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value
/* Actual Rate Calculation */
tradingEngineOrder.orderStatistics.actualRate.value = order.price
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
/* Fees Paid Calculation */
/*
As a response from the exchange we can not always get the fees.
For that reason we need to estimate them base of the information that we do have.
CCXT provides order.amount which represents the size we set for the order minus the
fees taken by the exchange, all denominated in Base Asset.
In this way if we substract to the order size this order.amount
we can get the fees. All this is denominated in base asset because that is how CCXT works.
The fees then can be estimated in Quoted Asset using the Actual Rate.
*/
tradingEngineOrder.orderBaseAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.size.value - order.amount
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
tradingEngineOrder.orderQuotedAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.feesPaid.value * tradingEngineOrder.orderStatistics.actualRate.value
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
/* Percentage Filled Calculation */
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
/* Size Filled Calculation */
/*
CCXT returns order.filled with an amount denominated in Base Asset. We will
take it from there for our Order Base Asset. For our Order Quoted Asset we
will use the field order.cost.
*/
tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled
tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10)
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = order.cost
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10)
actualRateCalculation()
percentageFilledCalculation()
feesPaidCalculation()
sizeFilledCalculation()
doTheAccounting(
tradingEngineStage,
@@ -725,6 +682,108 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
previousBaseAssetFeesPaid,
previousQuotedAssetFeesPaid
)
return
function actualRateCalculation() {
/*
Actual Rate Calculation: Let's remember that the exchange may not take the rate
we send it with the order (in Limit orders) and for any reason change it a little bit.
For example we might have sent a rate with too many decimals and the exchange truncate
them. Another thing that might happen is that we send a rate that is matching orders already
at the order book. In this case we would get an actual rate not only different than the one
we sent, but also, better since the trading engine inside the exchange would match out orders
with the first orders best by price.
In the case of Market Orders we don't even send a rate. For all those reason
we get and store the Actual Rate at which the order was filled.
*/
if (order.average !== undefined) {
/*
We will use the average whenever is available.
*/
tradingEngineOrder.orderStatistics.actualRate.value = order.average
} else {
/*
Otherwise we use the order.price.
*/
tradingEngineOrder.orderStatistics.actualRate.value = order.price
}
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
/*
Now we know the Actual Rate at which the order was filled. Since the actual rate
might not be the same as the Rate we defined for the order, we need to syncronize
the Order Size for Quoted Asset since it was calculated with the Order Size that we
now know it is not the once really used at the exchange. We will recalculate the
Order Size in Quoted Asset and not in Base Asset, since the Order Size in Base Asset
is the one we sent to the exchange, so that is fixed, regardless of how we initially
got the Order Size in Base Asset, eigher from direct imput from the user or calculated
from the Order Size in Quoted Asset input it by the user. So here we go.
*/
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.orderStatistics.actualRate.value
tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10)
/*
Great, this up-to-date order size in Base Asset and the Actual Rate can now be used for example to calculate
the orders fees in Quoted Assets, and it is perfectly accurate. The next problem
we need to fix is that the sizePlaced for Quoted Asset that we already have calculated
was based on the Order Rate, not the Actual Rate, so we need to fix that by anuncounting
the previous size and accounting the new one, all this only for Quoted Asset.
*/
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
previousQuotedAssetSize
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
tradingEngineOrder.orderQuotedAsset.size.value
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
}
function percentageFilledCalculation() {
/* Percentage Filled Calculation */
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
}
function feesPaidCalculation() {
/*
The exchange fees are taken from the Base Asset or the Quoted Asset depending if we
are buying or selling. Within the order information received we can not see the fees paid so we need to
calculate them by ourselves. To do this it is needed to be used the Session Fees Parameters.
*/
switch (tradingEngineOrder.type) {
case 'Market Order': {
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker)
break
}
case 'Limit Order': {
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker)
break
}
}
}
function sizeFilledCalculation() {
/*
CCXT returns order.filled with an amount denominated in Base Asset. We will
take it from there for our Order Base Asset. The amount in order.filled does
not account for the fees, which is exactly what we need in this case.
*/
tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled
tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10)
/*
For Quoted Asset, CCXT does not return any field with the size filled, so we
need to calculate that by ourselves. Since we know the Size in Quoted Asset thet
is already adjusted with the Actual Rate and the Percentage Filled, we will use all
this in this formula to get the Size Filled in Quoted Asset.
*/
tradingEngineOrder.orderQuotedAsset.sizeFilled.value =
tradingEngineOrder.orderQuotedAsset.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10)
}
}
function doTheAccounting(
@@ -787,61 +846,72 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
function updateBalances() {
/* Balances Update */
switch (true) {
/*
Let's remember that for Buy orders, the fees are paid in Base Asset. So we
actually don't receive the whole Size Filled in Base Asset, but the Size
Filled minus the Fees Paid. For the case of Quoted Asset, we actually
need to susbstract the whole Size Filled from the balance.
*/
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
/* Balance Base Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
previousBaseAssetSizeFilled
/* Balance Base Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value +
tradingEngineOrder.orderBaseAsset.sizeFilled.value
/* Balance Quoted Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value +
previousQuotedAssetSizeFilled +
previousQuotedAssetFeesPaid
/* Balance Quoted Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngineOrder.orderQuotedAsset.sizeFilled.value -
tradingEngineOrder.orderQuotedAsset.feesPaid.value
break
}
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/* Balance Base Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value +
previousBaseAssetSizeFilled +
previousBaseAssetFeesPaid
/* Balance Base Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
tradingEngine.current.episode.episodeBaseAsset.balance.value +
tradingEngineOrder.orderBaseAsset.sizeFilled.value -
tradingEngineOrder.orderBaseAsset.feesPaid.value
/* Balance Quoted Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngine.current.episode.episodeQuotedAsset.balance.value +
previousQuotedAssetSizeFilled
/* Balance Quoted Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value +
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngineOrder.orderQuotedAsset.sizeFilled.value
break
}
/*
For Sell orders, the fees are being paid in Quote Asset, so we never
acutally receive the whole Filled Size in Quote Asset, but the Filled
Size minus the Fees Paid. But we do expend the whole Filled Size in
Base Asset.
*/
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/* Balance Base Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value +
previousBaseAssetSizeFilled
/* Balance Base Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeBaseAsset.balance.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
tradingEngineOrder.orderBaseAsset.sizeFilled.value
/* Balance Quoted Asset: Undo the previous accounting */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
previousQuotedAssetSizeFilled +
previousQuotedAssetFeesPaid
/* Balance Quoted Asset: Account the current filling and fees */
tradingEngine.current.episode.episodeQuotedAsset.balance.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value +
tradingEngineOrder.orderQuotedAsset.sizeFilled.value -
tradingEngineOrder.orderQuotedAsset.feesPaid.value
break
}
}
tradingEngine.current.episode.episodeBaseAsset.balance.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.balance.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.balance.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.balance.value, 10)
}
}
function simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, exitType) {
@@ -911,7 +981,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
recalculateStageSize(tradingEngineStage)
recalculateStageSize(tradingEngineStage, tradingEngineOrder)
}
}
@@ -926,23 +996,51 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
tradingEngineStage.stageBaseAsset.sizePlaced.value =
tradingEngineStage.stageBaseAsset.sizePlaced.value -
tradingEngineOrder.orderBaseAsset.size.value
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
tradingEngineOrder.orderQuotedAsset.size.value
tradingEngineStage.stageBaseAsset.sizePlaced.value =
tradingEngineStage.stageBaseAsset.sizePlaced.value +
tradingEngineOrder.orderBaseAsset.sizeFilled.value +
tradingEngineOrder.orderBaseAsset.feesPaid.value
tradingEngineOrder.orderBaseAsset.sizeFilled.value
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
tradingEngineOrder.orderQuotedAsset.sizeFilled.value +
tradingEngineOrder.orderQuotedAsset.feesPaid.value
tradingEngineOrder.orderQuotedAsset.sizeFilled.value
tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10)
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
}
function applyFeePercentage(tradingEngineOrder, tradingSystemOrder, feePercentage) {
switch (true) {
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
/*
In this case the fees are taken from the Base Asset you receive.
*/
tradingEngineOrder.orderBaseAsset.feesPaid.value =
tradingEngineOrder.orderBaseAsset.size.value *
feePercentage / 100 *
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
break
}
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/*
In this case the fees are taken from the Quoted Asset you receive.
*/
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
tradingEngineOrder.orderQuotedAsset.size.value *
feePercentage / 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 checkOrderEvent(event, order, executionAlgorithm, executionNode) {
if (event !== undefined) {
for (let k = 0; k < event.situations.length; k++) {
@@ -6,7 +6,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
let thisObject = {
mantain: mantain,
reset: reset,
calculateStatistics: calculateStatistics,
cycleBasedStatistics: cycleBasedStatistics,
openPosition: openPosition,
closingPosition: closingPosition,
closePosition: closePosition,
@@ -92,6 +92,22 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
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.
*/
tradingEngineModule.cloneValues(tradingEngine.current.position, tradingEngine.last.position)
cycleBasedStatistics()
/* Updating Hits & Fails */
if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hits.value++
}
if (tradingEngine.current.position.positionBaseAsset.profitLoss.value < 0) {
tradingEngine.current.episode.episodeBaseAsset.fails.value++
}
if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hits.value++
}
if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value < 0) {
tradingEngine.current.episode.episodeQuotedAsset.fails.value++
}
}
function applyStopLossFormula(formulas, formulaId) {
@@ -256,47 +272,55 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
}
}
function calculateStatistics(tradingEngineStageNode) {
function cycleBasedStatistics() {
calculateAssetsStatistics()
calculatePositionStatistics()
function calculateAssetsStatistics() {
/* Profit Loss Calculation */
tradingEngine.current.position.positionBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
tradingEngine.current.position.positionBaseAsset.beginBalance
tradingEngine.current.position.positionQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngine.current.position.positionQuotedAsset.beginBalance
tradingEngine.current.position.positionBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.profitLoss.value, 10)
tradingEngine.current.position.positionQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionBaseAsset.ROI.value =
tradingEngine.current.position.positionBaseAsset.profitLoss.value * 100 /
tradingEngine.current.strategyOpenStage.stageBaseAsset.targetSize.value
tradingEngine.current.position.positionQuotedAsset.ROI.value =
tradingEngine.current.position.positionQuotedAsset.profitLoss.value * 100 /
tradingEngine.current.strategyOpenStage.stageQuotedAsset.targetSize.value
tradingEngine.current.position.positionBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.ROI.value, 10)
tradingEngine.current.position.positionQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionBaseAsset.ROI.value > 0) {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Hit'
} else {
}
if (tradingEngine.current.position.positionBaseAsset.ROI.value < 0) {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionBaseAsset.ROI.value === 0) {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Even'
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Hit'
} else {
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value < 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value === 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Even'
}
}
function calculatePositionStatistics() {
@@ -305,10 +329,10 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
(
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.position.endRate.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
)
)
tradingEngine.current.position.positionStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionStatistics.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionStatistics.ROI.value =
(
@@ -320,19 +344,23 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
tradingEngine.current.position.positionQuotedAsset.beginBalance
)
tradingEngine.current.position.positionStatistics.ROI.value = global.PRECISE(tradingEngine.current.position.positionStatistics.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionStatistics.ROI.value > 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Hit'
} else {
}
if (tradingEngine.current.position.positionStatistics.ROI.value < 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionStatistics.ROI.value === 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Even'
}
/* Days Calculation */
tradingEngine.current.position.positionStatistics.days.value =
tradingEngine.current.position.positionCounters.periods.value *
sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
tradingEngine.current.position.positionStatistics.days.value = global.PRECISE(tradingEngine.current.position.positionStatistics.days.value, 10)
}
}
@@ -7,7 +7,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
updateChart: updateChart,
mantain: mantain,
reset: reset,
calculateStatistics: calculateStatistics,
cycleBasedStatistics: cycleBasedStatistics,
runTriggerStage: runTriggerStage,
runOpenStage: runOpenStage,
runManageStage: runManageStage,
@@ -102,9 +102,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
resetTradingEngineDataStructure()
}
function calculateStatistics() {
tradingEpisodeModule.calculateStatistics()
tradingPositionModule.calculateStatistics()
function cycleBasedStatistics() {
tradingEpisodeModule.cycleBasedStatistics()
tradingPositionModule.cycleBasedStatistics()
}
function runTriggerStage() {
@@ -359,8 +359,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
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.sizeFilled.value >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizePlaced.value
) {
changeStageStatus('Open Stage', 'Closed')
@@ -369,8 +368,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
case 'Quoted Asset': {
if (
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value +
tradingEngine.current.strategyOpenStage.stageQuotedAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value >=
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizePlaced.value
) {
changeStageStatus('Open Stage', 'Closed')
@@ -897,10 +895,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
switch (tradingEngineStage.stageDefinedIn.value) {
case 'Base Asset': {
if (
global.PRECISE(
tradingEngineStage.stageBaseAsset.sizeFilled.value +
tradingEngineStage.stageBaseAsset.feesPaid.value
, 10)
tradingEngineStage.stageBaseAsset.sizeFilled.value
>=
tradingEngineStage.stageBaseAsset.targetSize.value
) {
@@ -912,10 +907,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
}
case 'Quoted Asset': {
if (
global.PRECISE(
tradingEngineStage.stageQuotedAsset.sizeFilled.value +
tradingEngineStage.stageQuotedAsset.feesPaid.value
, 10)
tradingEngineStage.stageQuotedAsset.sizeFilled.value
>=
tradingEngineStage.stageQuotedAsset.targetSize.value
) {
@@ -132,7 +132,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
/* Run the Close Stage */
await tradingStagesModule.runCloseStage()
tradingStagesModule.calculateStatistics()
tradingStagesModule.cycleBasedStatistics()
} catch (err) {
/*