Last active
May 10, 2020 18:33
-
-
Save jondubois/910824286cc7d217fb0a70a23d0de3e8 to your computer and use it in GitHub Desktop.
leasehold-deflation-simulation.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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