diff --git a/econ-demo.py b/econ-demo.py index 73272ec..75e4071 100644 --- a/econ-demo.py +++ b/econ-demo.py @@ -1,6 +1,9 @@ import pandas as pd import matplotlib.pyplot as plt +import numpy as np +import random +import sys import tabulate # it = 0 @@ -226,6 +229,90 @@ def draw_apy_inflation(terms=50, linear_label="? (linear)"): plt.close() # plt.show() +def draw_apy_inflation2(terms=50, linear_label="? (linear)"): + participants = ["Alice", "Bob", "Charlie"] + balances = [100,40,20] + + df = get_balances_over_time ( + participants, balances, ubi(balances, terms=terms) + ) + total_supply_ubi = calc_total_supply(df) + + df_si = get_balances_over_time ( + participants, balances, + compounding_interest(balances, terms=terms) + ) + total_supply_si = calc_total_supply(df_si) + # + 1 because the initial frame is included this time + x = [i for i in range(1913,1913+terms+1)] + try: + assert(len(x) == len(total_supply_si)) + except AssertionError: + print("Assert Error:") + print(len(x), len(total_supply_si)) + print(df_si) + exit() + + plt.style.use('dark_background') + plt.plot( + x, total_supply_ubi, color="cyan", label="dividend = 10" + ) + plt.plot( + x, total_supply_si, color="red", label="apy = 0.05" + ) + plt.axvline(x=1960, color='yellow', linestyle='--') + plt.title("Supply of Money Over Time") + plt.legend() + plt.xlabel("Year") + plt.ylabel("Total Currency") + plt.savefig("inflation-ubi-vs-5apy2.png") + plt.close() + # plt.show() + +def draw_apy_inflation3(terms=50, linear_label="? (linear)"): + participants = ["Alice", "Bob", "Charlie"] + balances = [100,40,20] + + df = get_balances_over_time ( + participants, balances, ubi(balances, terms=terms) + ) + total_supply_ubi = calc_total_supply(df) + + df_si = get_balances_over_time ( + participants, balances, + compounding_interest(balances, terms=terms) + ) + total_supply_si = calc_total_supply(df_si) + # + 1 because the initial frame is included this time + x = [i for i in range(1913,1913+terms+1)] + try: + assert(len(x) == len(total_supply_si)) + except AssertionError: + print("Assert Error:") + print(len(x), len(total_supply_si)) + print(df_si) + exit() + dy1 = np.gradient(total_supply_ubi, x) + dy2 = np.gradient(total_supply_si, x) + + plt.style.use('dark_background') + plt.plot( + x, dy1, color="cyan", label="deriv. const.div" + ) + plt.plot( + x, dy2, color="red", label="deriv. apy" + ) + plt.axvline(x=1960, color='yellow', linestyle='--') + plt.axvline(x=1940, color='orange', linestyle='--') + plt.title("Change in Supply of Money") + plt.legend() + plt.xlabel("Year") + plt.ylabel("Rate of Inflation") + plt.savefig("inflation-ubi-vs-5apy3.png") + plt.close() + # plt.show() + + def draw_3ubi_3apy(terms=50): participants = ["Alice", "Bob", "Charlie"] balances = [100,40,20] @@ -361,59 +448,144 @@ def draw_simple_mining_demo(): # print(m.iloc[:,:5].to_markdown()) visualize_wealth_dist(m, filename="wealth-distribution-pow.png") +def trade(players, balances, spend_limit=0.10): + assert(len(players) == len(balances)) + PRICE_FLOOR = 1 + pl_idx = len(players) - 1 + # randomly pick two players that will trade + buyer = 0 + seller = 0 + while buyer == seller: + # reroll until the two arent the same + buyer = random.choice([i for i in range(0, pl_idx)]) + seller = random.choice([i for i in range(0, pl_idx)]) + price = random.uniform( + PRICE_FLOOR, spend_limit * balances[buyer] + ) + balances[buyer] -= price + balances[seller] += price + # print(balances[buyer], balances[seller]) + return + class Sim: - def __init__(self, players, balances, terms=50): - self.players = players - self.balances = balances + def __init__(self, players, balances, terms=50, starting_amount=0): + self.players = players.copy() + self.balances = balances.copy() self.terms = terms + self.ADD_NEW_PPL_EVERY = 10 + self.NEW_PPL_START_WITH_BALANCE = starting_amount + self.IS_TRADING = True + + self.IS_HALVING = False + self.HALVING_EVERY = 5 + + self.TITLE = str() self.events = [] - pass - def add_player(self, name="Danny", balance=50): + def add_player(self, + balances, name="Player %i", starting_amount=0): + name = name % (len(self.players) + 1) self.players.append(name) - self.balances.apend(balance) - assert(len(self.players) == len(self.balances)) + balances.append(starting_amount) + try: + assert(len(self.players) == len(balances)) + except AssertionError as e: + print(e) + print(len(self.players)) + print(len(balances)) + sys.exit() - def run(self, update, trade = None): - i = 0 - ADD_NEW_PPL_EVERY = 3 - current_balances = self.balance + def run(self, update): + ADD_NEW_PPL_EVERY = self.ADD_NEW_PPL_EVERY + INIT_AMT = self.NEW_PPL_START_WITH_BALANCE + current_balances = self.balances result = list() - while i <= self.terms: + + dividend = 10 + + i = 0 + while i < self.terms: # add new player? - if ADD_NEW_PPL_EVERY > 0 and i % ADD_NEW_PPL_EVERY == 0: - pass + if i > 0 \ + and ADD_NEW_PPL_EVERY > 0 \ + and i % ADD_NEW_PPL_EVERY == 0: + self.add_player( + current_balances, starting_amount=INIT_AMT) new_bal = [] # make players trade if an - if trade: - current_balances = trade(current_balances) + if self.IS_TRADING: + trade(self.players, current_balances) for balance in current_balances: - b = update(balance) + if not self.IS_HALVING \ + or update == mini_apy: + b = update(balance) + else: + b = mini_dividend(balance, dividend=dividend) new_bal.append(b) + result.append(new_bal) current_balances = new_bal + if i > 0 and i % self.HALVING_EVERY == 0: + dividend = dividend/2 + i += 1 return result - def visualize(self): - pass + def visualize(self, data, filename=str()): + data.insert(0, self.balances) + data2 = [calc_share_of_wealth(row) for row in data] + df = pd.DataFrame(data2) + + # +1 to offset 0 index + x = [i for i in range(1,df.shape[0] + 1)] + assert(len(x) == df.shape[0]) + + plt.style.use('dark_background') + # each column is the balance over time of an individual + for column in df.columns: + plt.plot(x, df[column], label=self.players[column] ) + plt.xlabel("Terms") + plt.ylabel("Share of Wealth") + plt.title(self.TITLE) + # plt.legend() + if filename != str(): + plt.savefig(filename) + else: + plt.show() + plt.close() def mini_apy(balance, rate=0.05): return balance + (balance * rate) -def mini_ubi(balance, dividend = 10): +def mini_dividend(balance, dividend = 10): return balance + dividend if __name__ == "__main__": - illustrate_share_of_wealth() - visualize_ubi(terms=50) - draw_apy_inflation(terms=75) - draw_apy_inflation(terms=75, linear_label="dividend = 10") - draw_3ubi_3apy(terms=75) - - draw_simple_mining_demo() + # illustrate_share_of_wealth() + # visualize_ubi(terms=50) + # draw_apy_inflation(terms=75) + # draw_apy_inflation2(terms=75, linear_label="dividend = 10") + # draw_apy_inflation3(terms=75, linear_label="dividend = 10") + # draw_3ubi_3apy(terms=75) + # + # draw_simple_mining_demo() participants = ["Alice", "Bob", "Charlie"] balances = [100,40,20] s = Sim(participants, balances) + result = s.run(update=mini_dividend) + s.TITLE = "Constant dividend with new players joining and trading" + # s.visualize(result, "const-new-players-trade.png") + + t = Sim(participants, balances) + result2 = t.run(update=mini_apy) + t.TITLE = "APY with new people joining and trading" + # t.visualize(result2, "apy-new-playrs-trade.png") + + u = Sim(participants, balances) + u.IS_HALVING = True + result3 = u.run(update=mini_dividend) + # print(pd.DataFrame(result3)) + u.TITLE = "Halving dividend when new users mine and trade" + u.visualize(result3, filename="halving-dividend-new-users-trade.png")