Shammoo25

AMPL Simulation Script

Jul 22nd, 2020 (edited)
825
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import math
  2. import statistics as stat
  3. import random
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6.  
  7. '''
  8. Most recent update: 2020-10-16
  9.  
  10. Code written by Hikaru Sunakawa (@shammoo25 on Telegram), 2020-07-16, Japan.
  11.  
  12. This script simulates AMPL economics with user-defined initial conditions. You are able to configure the initial
  13. AMPL price, AMPL supply and the amount of AMPL in your wallet. You must also choose how many days you want to simulate,
  14. and how many simulations you want to perform. You may also choose to plot the results, by configuring the plot_results
  15. and max_plots parameters. At the end of the script, when all simulations have been performed, statistics about the
  16. simulations will be printed to the console.
  17.  
  18. The volatiltiy and demand coefficients used in this configuration produce "adequately similar" price action (based
  19. purely on a qualitative assessment of the output simulation plots versus the actual AMPL data plots over the last 90
  20. days), but these coefficients can be changed to whatever you like for the purpose of experimentation.
  21.  
  22. AMPL and Oracle price changes are random, determined using volatility ranges and dynamically changing weights which
  23. favour either an up or down price movement.
  24.  
  25. Note that the random number functions do not have defined seeds, meaning that the results will be different every time
  26. the script is run
  27.  
  28. Please modify the code as you see fit if you feel that it can be improved or corrected. Please let me know on my
  29. telegram handle above if you have any interesting suggestions
  30. '''
  31.  
  32. ############################################ MODIFY THE FOLLOWING VALUES ###############################################
  33. sim_number = 10000  # Number of simulations to perform (if doing more than 1000 simulations, set print_daily and plot_results to False to vastly improve performance)
  34. days = 90  # Number of days per simulation
  35. print_daily = False  # Print the daily stats to the console during each simulation? Will improve performance if False
  36. plot_results = False  # Plot simulation results? True or False. Will output the maximum number of figures determined by max_plots
  37. max_plots = 20  # Maximum number of plot figures to output (1 plot figure per simulation)
  38. init_ampl_balance = 1000  # Initial amount of AMPL in wallet
  39. init_ampl_price = 0.92  # [USD] Initial price of AMPL
  40. init_supply = 166929000.0  # Initial TOTAL supply of AMPL
  41. price_target = 1.014  # [USD] Price target used in rebase calculation
  42. demand_threshold = 0.05  # [USD] The difference between AMPL price and $1.00, used for changing demand coefficients
  43. equal_chance = False  # If this is True, then every day there is the same chance of a upward or downward price movement, and the demand coefficients below are not used
  44. low_price_demand = 1.70  # The higher this coefficient, the higher the chance of an upward price movement when price is within demand_threshold of $1.00
  45. high_price_demand = 1.20  # The higher this coefficient, the higher the chance of an upward price movement when price is outside demand_threshold of $1.00
  46. min_low_volatility = 0.02  # The higher this value, the higher the minimum possible value of the daily AMPL and Oracle price swings when price is within demand_threshold of $1.00
  47. max_low_volatility = 0.08  # The higher this value, the higher the maximum possible value of the daily AMPL and Oracle price swings when price is within demand_threshold of $1.00
  48. min_high_volatility = 0.10  # The higher this value, the higher the minimum possible value of the daily AMPL and Oracle price swings when price is outside demand_threshold of $1.00
  49. max_high_volatility = 0.30  # The higher this value, the higher the maximum possible value of the daily AMPL and Oracle price swings when price is outside demand_threshold of $1.00
  50. ########################################################################################################################
  51.  
  52. # Constants
  53. rebase_lag = 10  # [days] Lag used in rebase calculation
  54. contraction_threshold = price_target * 0.95  # [USD] Oracle rate window lower value. Between this value and expansion_threshold, rebase will not take place
  55. expansion_threshold = price_target * 1.05  # [USD] Oracle rate window higher value
  56.  
  57. # Initialize
  58. days += 1
  59. ampl_price = np.zeros((days, sim_number))
  60. supply = np.zeros((days, sim_number))
  61. my_balance = np.zeros((days, sim_number))
  62. rebase = np.zeros((days, sim_number))
  63. market_cap = np.zeros((days, sim_number))
  64. ampl_value = np.zeros((days, sim_number))
  65. init_ampl_value = init_ampl_price * init_ampl_balance
  66. init_market_cap = init_supply * init_ampl_price
  67.  
  68. ################################################### SIMULATE ###########################################################
  69. for s in range(sim_number):
  70.     for x in range(days):
  71.         if x == 0:
  72.             ampl_price[0, s] = init_ampl_price
  73.             supply[0, s] = init_supply
  74.             my_balance[0, s] = init_ampl_balance
  75.             rebase[0, s] = 0.0
  76.             market_cap[0, s] = init_market_cap
  77.             ampl_value[0, s] = init_ampl_value
  78.         else:
  79.             # Determine demand strength and volatility
  80.             if math.fabs(ampl_price[x - 1, s] - 1.00) < demand_threshold:
  81.                 demand_strength = random.uniform(1.0, low_price_demand)
  82.                 volatility = random.uniform(min_low_volatility, max_low_volatility)
  83.             else:
  84.                 demand_strength = random.uniform(min(low_price_demand, high_price_demand),
  85.                                                  max(low_price_demand, high_price_demand))
  86.                 volatility = random.uniform(min_high_volatility, max_high_volatility)
  87.             # Determine direction of movement
  88.             direction_list = [1.0] + [-1.0]
  89.             if equal_chance:
  90.                 up_weight = 50
  91.                 down_weight = 50
  92.             else:
  93.                 up_weight = demand_strength
  94.                 down_weight = 1 / demand_strength
  95.             direction = random.choices(direction_list, weights=(up_weight, down_weight), k=1)
  96.             direction = float(direction[0])
  97.             # Calculate price, balance, supply and market cap
  98.             ampl_price[x, s] = ampl_price[x - 1, s] * (1.0 + (volatility * direction))  # Calculate pre-rebase AMPL price
  99.             # Determine Oracle rate based on average AMPL price (using previous post-rebase price and current pre-rebase price), then calculate  subsequent rebase amount
  100.             oracle_price = (ampl_price[x - 1, s] + ampl_price[x, s]) / 2
  101.             if contraction_threshold <= oracle_price <= expansion_threshold:
  102.                 rebase[x, s] = 0.0
  103.             else:
  104.                 rebase[x, s] = (oracle_price - price_target) / price_target / rebase_lag
  105.             ampl_price[x, s] *= (1 - rebase[x - 1, s])  # Calculate post-rebase AMPL price
  106.             my_balance[x, s] = my_balance[x - 1, s] * (1 + rebase[x, s])
  107.             supply[x, s] = supply[x - 1, s] * (1 + rebase[x, s])
  108.             market_cap[x, s] = ampl_price[x, s] * supply[x, s]
  109.             ampl_value[x, s] = my_balance[x, s] * ampl_price[x, s]
  110.  
  111.         if print_daily:
  112.             print("Sim " + str(s + 1),
  113.                   "| Day " + str(x),
  114.                   "| AMPL price = $" + str('%.2f' % (ampl_price[x, s])),
  115.                   "| AMPL Balance = " + f'{my_balance[x, s]:,.0f}',
  116.                   "| AMPL Value = $" + str('%.3f' % (ampl_value[x, s])),
  117.                   "| Rebase = " + str('%.3f' % (100 * rebase[x, s])) + "%",
  118.                   "| Supply = " + f'{supply[x, s]:,.0f}',
  119.                   "| Market Cap = $" + f'{market_cap[x, s]:,.0f}')
  120.  
  121.     if plot_results and s < max_plots:
  122.         fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, figsize=(10, 8), constrained_layout=True)
  123.         fig.suptitle('Simulation ' + str(s + 1))
  124.         ax1.plot(ampl_price[:, s])
  125.         ax1.set_title('AMPL Price (USD)')
  126.         ax1.grid(b=True, which='both', axis='both')
  127.         ax2.plot(market_cap[:, s], 'tab:orange')
  128.         ax2.set_title('Market Cap')
  129.         ax2.grid(b=True, which='both', axis='both')
  130.         ax3.plot(supply[:, s], 'tab:cyan')
  131.         ax3.set_title('Supply')
  132.         ax3.grid(b=True, which='both', axis='both')
  133.         ax4.plot(my_balance[:, s], 'tab:green')
  134.         ax4.set_title('Amount of AMPL Held in Wallet')
  135.         ax4.grid(b=True, which='both', axis='both')
  136.         ax5.plot(ampl_value[:, s], 'tab:red')
  137.         ax5.set_title('USD Value of AMPL in Wallet')
  138.         ax5.grid(b=True, which='both', axis='both')
  139.  
  140.     if print_daily == False:
  141.         percent_complete = s / sim_number * 100
  142.         if percent_complete % 1 == 0:
  143.             print('\r', str(int(percent_complete)) + '% of simulations performed', end='')
  144.     else:
  145.         print('-------------------------------------------------------------------------------------------------------'
  146.               '-----------------------------------------------------------------------------')
  147.  
  148. print('\r', 'All simulations performed', end='')
  149.  
  150. ###############################################  CALCULATE STATISTICS ##################################################
  151. price_up = 0
  152. supply_up = 0
  153. balance_up = 0
  154. value_up = 0
  155. mc_up = 0
  156. price_diff_percent = np.zeros(sim_number)
  157. price_diff_dollar = np.zeros(sim_number)
  158. supply_diff = np.zeros(sim_number)
  159. supply_diff_percent = np.zeros(sim_number)
  160. balance_diff_percent = np.zeros(sim_number)
  161. balance_diff = np.zeros(sim_number)
  162. value_diff_percent = np.zeros(sim_number)
  163. value_diff_dollar = np.zeros(sim_number)
  164. mc_diff_percent = np.zeros(sim_number)
  165. mc_diff_dollar = np.zeros(sim_number)
  166. drawdown = np.zeros(sim_number)
  167. recovery = np.zeros(sim_number)
  168. for s in range(sim_number):
  169.     if ampl_price[-1, s] > ampl_price[0, s]:
  170.         price_up += 1
  171.     if supply[-1, s] > supply[0, s]:
  172.         supply_up += 1
  173.     if my_balance[-1, s] > my_balance[0, s]:
  174.         balance_up += 1
  175.     if ampl_value[-1, s] > ampl_value[0, s]:
  176.         value_up += 1
  177.     if market_cap[-1, s] > market_cap[0, s]:
  178.         mc_up += 1
  179.     price_diff_percent[s] = (ampl_price[-1, s] / ampl_price[0, s] * 100) - 100
  180.     price_diff_dollar[s] = ampl_price[-1, s] - ampl_price[0, s]
  181.     supply_diff_percent[s] = (supply[-1, s] / [supply[0, s]] * 100) - 100
  182.     supply_diff[s] = supply[-1, s] - supply[0, s]
  183.     balance_diff_percent[s] = (my_balance[-1, s] / my_balance[0, s] * 100) - 100
  184.     balance_diff[s] = my_balance[-1, s] - my_balance[0, s]
  185.     value_diff_percent[s] = (ampl_value[-1, s] / ampl_value[0, s] * 100) - 100
  186.     value_diff_dollar[s] = ampl_value[-1, s] - ampl_value[0, s]
  187.     mc_diff_percent[s] = (market_cap[-1, s] / market_cap[0, s] * 100) - 100
  188.     mc_diff_dollar[s] = market_cap[-1, s] - market_cap[0, s]
  189.     min_market_cap = min(market_cap[:, s])
  190.     drawdown[s] = max(0, 100 - (min_market_cap / init_market_cap * 100))
  191. max_drawdown_index = np.argmax(drawdown)
  192.  
  193. print(f'\n**************************************** OVERALL STATISTICS *****************************************')
  194. print(str(price_up) + ' out of ' + str(sim_number) + ' (' + str(price_up / sim_number * 100)
  195.       + '%) simulations ended with AMPL price higher than the initial price')
  196. print(str(supply_up) + ' out of ' + str(sim_number) + ' (' + str(supply_up / sim_number * 100)
  197.       + '%) simulations ended with AMPL supply higher than the initial amount')
  198. print(str(mc_up) + ' out of ' + str(sim_number) + ' (' + str(mc_up / sim_number * 100)
  199.       + '%) simulations ended with market cap higher than the initial value')
  200. print(str(balance_up) + ' out of ' + str(sim_number) + ' (' + str(balance_up / sim_number * 100)
  201.       + '%) simulations ended with amount of AMPL in wallet higher than the initial amount')
  202. print(str(value_up) + ' out of ' + str(sim_number) + ' (' + str(value_up / sim_number * 100)
  203.       + '%) simulations ended with value of AMPL (USD) in wallet higher than the initial value')
  204.  
  205. # AMPL Price
  206. print(f'\nAMPL Price Changes:\n' + 'The mean price on the last day was $' + f'{stat.mean(ampl_price[-1, :]):,.2f}'
  207.       + str('. This is a difference of $')
  208.       + str('%.2f' % stat.mean(price_diff_dollar)) + ' ('
  209.       + str('%.2f' % stat.mean(price_diff_percent)) + '%) from the initial price of $' + str('%.2f' % init_ampl_price))
  210.  
  211. print('The median price on the last day was $' + f'{stat.median(ampl_price[-1, :]):,.2f}'
  212.       + str('. This is a difference of $')
  213.       + str('%.2f' % stat.median(price_diff_dollar)) + ' ('
  214.       + str('%.2f' % stat.median(price_diff_percent)) + '%) from the initial price of $'
  215.       + str('%.2f' % init_ampl_price))
  216.  
  217. # AMPL Supply
  218. print(f'\nAMPL Supply Changes:\n'
  219.       + 'The mean supply at the end of ' + str(days - 1) + ' days was ' + f'{stat.mean(supply[-1, :]):,.0f}'
  220.       + '. This is a change of ' + f'{stat.mean(supply_diff):,.0f}'
  221.       + ' (' + f'{stat.mean(supply_diff_percent):,.2f}'
  222.       + '%) from the initial supply of ' + f'{init_supply:,.0f}')
  223.  
  224. print('The median supply at the end of ' + str(days - 1)
  225.       + ' days was ' + f'{stat.median(supply[-1, :]):,.0f}'
  226.       + '. This is a change of ' + f'{stat.median(supply_diff):,.0f}'
  227.       + ' (' + f'{stat.median(supply_diff_percent):,.2f}'
  228.       + '%) from the initial supply of ' + f'{init_supply:,.0f}')
  229.  
  230. # Market Cap
  231. print(f'\nMarket Cap Changes:\n'
  232.       + 'The mean market cap at the end of ' + str(days - 1) + ' days was $' + f'{stat.mean(market_cap[-1, :]):,.0f}'
  233.       + '. This is a change of $' + f'{stat.mean(mc_diff_dollar):,.0f}'
  234.       + ' (' + f'{stat.mean(mc_diff_percent):,.2f}'
  235.       + '%) from the initial amount of $' + f'{init_market_cap:,.0f}')
  236.  
  237. print('The median market cap at the end of ' + str(days - 1) + ' days was $' + f'{stat.median(market_cap[-1, :]):,.0f}'
  238.       + '. This is a change of $' + f'{stat.median(mc_diff_dollar):,.0f}'
  239.       + ' (' + f'{stat.median(mc_diff_percent):,.2f}'
  240.       + '%) from the initial amount of $' + f'{init_market_cap:,.0f}')
  241.  
  242. # Amount of AMPL in Wallet
  243. print(f'\nAMPL Wallet Balance Changes:\n' + 'The mean amount of held AMPL at the end of ' + str(
  244.     days - 1) + ' days was ' + f'{stat.mean(my_balance[-1, :]):,.2f}'
  245.       + ' AMPL. This is a change of ' + f'{stat.mean(balance_diff):,.2f}'
  246.       + ' (' + f'{stat.mean(balance_diff_percent):,.2f}'
  247.       + '%) from the initial amount of ' + f'{init_ampl_balance:,.2f}' + ' AMPL.')
  248.  
  249. print('The median amount of held AMPL at the end of ' + str(
  250.     days - 1) + ' days was ' + f'{stat.median(my_balance[-1, :]):,.2f}'
  251.       + ' AMPL. This is a change of ' + f'{stat.median(balance_diff):,.2f}'
  252.       + ' (' + f'{stat.median(balance_diff_percent):,.2f}'
  253.       + '%) from the initial amount of ' + f'{init_ampl_balance:,.2f}' + ' AMPL')
  254.  
  255. # USD Value of AMPL in Wallet
  256. print(f'\nAMPL Wallet USD Value Changes:\n' + 'The mean amount of USD value of held AMPL at the end of ' + str(
  257.     days - 1) + ' days was $' + f'{stat.mean(ampl_value[-1, :]):,.2f}'
  258.       + '. This is a change of $' + f'{stat.mean(value_diff_dollar):,.2f}'
  259.       + ' (' + f'{stat.mean(value_diff_percent):,.2f}'
  260.       + '%) from the initial amount of $' + f'{init_ampl_value:,.2f}')
  261.  
  262. print('The median amount of USD value held AMPL at the end of ' + str(days - 1) + ' days was $'
  263.       + f'{stat.median(ampl_value[-1, :]):,.2f}'
  264.       + '. This is a change of $' + f'{stat.median((ampl_value[-1, :]) - init_ampl_value):,.2f}'
  265.       + ' (' + f'{stat.median(value_diff_percent):,.2f}'
  266.       + '%) from the initial amount of $' + f'{init_ampl_value:,.2f}')
  267.  
  268. # Drawdown
  269. print(f'\nMarket Cap Drawdown % from the Initial USD Value (equivalent to drawdown % of USD value of AMPL in wallet):\n'
  270.       + 'The mean percentage drawdown during the ' + str(days - 1)
  271.       + ' day period was -' + f'{stat.mean(drawdown):,.2f}' + '%')
  272. print('The median percentage drawdown during the ' + str(days - 1)
  273.       + ' day period was -' + f'{stat.median(drawdown):,.2f}' + '%')
  274. print('The largest drawndown out of all the simulations was -' + f'{(max(drawdown)):,.2f}' + '%'
  275.       + ' in simulation #' + str(max_drawdown_index + 1))
  276. print('Simulation #' + str(max_drawdown_index + 1) + ' ended with a market cap of $'
  277.       + f'{market_cap[-1, max_drawdown_index]:,.0f}' + ' which is a change of $'
  278.       + f'{market_cap[-1, max_drawdown_index] - init_market_cap:,.0f}'
  279.       + ' (' + f'{(market_cap[-1, max_drawdown_index] / market_cap[0, max_drawdown_index] * 100) - 100:,.2f}'
  280.       + '%) from the initial market cap of $'  + f'{init_market_cap:,.0f}')
  281.  
  282. if plot_results:
  283.     plt.show()
RAW Paste Data