Files
Superalgos/LowFrequencyTrading/TradingPosition.js
T
2020-08-15 19:47:17 +02:00

352 lines
18 KiB
JavaScript

exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEngineModule) {
/*
This module packages all functions related to Positions.
*/
const MODULE_NAME = 'Trading Position'
let thisObject = {
mantain: mantain,
reset: reset,
openPosition: openPosition,
closingPosition: closingPosition,
closePosition: closePosition,
applyStopLossFormula: applyStopLossFormula,
applyTakeProfitFormula: applyTakeProfitFormula,
updateStopLoss: updateStopLoss,
updateTakeProfit: updateTakeProfit,
initialTargets: initialTargets,
updateEnds: updateEnds,
resetTradingEngineDataStructure: resetTradingEngineDataStructure,
updateCounters: updateCounters,
initialize: initialize,
finalize: finalize
}
let tradingEngine
let tradingSystem
let sessionParameters
return thisObject
function initialize() {
tradingSystem = bot.simulationState.tradingSystem
tradingEngine = bot.simulationState.tradingEngine
sessionParameters = bot.SESSION.parameters
}
function finalize() {
tradingEngine = undefined
tradingSystem = undefined
sessionParameters = undefined
}
function mantain() {
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 + 1
tradingEngine.current.position.identifier.value = global.UNIQUE_ID()
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 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) {
tradingEngine.current.position.status.value = 'Closing'
tradingEngine.current.position.exitType.value = exitType
}
function closePosition() {
tradingEngine.current.position.status.value = 'Closed'
tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.position.endRate.value = tradingEngine.current.episode.candle.close.value
tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value
/* Position Statistics & Results */
updateStatistics()
updateResults()
/*
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)
}
function applyStopLossFormula(formulas, formulaId) {
tradingEngine.current.position.stopLoss.value = formulas.get(formulaId)
}
function applyTakeProfitFormula(formulas, formulaId) {
tradingEngine.current.position.takeProfit.value = formulas.get(formulaId)
}
function updateStopLoss(phase) {
tradingEngine.current.position.stopLoss.stopLossPhase.value = phase
}
function updateTakeProfit(phase) {
tradingEngine.current.position.takeProfit.takeProfitPhase.value = phase
}
function initialTargets(tradingSystemStageNode, tradingEngineStageNode) {
if (tradingSystemStageNode.initialTargets === undefined) {
const message = 'Stage without Initial Targets node. Add one please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode)
}
setTargetRate()
setTargetSize()
function setTargetRate() {
if (tradingSystemStageNode.initialTargets.targetRate === undefined) {
const message = 'Target Rate Node not Found. Fix this please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets)
}
if (tradingSystemStageNode.initialTargets.targetRate.formula === undefined) {
const message = 'Formula of Target Rate Node not Found. Fix this please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate)
}
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, tradingSystemStageNode.initialTargets.targetRate.formula)
}
if (isNaN(value)) {
const message = 'Target Rate must be a number. Fix this please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetRate.formula)
}
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.entryTargetRate.value = global.PRECISE(value, 10)
break
}
case 'Close Stage': {
tradingEngine.current.position.exitTargetRate.value = global.PRECISE(value, 10)
break
}
}
}
function setTargetSize() {
/* Basic Validation */
if (
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, tradingSystemStageNode)
}
/* Position In Base Asset */
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, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
}
if (value === undefined) {
const message = 'Target Size In Base Asset cannot be zero. Fix this please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula)
}
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value =
global.PRECISE(value * tradingEngine.current.position.entryTargetRate.value, 10)
break
}
case 'Close Stage': {
tradingEngine.current.position.positionBaseAsset.exitTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionQuotedAsset.exitTargetSize.value =
global.PRECISE(value * tradingEngine.current.position.exitTargetRate.value, 10)
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, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset)
}
}
/* Position In Quoted Asset */
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, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
if (value === undefined) {
const message = 'Target Size In Quoted Asset cannot be zero. Fix this please.'
badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
switch (tradingSystemStageNode.type) {
case 'Open Stage': {
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value =
global.PRECISE(value / tradingEngine.current.position.entryTargetRate.value, 10)
break
}
case 'Close Stage': {
tradingEngine.current.position.positionQuotedAsset.exitTargetSize.value = global.PRECISE(value, 10)
tradingEngine.current.position.positionBaseAsset.exitTargetSize.value =
global.PRECISE(value / tradingEngine.current.position.exitTargetRate.value, 10)
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, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula)
}
}
}
}
function updateEnds() {
if (tradingEngine.current.position.status.value === 'Open') {
tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.end.value
tradingEngine.current.position.endRate.value = tradingEngine.current.episode.candle.close.value
tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value
}
}
function resetTradingEngineDataStructure() {
if (tradingEngine.current.position.status.value === 'Closed') {
tradingEngineModule.initializeNode(tradingEngine.current.position)
}
}
function updateCounters() {
if (tradingEngine.current.position.status.value === 'Open') {
tradingEngine.current.position.positionCounters.periods.value++
}
}
function updateStatistics() {
/* Profit Loss Calculation */
tradingEngine.current.position.positionStatistics.profitLoss.value =
(
tradingEngine.current.episode.episodeBaseAsset.balance.value * tradingEngine.current.position.endRate.value +
tradingEngine.current.episode.episodeQuotedAsset.balance.value
) -
(
tradingEngine.current.position.positionBaseAsset.beginBalance * tradingEngine.current.position.beginRate.value +
tradingEngine.current.position.positionQuotedAsset.beginBalance
)
tradingEngine.current.position.positionStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionStatistics.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionStatistics.ROI.value =
(
tradingEngine.current.position.positionStatistics.profitLoss.value
)
* 100 /
(
tradingEngine.current.position.positionBaseAsset.beginBalance * tradingEngine.current.position.beginRate.value +
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 {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Fail'
}
/* 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)
}
function updateResults() {
/* 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.position.positionBaseAsset.entryTargetSize.value
tradingEngine.current.position.positionQuotedAsset.ROI.value =
tradingEngine.current.position.positionQuotedAsset.profitLoss.value * 100 /
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.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 {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Fail'
}
}
function badDefinitionUnhandledException(err, message, node) {
tradingSystem.errors.push([node.id, message])
logger.write(MODULE_NAME, "[ERROR] -> " + message);
logger.write(MODULE_NAME, "[ERROR] -> node.name = " + node.name);
logger.write(MODULE_NAME, "[ERROR] -> node.type = " + node.type);
logger.write(MODULE_NAME, "[ERROR] -> node.config = " + JSON.stringify(node.config));
if (err !== undefined) {
logger.write(MODULE_NAME, "[ERROR] -> err.stack = " + err.stack);
}
throw 'Please fix the problem and try again.'
}
}