UNPKG

@ixily/activ

Version:

Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.

595 lines 26.4 kB
"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