Skip to content

Instantly share code, notes, and snippets.

@jondubois
Last active May 10, 2020 18:33
Show Gist options
  • Save jondubois/910824286cc7d217fb0a70a23d0de3e8 to your computer and use it in GitHub Desktop.
Save jondubois/910824286cc7d217fb0a70a23d0de3e8 to your computer and use it in GitHub Desktop.
leasehold-deflation-simulation.js
const SIMULATION_DAYS = 366 * 5;
const MAX_USER_COUNT = 10000;
const INITIAL_PROPERTY_PORTFOLIO_VALUE_DOLLARS = 10000000;
const ANNUAL_PROPERTY_PORTFOLIO_VALUE_GROWTH = .05;
// const ANNUAL_PROPERTY_PORTFOLIO_VALUE_GROWTH = 0;
const ANNUAL_YIELD = 0.2;
const ANNUAL_PROFIT = INITIAL_PROPERTY_PORTFOLIO_VALUE_DOLLARS * ANNUAL_YIELD;
const INITIAL_TOTAL_TOKEN_SUPPLY = 100000000;
const INITIAL_TOKEN_PRICE_DOLLARS = INITIAL_PROPERTY_PORTFOLIO_VALUE_DOLLARS / INITIAL_TOTAL_TOKEN_SUPPLY;
// const INITIAL_TOKEN_PRICE_DOLLARS = 0.01;
const BUYBACK_RATIO = .8;
// 5% probability or higher gives stable daily prices. Lower than that can create volatility.
const USER_DAILY_SELL_PROBABILITY = .05;
// Higher divisor means users sell fewer tokens on average (they HODL more).
const USER_SALE_QTY_DIVISOR = 4;
const INITIAL_DAILY_BURN_DOLLARS = ANNUAL_PROFIT * BUYBACK_RATIO / 366;
const DAILY_PORTFOLIO_VALUE_GROWTH_MULTIPLIER = ANNUAL_PROPERTY_PORTFOLIO_VALUE_GROWTH / 366 + 1;
const USER_BASE_SELL_PERCENT_GRADIENT = 1 / MAX_USER_COUNT / USER_SALE_QTY_DIVISOR;
let userSellToBurnFunction = (dayIndex, user, allUsers, price, prevPrice) => {
let percentOfBalanceToSell = Math.min(1, USER_BASE_SELL_PERCENT_GRADIENT * user.id);
let percentChanceToSell = USER_DAILY_SELL_PROBABILITY / 2;
let rand = Math.random();
if (rand < percentChanceToSell) {
let amountToSell = percentOfBalanceToSell * user.tokenBalance;
user.dollarBalance += amountToSell * price;
return amountToSell;
}
return 0;
};
let userSellToOtherUserFunction = (dayIndex, user, allUsers, price, prevPrice) => {
let percentOfBalanceToSell = Math.min(1, USER_BASE_SELL_PERCENT_GRADIENT * user.id);
let percentChanceToSell = USER_DAILY_SELL_PROBABILITY / 2;
let rand = Math.random();
if (rand < percentChanceToSell) {
let amountToSell = percentOfBalanceToSell * user.tokenBalance;
user.tokenBalance -= amountToSell;
user.dollarBalance += amountToSell * price;
let buyer = allUsers[Math.floor(Math.random() * allUsers.length)];
buyer.tokenBalance += amountToSell;
buyer.dollarBalance -= amountToSell * price;
}
};
let totalSupply = INITIAL_TOTAL_TOKEN_SUPPLY;
let lastPrice = INITIAL_TOKEN_PRICE_DOLLARS;
let prevPrice = lastPrice;
let users = [];
let propertyPortfolioValue = INITIAL_PROPERTY_PORTFOLIO_VALUE_DOLLARS;
const INITIAL_MARKET_CAP = totalSupply * lastPrice;
let supplyToDistribute = totalSupply;
// The equation used mirrors real global wealth distribution.
for (let i = 0; i < MAX_USER_COUNT; i++) {
let tokenBalance = Math.min(supplyToDistribute, Math.max(0, 3500 / (i / 3500 + .002) + 1000));
supplyToDistribute -= tokenBalance;
users.push({
id: i,
address: `${i}LSH`,
tokenBalance,
dollarBalance: 0
});
}
let dailyPrices = [];
let marketCap = totalSupply * lastPrice;
let burnDollarsForDay = INITIAL_DAILY_BURN_DOLLARS;
for (let i = 0; i < SIMULATION_DAYS; i++) {
let totalSoldForDay = 0;
for (let user of users) {
let numberOfTokensToSell = userSellToBurnFunction(i, user, users, lastPrice, prevPrice);
let amountToSell = Math.min(user.tokenBalance, numberOfTokensToSell);
user.tokenBalance -= amountToSell;
totalSoldForDay += amountToSell;
// To appoximate users selling to each other.
userSellToOtherUserFunction(i, user, users, lastPrice, prevPrice);
}
totalSupply -= totalSoldForDay;
if (totalSoldForDay > 0) {
prevPrice = lastPrice;
lastPrice = burnDollarsForDay / totalSoldForDay;
}
burnDollarsForDay *= DAILY_PORTFOLIO_VALUE_GROWTH_MULTIPLIER;
propertyPortfolioValue *= DAILY_PORTFOLIO_VALUE_GROWTH_MULTIPLIER;
marketCap = totalSupply * lastPrice;
console.log(`DAY ${i} TOTAL SOLD:`, totalSoldForDay, 'PRICE:', lastPrice);
dailyPrices.push(lastPrice);
}
console.log('---------------');
for (let i = 999; i >= 0; i--) {
console.log(`USER ${i}`, 'Token balance:', users[i].tokenBalance, 'Net worth:', `$${users[i].tokenBalance * lastPrice + users[i].dollarBalance}`);
}
console.log('---------------');
const formatThousands = function (num, precision, separator) {
if (precision == null) {
precision = 2;
}
let sign = num < 0 ? '-' : '';
num = Math.abs(num);
let pow = 10 ** precision;
num = Math.round(num * pow) / pow;
separator = separator || ',';
let numParts = [];
let paddingZero = '0';
let fractionDecimals = num.toString().split('.')[1] || '';
if (fractionDecimals.length) {
fractionDecimals = '.' + fractionDecimals;
}
let remaining = Math.floor(num);
if (remaining === 0) {
return remaining + fractionDecimals;
}
let lastDigits;
while (remaining !== 0) {
lastDigits = (remaining % 1000).toString();
remaining = Math.floor(remaining / 1000);
if (remaining !== 0) {
lastDigits = paddingZero.repeat(3 - lastDigits.length) + lastDigits;
}
numParts.push(lastDigits);
}
return sign + numParts.reverse().join(separator) + fractionDecimals;
}
console.log('INITIAL PROPERTY PORTFOLIO VALUE:', `$${formatThousands(INITIAL_PROPERTY_PORTFOLIO_VALUE_DOLLARS, 0)}`);
console.log('INITIAL MARKET CAP:', `$${formatThousands(INITIAL_MARKET_CAP, 0)}`);
console.log('DAILY BURN DOLLARS:', `$${formatThousands(INITIAL_DAILY_BURN_DOLLARS, 2)}`);
console.log('---------------');
console.log('FINAL PROPERTY PORTFOLIO VALUE:', `$${formatThousands(propertyPortfolioValue, 0)}`);
console.log('FINAL TOKEN SUPPLY:', formatThousands(totalSupply, 0));
console.log('FINAL TOKEN PRICE:', `$${formatThousands(lastPrice, 4)}`);
console.log('FINAL TOKEN MARKET CAP:', `$${formatThousands(marketCap, 0)}`);
console.log('TOKEN PRICE GROWTH:', `${formatThousands(Math.round((marketCap / INITIAL_MARKET_CAP - 1) * 100), 2)}%`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment