@ixily/activ
Version:
Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.
595 lines • 26.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IdeaPerformanceModule = exports.enrichTrades = exports.calculateProfitTranches = exports.calculateResultProcess = void 0;
const getIdeaPerformance = (tradeIdeas) => {
const openRecommendation = tradeIdeas[0];
const adjustRecommendations = tradeIdeas.slice(1, -1);
let closeRecommendation = tradeIdeas[tradeIdeas.length - 1];
if (closeRecommendation.idea
.kind !== 'close') {
adjustRecommendations.push(closeRecommendation);
closeRecommendation = undefined;
}
const calc = (0, exports.calculateResultProcess)(openRecommendation, adjustRecommendations, closeRecommendation);
// console.log('post calculateResultProcess');
// log('getCalculateResult (calc)', calc);
// const userReference = data.userReference;
// const assetId = data.assetId;
// const ticker = data.ticker;
// await calculateAssetProfit(userReference, assetId, ticker, dbConnectionString);
// const calculationStr = calculation.toString();
// encrich the calcs
const enrichedCals = (0, exports.enrichTrades)(calc.calculation, adjustRecommendations);
let enrichedCalsHandlered = enrichedCals;
// if (!storeResult) {
// enrichedCalsHandlered = enrichedCals?.filter(
// (res) => res.note !== 'Position closed',
// )
// }
const result = {
isRebalance: false,
summary: calc.profit,
calculation: enrichedCalsHandlered,
};
return result;
};
const calculateResultProcess = (openRecommendation, adjustRecommendations, closeRecommendation) => {
const calculation = [];
// to start we get the price from the open
// console.log('openRecommendation')
// console.log(openRecommendation)
// console.log('openRecommendation?.idea')
// console.log(openRecommendation?.idea)
const openIdea = openRecommendation.idea;
if (openIdea.kind !== 'open') {
throw new Error('Calculate Performance Error: openIdea.kind !== open: Did your idea contains the correct strategy reference and strategy have an open idea on this ticker?');
}
// console.log('openIdea.trade')
// console.log(openIdea.trade)
let closeIdea;
if (closeRecommendation?.idea !== undefined) {
closeIdea =
closeRecommendation?.idea;
}
else {
closeIdea = {
...adjustRecommendations[adjustRecommendations.length - 1]
.idea,
};
closeIdea.kind = 'close';
delete closeIdea.adjustment;
}
const direction = openIdea.trade.direction;
const openAsk = openIdea.priceInfo.price.ask || openIdea.priceInfo.price.globalPrice;
const openBid = openIdea.priceInfo.price.bid || openIdea.priceInfo.price.globalPrice;
const rightOpenPrice = direction === 'long' ? openAsk : openBid;
const openPrice = Number(rightOpenPrice || 0);
// console.log('openRecommendation', openRecommendation);
// console.log('direction', direction);
let tradeType = direction == 'long' ? 'buy' : 'sell';
// lets include the conviction
const openObj = {
qty: 1,
price: openPrice,
direction,
description: 'init',
tranches: [
{
qty: 1,
price: openPrice,
action: tradeType,
},
],
return: {
iValue: 1,
buys: 0,
sells: 0,
cash: 1,
},
};
if (direction === 'long') {
openObj.return.buys = 1;
}
else {
openObj.return.sells = 1;
}
// add this to our calculation
calculation.push(openObj);
// update any
if (adjustRecommendations.length > 0) {
for (const i in adjustRecommendations) {
if (i) {
// get the size of the existing position
const currentPosition = calculation[calculation.length - 1].qty || 0;
const currentDirection = calculation[calculation.length - 1].direction;
// adjustment info
// console.log(
// 'adjustRecommendations[i].idea!.idea as ITradeIdeaIdea:',
// )
// console.log(
// adjustRecommendations[i].idea!.idea as ITradeIdeaIdea,
// )
const adjustment = adjustRecommendations[i]
.idea;
const adjustmentType = adjustment.adjustment.kind;
const adjustmentPercentage = adjustment.adjustment.percentage;
const adjustmentAsk = adjustment.priceInfo.price.ask ||
adjustment.priceInfo.price.globalPrice;
const adjustmentBid = adjustment.priceInfo.price.bid ||
adjustment.priceInfo.price.globalPrice;
const adjustmentPrice = direction === 'long'
? adjustmentType === 'increase'
? adjustmentAsk
: adjustmentBid
: adjustmentType === 'increase'
? adjustmentBid
: adjustmentAsk;
// oneIndexedCala
// we need to work out the percentage change
const previousPrice = calculation[calculation.length - 1].price;
// console.log('1 Indx: previousPrice', previousPrice);
const currentPrice = adjustmentPrice;
// console.log('1 Indx: currentPrice', currentPrice);
const priceChange = (currentPrice - previousPrice) / previousPrice;
// console.log('1 Indx: priceChange', priceChange);
const newOneIndexedValue = calculation[calculation.length - 1].return.iValue +
calculation[calculation.length - 1].return.iValue *
priceChange;
// console.log('1 Indx: newOneIndexedValue', newOneIndexedValue);
switch (adjustmentType) {
case 'increase':
// we're increasing the size of the position
// console.log('current position', currentPosition);
// console.log('adjustmentPercentage', adjustmentPercentage);
const increasedAmt = (adjustmentPercentage / 100) * currentPosition;
// console.log('increasedAmt', increasedAmt);
// const val = increasedAmt * currentPosition;
const increaseOneIndexedValue = (adjustmentPercentage / 100) * newOneIndexedValue;
// console.log('1 Indx: nincreaseOneIndexedValue', increaseOneIndexedValue);
// what do we format the qty to?
const newQty = currentPosition + increasedAmt;
// const newVal = newQty * adjustmentPrice;
tradeType = direction === 'long' ? 'buy' : 'sell';
// our actual trade object
const tradeObj = {
qty: increasedAmt,
price: adjustmentPrice,
action: tradeType,
};
// add our trade object to the tranches
// console.log('push into trances', tradeObj);
const currentTranchesIncrease = [
...calculation[calculation.length - 1].tranches,
];
currentTranchesIncrease.push(tradeObj);
// now adjust our current oneIndexedValue for the price change
// const currentOneIndexedValue = (priceChange *
// oneIndexedValue = (Number(tranches[t].price - tranches[count - 1].value) / tranches[count - 1].value;
// // now this value has been increased or decreased
// if (tranches[t].AdjustmentType === 'increase') {
// oneIndexedValue *
// }
// oneIndexedCala
// we need to work out the percentage change
// currentValue - previousValue
// const previousPrice = tranches[count - 1].price;
// const currentPrice = tranches[t].price;
// const priceChange = (currentPrice - previousPrice) / previousPrice;
// the extra 'cash' a user puts in is the % adjustment of our current position
const newCash = (Number(adjustmentPercentage) / 100) *
Number(increaseOneIndexedValue);
// console.log('new cash', newCash);
const totalCash = Number(newCash) +
Number(calculation[calculation.length - 1].return.cash);
// console.log('Number(calculation[calculation.length - 1].return.buys)', Number(calculation[calculation.length - 1].return.buys));
const totalBuys = Number(calculation[calculation.length - 1].return.buys) + Number(newCash);
// console.log('totalBuys', totalBuys);
const newReturn = {
iValue: increaseOneIndexedValue,
buys: totalBuys,
sells: Number(calculation[calculation.length - 1].return
.sells),
cash: totalCash,
};
// console.log('return in increase', newReturn);
// put it all together
const recObj = {
qty: newQty,
price: adjustmentPrice,
direction: currentDirection,
description: 'increase',
tranches: currentTranchesIncrease,
return: newReturn,
};
calculation.push(recObj);
break;
case 'decrease':
// we're increasing the size of the position
const decreaseAmt = (adjustmentPercentage / 100) * currentPosition;
const decreaseOneIndexedValue = (adjustmentPercentage / 100) * newOneIndexedValue;
// console.log('1 Indx: decreaseOneIndexedValue', decreaseOneIndexedValue);
// what do we format the qty to?
const newQtyD = currentPosition - decreaseAmt;
tradeType = direction === 'long' ? 'buy' : 'sell';
const reverseTradeType = tradeType === 'buy' ? 'sell' : 'buy';
// our actual trade object
const tradeObjD = {
qty: decreaseAmt,
price: adjustmentPrice,
action: reverseTradeType,
};
// add our trade object to the tranches
// console.log('push into trances', tradeObj);
const currentTranchesDecrease = [
...calculation[calculation.length - 1].tranches,
];
currentTranchesDecrease.push(tradeObjD);
const totalSells = Number(calculation[calculation.length - 1].return
.sells) + Number(decreaseOneIndexedValue);
// note that we don't deduct the cash withdrawn as the user has already 'invested' a certain amount of cash
const updatedReturn = {
iValue: decreaseOneIndexedValue,
buys: Number(calculation[calculation.length - 1].return
.buys),
sells: totalSells,
cash: calculation[calculation.length - 1].return
.cash,
};
// console.log('return in decrease', updatedReturn);
// put it all together
const recObjD = {
qty: newQtyD,
price: adjustmentPrice,
direction: currentDirection,
description: 'decrease',
tranches: currentTranchesDecrease,
return: updatedReturn,
};
calculation.push(recObjD);
break;
}
}
}
}
// console.log('calculations object final', calculation);
// console.log('closeRecommendation', closeRecommendation);
// if (closeRecommendation) { // <= always true
// console.log('closeRecommendation', closeRecommendation);
// we get the close qty from the previous row qty
const totalPosition = calculation[calculation.length - 1].qty || 0;
const closeAsk = closeIdea.priceInfo.price.ask ||
closeIdea.priceInfo.price.globalPrice;
const closeBid = closeIdea.priceInfo.price.ask ||
closeIdea.priceInfo.price.globalPrice;
const closePrice = (direction === 'long' ? closeBid : closeAsk) || 0;
const existingTranches = [...calculation[calculation.length - 1].tranches];
// this gets the reverse direction
// console.log('initial trade idea open direction', direction);
// console.log('closeTradeType', closeTradeType);
const reverseCloseTradeType = direction === 'long' ? 'sell' : 'buy';
// console.log('reverseCloseTradeType', reverseCloseTradeType);
// oneIndexedCala
// we need to work out the percentage change
const previousPrice = calculation[calculation.length - 1].price;
const currentPrice = closePrice;
const priceChange = ((currentPrice - previousPrice) / previousPrice) * 100;
// console.log('previousPrice', previousPrice);
// console.log('currentPrice', currentPrice);
// console.log('priceChange', priceChange);
// console.log('calculation.length', calculation.length);
// console.log('calculation[calculation.length - 1]', calculation[calculation.length - 1]);
// console.log('Number(calculation[calculation.length - 1].iValue)', Number(calculation[calculation.length - 1].return.iValue));
// this is 0
// const newOneIndexedValue = 0;
const newOneIndexedValue = Number(calculation[calculation.length - 1].return.iValue) +
Number(calculation[calculation.length - 1].return.iValue) *
Number(priceChange);
// console.log('newOneIndexedValue', newOneIndexedValue);
const tradeObjClose = {
qty: totalPosition,
price: closePrice,
action: reverseCloseTradeType,
};
// console.log('tradeObjClose', tradeObjClose);
existingTranches.push(tradeObjClose);
// console.log('existingTranches', existingTranches);
// if this is a long order, then we are selling
let closeBuys = 0;
let closeSells = 0;
if (direction === 'long') {
closeBuys = Number(calculation[calculation.length - 1].return.buys);
closeSells =
Number(calculation[calculation.length - 1].return.sells) +
Number(newOneIndexedValue);
}
else if (direction === 'short') {
closeBuys =
Number(calculation[calculation.length - 1].return.buys) +
Number(newOneIndexedValue);
closeSells = Number(calculation[calculation.length - 1].return.sells);
}
// note that we don't deduct the cash withdrawn as the user has already 'invested' a certain amount of cash
const updatedReturn = {
iValue: newOneIndexedValue,
buys: closeBuys,
sells: closeSells,
cash: calculation[calculation.length - 1].return.cash,
};
const closeObj = {
qty: 0,
price: closePrice,
direction,
description: 'close',
tranches: existingTranches,
return: updatedReturn,
};
// update our main array
calculation.push(closeObj);
// update the tranches with the profit
const profit = (0, exports.calculateProfitTranches)(existingTranches, closeObj.return, calculation);
// } else {
// // console.log('No close recommendation');
// }
// update our calculation with a little helpful info
// console.log('final calculation', calculation);
const response = {
calculation,
profit,
};
return response;
};
exports.calculateResultProcess = calculateResultProcess;
const calculateProfitTranches = (tranches, data, calculationArray) => {
// let profit = 0;
let value = 0;
let qty = 0;
let iValue = 0;
let totalBuys = 0;
let totalSells = 0;
let count = 0;
// loop through the tranches
for (const t in tranches) {
if (t) {
count++;
// if this isn't the open, then we have a previous price
if (tranches.length === 1) {
// profit is the same as the price of the current tranche
// tranches[t].profit = 0
qty = tranches[t].qty;
value = tranches[t].qty * tranches[t].price;
iValue = 1; // always starts with 1
}
else {
// console.log('tranche[t]', tranches[t]);
const jsDecimalFix = 1000000000;
// if a buy
if (tranches[t].action?.toLowerCase() === 'buy') {
// console.log('Buy count', count);
// console.log('Buy qty', tranches[t].qty);
// value at the current price
qty = qty + tranches[t].qty;
// console.log('buy updated qty', qty);
value =
Number(value) +
((Number(tranches[t].qty) * jsDecimalFix) /
jsDecimalFix) *
(Number(tranches[t].price * jsDecimalFix) /
jsDecimalFix);
// console.log('buy updated value', value);
// iValue = Number(value) / Number(tranches[t].price);
totalBuys = value;
}
else if (tranches[t].action?.toLowerCase() === 'sell') {
// console.log('Sell count', count);
// console.log('Sell qty', tranches[t].qty);
// value at the current price
qty = qty - tranches[t].qty;
// console.log('Sell updated qty', tranches[t].qty);
// console.log('Sell updated price', tranches[t].price);
const sellValue = (Number(tranches[t].qty) *
jsDecimalFix *
Number(tranches[t].price) *
jsDecimalFix) /
jsDecimalFix /
jsDecimalFix;
// const sellValue = Number(tranches[t].qty) * Number(tranches[t].price);
// console.log('sellValue', sellValue);
// console.log('Sell updated value', value);
value = Number(sellValue) - Number(value);
totalSells = value;
}
}
}
}
// console.log('totalQty', qty);
// console.log('totalValue', value);
// now we work out the percentage change
let profit = false;
let capital = 0;
const startAction = tranches[0].action?.toLowerCase();
// const endAction = tranches[tranches.length - 1].action?.toLowerCase()
// if the initial position was short, we need a negative number to profit
// console.log('tranches[0].action', tranches[0].action);
// console.log('value', value);
if (startAction === 'sell') {
if (Number(value) < 0) {
profit = true;
}
// max capital used
capital = totalSells;
}
else if (startAction === 'buy') {
if (Number(value) > 0) {
profit = true;
}
// max capital used
capital = totalBuys;
}
let percentage = 0;
if (tranches.length > 0) {
percentage = value / capital || 0;
}
// console.log('value', value);
// console.log('capital', capital);
// this is our fina;
// get our start price
// get our end price
let startPrice = 0;
let endPrice = 0;
let priceChangePercentage = 0;
let priceChangeSuccess = false;
let priceChangeDirection = 'up';
startPrice = tranches[0].price;
endPrice = tranches[tranches.length - 1].price;
if (Number(startPrice) > 0 && Number(endPrice)) {
const diff = endPrice - startPrice;
priceChangePercentage = diff / startPrice;
// log('calculateProfitTranches (diff)', diff);
// log('calculateProfitTranches (priceChangePercentage)', priceChangePercentage);
if (startAction === 'sell') {
if (diff < 0) {
priceChangeSuccess = true;
priceChangeDirection = 'down';
}
else {
priceChangeDirection = 'up';
}
}
else if (startAction === 'buy') {
if (diff > 0) {
priceChangeSuccess = true;
priceChangeDirection = 'up';
}
else {
priceChangeDirection = 'down';
}
}
}
const response = {
profit,
capital,
percentage,
price: {
percentage: priceChangePercentage,
direction: priceChangeDirection,
success: priceChangeSuccess,
},
};
// console.log('calculateProfitTranches (calculationArray)', calculationArray)
// console.log('calculateProfitTranches (tranches)', tranches)
/* JB CHANGES */
const lastChangesStatus = true;
if (lastChangesStatus) {
const isClosed = calculationArray?.find((res) => res?.description === 'close');
const shortCheck = isClosed !== undefined && startAction === 'sell';
if (shortCheck) {
const diff = startPrice - endPrice;
const priceChangePercentage = diff / startPrice;
response.percentage = priceChangePercentage;
response.price.percentage = priceChangePercentage;
if (diff > 0) {
response.profit = true;
response.price.direction = 'up';
}
if (diff < 0) {
response.profit = false;
response.price.direction = 'down';
}
}
}
/* JB CHANGES */
// log('calculateProfitTranches (response)', response);
return response;
};
exports.calculateProfitTranches = calculateProfitTranches;
/**
* Adds a little more detail to our data
*
* @export
*/
const enrichTrades = (calculations, adjustsMetas) => {
const adjusts = adjustsMetas.map((each) => each.idea);
// console.log('calculations', calculations);
const data = [];
// get the last of the tranches
const tranches = calculations[calculations.length - 1].tranches;
tranches.forEach((item, index) => {
let note = '';
if (index === 0) {
note = 'Position initiated';
}
else if (index === calculations.length - 1) {
note = 'Position closed';
}
else if (adjusts.length > 0) {
if (adjusts[index - 1].adjustment.kind === 'increase') {
note =
'Increase by ' +
adjusts[index - 1].adjustment.percentage.toFixed(0) +
'%';
}
else if (adjusts[index - 1].adjustment.kind === 'decrease') {
note =
'Decrease by ' +
adjusts[index - 1].adjustment.percentage.toFixed(0) +
'%';
}
}
const row = {
note,
action: item.action,
qty: item.qty,
price: item.price,
trade: item.qty * item.price,
positionQty: 0,
positionDirection: 'long',
positionValue: 0,
};
data.push(row);
});
data.forEach((row, i) => {
row.trade = Number(row.qty) * Number(row.price);
const action = row.action.toLowerCase() || '';
// console.log('i', i);
if (i === 0) {
row.positionQty = row.qty;
// row.positionValue = row.qty * row.price;
if (action === 'buy') {
row.positionDirection = 'long';
}
else if (action === 'sell') {
row.positionDirection = 'short';
}
row.positionValue = row.qty * row.price;
}
else if (action === 'buy') {
if (data[0].positionDirection === 'long') {
row.positionQty = data[i - 1].positionQty + row.qty;
row.positionValue = row.positionQty * row.price;
}
else if (data[0].positionDirection === 'short') {
row.positionQty = data[i - 1].positionQty - row.qty;
row.positionValue = row.positionQty * row.price;
}
}
else if (action === 'sell') {
if (data[0].positionDirection === 'long') {
row.positionQty = data[i - 1].positionQty - row.qty;
row.positionValue = row.positionQty * row.price;
}
else if (data[0].positionDirection === 'short') {
row.positionQty = data[i - 1].positionQty + row.qty;
row.positionValue = row.positionQty * row.price;
}
}
if (row.positionQty === 0) {
row.positionDirection = 'closed';
}
});
return data;
};
exports.enrichTrades = enrichTrades;
exports.IdeaPerformanceModule = {
getIdeaPerformance,
};
//# sourceMappingURL=idea-performance.module.js.map