Actual Size, Size to be Filled and Fees To be Paid
This commit is contained in:
1 parent
3a500976a2
commit
7cacff86d0
10 files changed
+507
-123
No files matched your search
@@ -236,10 +236,16 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
if (tradingEngineOrder.orderBaseAsset.size === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.size === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderBaseAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeFilled === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderBaseAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesPaid === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderBaseAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.actualSize === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined', tradingEngineOrder) }
|
||||
|
||||
if (tradingEngineOrder.orderQuotedAsset.size === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.size === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderQuotedAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesPaid === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderQuotedAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.actualSize === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined', tradingEngineOrder) }
|
||||
if (tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined', tradingEngineOrder) }
|
||||
}
|
||||
|
||||
async function tryToOpenOrder(tradingEngineStage, executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName) {
|
||||
@@ -407,13 +413,15 @@ 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
|
||||
let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value
|
||||
|
||||
actualSizeSimulation()
|
||||
actualRateSimulation()
|
||||
feesToBePaidSimulation()
|
||||
sizeToBeFilledSimulation()
|
||||
percentageFilledSimulation()
|
||||
feesPaidSimulation()
|
||||
sizeFilledSimulation()
|
||||
@@ -433,14 +441,26 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
|
||||
}
|
||||
|
||||
async function actualSizeSimulation() {
|
||||
/*
|
||||
While backtesting and paper trading, we do not really need to change
|
||||
simulate an Actual Size. We will just copy to that field the
|
||||
size of the order.
|
||||
*/
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.size.value
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderQuotedAsset.size.value
|
||||
|
||||
}
|
||||
|
||||
function actualRateSimulation() {
|
||||
/* Actual Rate Simulation */
|
||||
let calculatedBasedOnTradingSystem = false
|
||||
let previousQuotedAssetActualSize
|
||||
|
||||
basedOnTradingSystem()
|
||||
basedOnSessionParameters()
|
||||
considerBestMatchWithOrderBook()
|
||||
recalculateQuotedAsset()
|
||||
recalculateActualSize()
|
||||
recalculateSizePlaced()
|
||||
|
||||
function basedOnTradingSystem() {
|
||||
@@ -513,49 +533,100 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
|
||||
}
|
||||
|
||||
function recalculateQuotedAsset() {
|
||||
function recalculateActualSize() {
|
||||
/*
|
||||
Recalculate the Quouted Asset Size based on the newly calculated Actual Rate.
|
||||
The Quoted Asset Actual Size in a backtest is equal to the Quoted Asset Size, which is related to
|
||||
the Base Asset Size by the Order Rate. Since now we have the Actual Rate, we will need to recalculate
|
||||
the Quoted Asset Actual Size using the 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)
|
||||
previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10)
|
||||
}
|
||||
|
||||
function recalculateSizePlaced() {
|
||||
/*
|
||||
Recaulculate the Size Placed Based on the newly calculated Quoted Asset Size.
|
||||
For the Quoted Asset, the Size Placed was previously calculated with the previous Actual Size (or Size when these were equal).
|
||||
Since we have just recaclcualted the Actual Size in Quoted Asset, we need to fix the Size Placed for the stage which was
|
||||
calculated with the previous value.
|
||||
*/
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
|
||||
previousQuotedAssetSize
|
||||
previousQuotedAssetActualSize
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderQuotedAsset.size.value
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
async function feesToBePaidSimulation() {
|
||||
/*
|
||||
The Fees To Be Paid are the fees that will be paid once the order is 100% Filled.
|
||||
*/
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.taker,
|
||||
100
|
||||
)
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.maker,
|
||||
100
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sizeToBeFilledSimulation() {
|
||||
/*
|
||||
The Size to Be Filled is the Actual Size minus the Fees to be Paid.
|
||||
*/
|
||||
tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value =
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value -
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid.value
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value =
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value -
|
||||
tradingEngineOrder.orderQuotedAsset.feesToBePaid.value
|
||||
}
|
||||
|
||||
function percentageFilledSimulation() {
|
||||
/*
|
||||
Order Filling Simulation: the way we are going to handle this is the following.
|
||||
For buy orders we are going to check if the price was below the order rate, and
|
||||
for sell orders we are going to check if the price was above the order rate.
|
||||
When we are live, it is the exchange who knows which order was filled and in
|
||||
which amount. Since we are backtesting here, we need to simulate what happens
|
||||
at the exchange. We can not simulate that an order is filled if the candle we
|
||||
can see did not touch the order.
|
||||
|
||||
For buy orders we are going to check if the price was below the order actual rate, and
|
||||
for sell orders we are going to check if the price was above the order actual rate.
|
||||
|
||||
If we have a partial filling defined we are going to use it to estimate how much
|
||||
of the order we will consider filled.
|
||||
If we have a partial filling defined specifically for this order,
|
||||
we are going to use it to estimate how much of the order we will consider filled.
|
||||
*/
|
||||
let orderWasHit
|
||||
switch (tradingSystemOrder.type) {
|
||||
case 'Limit Buy Order': {
|
||||
if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.rate.value) {
|
||||
if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.actualRate.value) {
|
||||
orderWasHit = true
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Limit Sell Order': {
|
||||
if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.rate.value) {
|
||||
if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.actualRate.value) {
|
||||
orderWasHit = true
|
||||
}
|
||||
break
|
||||
@@ -590,7 +661,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
}
|
||||
/*
|
||||
If there is no definition for this at the trading system we will assume orders
|
||||
are 100% filled every single time.
|
||||
are 100% filled every single time they are hit, which is the most likely thing to happen.
|
||||
*/
|
||||
if (calculatedBasedOnTradingSystem === false) {
|
||||
/* We will assume that 100% of the order was filled */
|
||||
@@ -626,7 +697,13 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
if (tradingSystemOrder.simulatedExchangeEvents !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) {
|
||||
if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) {
|
||||
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage)
|
||||
applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage,
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -637,24 +714,41 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
*/
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker)
|
||||
applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.taker,
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
)
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker)
|
||||
applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.maker,
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sizeFilledSimulation() {
|
||||
/* Size Filled */
|
||||
/*
|
||||
Size Filled is the amount of Base Asset or Quoted Asset that at the exchange matches
|
||||
another counterparty order. We alredy have simulated the Percentage Filled and the
|
||||
Size To Be Filled, which is the order Actual Size minus the Fees To Be Paid.
|
||||
*/
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.sizeFilled.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.sizeFilled.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10)
|
||||
@@ -704,7 +798,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
await updateEndsWithCycle(tradingEngineOrder)
|
||||
tradingSystem.infos.push([tradingSystemOrder.id, 'checkExchangeEvents -> Closing Order with Exit Type ' + tradingEngineOrder.exitType.value])
|
||||
|
||||
|
||||
await syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order)
|
||||
return
|
||||
}
|
||||
@@ -769,8 +863,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
async function syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order) {
|
||||
|
||||
let previousBaseAssetSize = tradingEngineOrder.orderBaseAsset.size.value
|
||||
let previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value
|
||||
let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value
|
||||
let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value
|
||||
let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value
|
||||
@@ -778,6 +870,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
await actualSizeCalculation()
|
||||
await actualRateCalculation()
|
||||
await feesToBePaidCalculation()
|
||||
await sizeToBeFilledCalculation()
|
||||
await percentageFilledCalculation()
|
||||
await feesPaidCalculation()
|
||||
await sizeFilledCalculation()
|
||||
@@ -797,46 +891,63 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
async function actualSizeCalculation() {
|
||||
/*
|
||||
When we submit the order to the exchange, we do it specifying the order size
|
||||
in Base Asset. The exchange might take that size or might change it a little
|
||||
bit if for example, if it does not accept as many decimals as we are sending.
|
||||
in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API.
|
||||
|
||||
The exchange might take that size or might change it a little
|
||||
bit. For example, if it does not accept as many decimals as we are sending.
|
||||
If we identify the situation in which the size accepted is different than the
|
||||
size we have accounted for, we need to make several adjustments so that the
|
||||
accounting syncronizes with reality.
|
||||
*/
|
||||
if (tradingEngineOrder.orderBaseAsset.size.value === order.amount) { return }
|
||||
/*
|
||||
We will sync the Base Asset size, and recalculate the Quoted Asset size too.
|
||||
|
||||
/* This calculation only happens once, the first time the order is checked. */
|
||||
if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return }
|
||||
|
||||
/* We receive the actual size from the exchange at the order.amount field. */
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount
|
||||
|
||||
/*
|
||||
If the exchange accepted the size sent on the request to create the order,
|
||||
we don't need to do anything else here.
|
||||
*/
|
||||
tradingEngineOrder.orderBaseAsset.size.value = order.amount
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value
|
||||
|
||||
tradingEngineOrder.orderBaseAsset.size.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.size.value, 10)
|
||||
tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10)
|
||||
if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return }
|
||||
/*
|
||||
Finaly we need to unaccount the previous size placed and correctly account
|
||||
for the the new sizes we now have.
|
||||
*/
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value -
|
||||
previousBaseAssetSize
|
||||
If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received
|
||||
is in Base Asset only. In the next formula, we are useing the rate of the order.
|
||||
This rate might be replaced afteerwards for an actual rate. When that happens we will
|
||||
need to adjust these results.
|
||||
*/
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value
|
||||
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderBaseAsset.size.value
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10)
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10)
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
|
||||
previousQuotedAssetSize
|
||||
recalculateSizePlaced()
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderQuotedAsset.size.value
|
||||
function recalculateSizePlaced() {
|
||||
/*
|
||||
Wee also need to unaccount the previous size placed and correctly account
|
||||
for the the new actual sizes we now have.
|
||||
*/
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value -
|
||||
tradingEngineOrder.orderBaseAsset.size.value
|
||||
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10)
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value
|
||||
|
||||
previousBaseAssetSize = tradingEngineOrder.orderBaseAsset.size.value
|
||||
previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
|
||||
tradingEngineOrder.orderQuotedAsset.size.value
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10)
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
async function actualRateCalculation() {
|
||||
@@ -866,56 +977,124 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
|
||||
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
|
||||
|
||||
/*
|
||||
If the Actual Rate happens to be exctly the same than the order rate, then there is
|
||||
nothing else to do here. Otherwise there are adjustments to be made.
|
||||
*/
|
||||
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return }
|
||||
/*
|
||||
Now we know the Actual Rate at which the order was filled. Since the actual rate
|
||||
is not 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
|
||||
let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
recalculateActualSize()
|
||||
recalculateSizePlaced()
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
function recalculateActualSize() {
|
||||
/*
|
||||
Now we know the Actual Rate at which the order was filled. Since the actual rate
|
||||
is not the same as the Rate we defined for the order, we need to syncronize
|
||||
the Actual Order Size for Quoted Asset since it was calculated with the Order Size that we
|
||||
now know it is not the one really used at the exchange. We will recalculate the
|
||||
Actual Size in Quoted Asset and not in Base Asset, since the Actual Size in Base Asset
|
||||
is the one we accepted by the exchange, so that is fixed, regardless of how we initially
|
||||
got the Order Size in Base Asset, either 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.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10)
|
||||
}
|
||||
|
||||
function recalculateSizePlaced() {
|
||||
/*
|
||||
Since we changed the Actual Size in Quoted Asset, we need to fix the Size Placed in Quoted Asset
|
||||
that was previously calculated with the previous value of actualSize.
|
||||
*/
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
|
||||
previousQuotedAssetActualSize
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value +
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
}
|
||||
}
|
||||
|
||||
async function feesToBePaidCalculation() {
|
||||
/*
|
||||
The Fees To Be Paid are the fees that will be paid once the order is 100% Filled.
|
||||
*/
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.taker,
|
||||
100
|
||||
)
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.maker,
|
||||
100
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sizeToBeFilledCalculation() {
|
||||
/*
|
||||
The Size to Be Filled is the Actual Size minus the Fees to be Paid.
|
||||
*/
|
||||
tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value =
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value -
|
||||
tradingEngineOrder.orderBaseAsset.feesToBePaid.value
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value =
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value -
|
||||
tradingEngineOrder.orderQuotedAsset.feesToBePaid.value
|
||||
}
|
||||
|
||||
async function percentageFilledCalculation() {
|
||||
/* Percentage Filled Calculation */
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
|
||||
/*
|
||||
Percentage Filled Calculation: The only information we get from the exchange is the order.filled.
|
||||
We know this field will go up until reaching the Size To Be Filled. Once it reaches that number
|
||||
the order will be 100% filled.
|
||||
*/
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value)
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
|
||||
}
|
||||
|
||||
async 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.
|
||||
are buying or selling. Within the order information received from the exchange we can not see the fees paid so we need to
|
||||
calculate them ourselves. To do this it is needed to be used the Session Fees Parameters.
|
||||
*/
|
||||
switch (tradingEngineOrder.type) {
|
||||
case 'Market Order': {
|
||||
await applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker)
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.taker,
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
)
|
||||
break
|
||||
}
|
||||
case 'Limit Order': {
|
||||
await applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker)
|
||||
await applyFeePercentage(
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid,
|
||||
tradingEngineOrder,
|
||||
tradingSystemOrder,
|
||||
sessionParameters.feeStructure.config.maker,
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -936,7 +1115,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
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.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
|
||||
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10)
|
||||
}
|
||||
@@ -1003,65 +1182,65 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
/* 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.
|
||||
Let's remember that for Buy orders, the fees are paid in Base Asset.So we
|
||||
need to substract from the balance of Quoted Asset both the size filled and
|
||||
the fees, and we will receive at the balance of Base Asset just the size filled,
|
||||
bacuase the exchange gets the fees in Base Asset for them.
|
||||
*/
|
||||
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 +
|
||||
previousBaseAssetFeesPaid
|
||||
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 -
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.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
|
||||
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.sizeFilled.value -
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.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.
|
||||
For Sell orders, the fees are being paid in Quote Asset, so we will substract
|
||||
from the Base Asset balance the size filled and the fees, and we will add
|
||||
to the Quoted Asset balance only the size filled, because the exchange
|
||||
get those fees.
|
||||
*/
|
||||
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
|
||||
previousBaseAssetSizeFilled +
|
||||
previousBaseAssetFeesPaid
|
||||
|
||||
/* 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
|
||||
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 -
|
||||
previousQuotedAssetSizeFilled +
|
||||
previousQuotedAssetFeesPaid
|
||||
previousQuotedAssetSizeFilled
|
||||
|
||||
/* 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
|
||||
tradingEngineOrder.orderQuotedAsset.sizeFilled.value
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1164,15 +1343,15 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
Size Placed accumulates for each asset, the order size placed at the exchange. A cancelation means that
|
||||
only the part filled can be considered placed, so we need to substract from the stage size
|
||||
the remainder. To achieve this with the information we currently have, we are going first
|
||||
to unaccount the order size, and the account only the sizeFilled + the feesPaid.
|
||||
to unaccount the order actual size, and the account only the sizeFilled + the feesPaid.
|
||||
*/
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value -
|
||||
tradingEngineOrder.orderBaseAsset.size.value
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value
|
||||
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value -
|
||||
tradingEngineOrder.orderQuotedAsset.size.value
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value
|
||||
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value =
|
||||
tradingEngineStage.stageBaseAsset.sizePlaced.value +
|
||||
@@ -1186,26 +1365,26 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM
|
||||
tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10)
|
||||
}
|
||||
|
||||
async function applyFeePercentage(tradingEngineOrder, tradingSystemOrder, feePercentage) {
|
||||
async function applyFeePercentage(feesNode, tradingEngineOrder, tradingSystemOrder, feePercentage, percentageFilled) {
|
||||
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.
|
||||
In this case the fees are taken from the Base Asset you receive as a result of the trade at the exchange.
|
||||
*/
|
||||
tradingEngineOrder.orderBaseAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderBaseAsset.size.value *
|
||||
feesNode.value =
|
||||
tradingEngineOrder.orderBaseAsset.actualSize.value *
|
||||
feePercentage / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
percentageFilled / 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.
|
||||
In this case the fees are taken from the Quoted Asset you receive as a result of the trade at the exchange.
|
||||
*/
|
||||
tradingEngineOrder.orderQuotedAsset.feesPaid.value =
|
||||
tradingEngineOrder.orderQuotedAsset.size.value *
|
||||
tradingEngineOrder.orderQuotedAsset.actualSize.value *
|
||||
feePercentage / 100 *
|
||||
tradingEngineOrder.orderStatistics.percentageFilled.value / 100
|
||||
percentageFilled / 100
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +143,13 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
the Root Node specifically for this property only.
|
||||
*/
|
||||
if (recordProperty.config.nodePath !== undefined) {
|
||||
if ( recordProperty.config.nodePath === "tradingEngine.exchangeOrders.marketBuyOrders.marketOrders[index].orderBaseAsset") {
|
||||
let a = tradingEngine
|
||||
}
|
||||
try {
|
||||
propertyRootNode = eval(recordProperty.config.nodePath)
|
||||
} catch (err) {
|
||||
badDefinitionUnhandledException(err, 'Error Evaluation Record Property nodePath -> nodePath = ' + recordProperty.config.nodePath, product, recordProperty)
|
||||
badDefinitionUnhandledException(err, 'Error Evaluating Record Property nodePath -> nodePath = ' + recordProperty.config.nodePath, product, recordProperty)
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
@@ -891,8 +891,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
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.
|
||||
|
||||
Note that the comparison is made with a Rounding Factor in order to avoid rounding problems.
|
||||
*/
|
||||
const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001
|
||||
@@ -923,9 +925,11 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function positionFilled() {
|
||||
changeStageStatus(stageName, 'Closed', 'Position Size Filled')
|
||||
}
|
||||
|
||||
function checkCloseStageEvent(tradingSystemStage) {
|
||||
/* Check the Close Stage Event */
|
||||
tradingSystem.evalConditions(tradingSystemStage, 'Close Stage Event')
|
||||
|
||||
Reference in new issue
Block a user