Skip to content

Instantly share code, notes, and snippets.

@timotgl
Created December 25, 2021 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timotgl/670b5e6d4d6abe276c445d298bcdfa01 to your computer and use it in GitHub Desktop.
Save timotgl/670b5e6d4d6abe276c445d298bcdfa01 to your computer and use it in GitHub Desktop.
Calculate optimal amount of ETF shares to sell to take advantage of the german "Sparer-Pauschbetrag" (Node.js)
// Optimal amount of profit (in EUR) to be gained from ETF sale
const optimalProfit = 1144.29
// Sale price (bid) of one share of the ETF in EUR
const bidPrice = 103.74;
// Amount of shares I own in chronologically ascending order.
// (first element = oldest shares)
const shares = [
// amount, share price at the time in EUR
[ 30.000, 81.67],
[ 6.147, 81.34],
[ 119.000, 83.99]
];
let fractionedSharesWarning = 'None of the positions will be fractioned.';
const simulateSale = (numSharesSold) => {
// Iterate through elements of shares array
let sharesIndex = 0;
// Total profit (in EUR) earned when numSharesSold is sold at bidPrice.
let totalProfit = 0;
// Keep track of how many shares (starting with numSharesSold) we still have to sell.
let sharesSold = numSharesSold;
while (sharesSold > 0 && sharesIndex < shares.length) {
//console.log('--------------------------------------------------------------------');
//console.log('Shares left to sell:', sharesSold);
const [originalAmount, originalPrice] = shares[sharesIndex];
let amount = originalAmount;
if (amount > sharesSold) {
// The position is bigger than the number of shares we still need to sell
fractionedSharesWarning =
`Warning: The position with ${amount} ` +
`shares bought at ${originalPrice} ` +
`EUR will be reduced to ${Number((amount - sharesSold).toFixed(3))} shares.\n` +
'Following FIFO, your next sale will start with this position.';
amount = sharesSold;
}
const profit = Number(((amount * bidPrice) - (amount * originalPrice)).toFixed(2));
/*
console.log(
'Selling', amount,
'ETF shares at', bidPrice,
'EUR per share results in a profit of', profit,
'EUR'
);
*/
totalProfit += profit;
sharesSold -= amount;
sharesIndex += 1;
}
totalProfit = Number(totalProfit.toFixed(2));
//console.log('--------------------------------------------------------------------');
/*
console.log(
'Total profit:', totalProfit,
'EUR for', numSharesSold,
'shares sold. Total sale value:', Number((numSharesSold * bidPrice).toFixed(2))
);
*/
return totalProfit;
};
let numSharesToSell = 0;
let profit = 0;
while (true) {
numSharesToSell += 1;
profit = simulateSale(numSharesToSell);
if (profit >= optimalProfit) {
numSharesToSell -= 1;
profit = simulateSale(numSharesToSell);
break;
}
}
console.log(
'You should sell', numSharesToSell,
'shares to approximate but not exceed the optimal profit of', optimalProfit,
'EUR with', profit,
'EUR.'
);
console.log(fractionedSharesWarning);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment