Merge branch 'master' of https://github.com/BlockScience/Community_Inclusion_Currencies
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 73 KiB |
11
README.md
|
|
@ -1,9 +1,16 @@
|
|||
# Community_Inclusion_Currencies
|
||||
Repository for Complex Systems model of the Grassroots Economics Community Inclusion Currency project.
|
||||
Repository for Complex Systems model of the Grassroots Economics Community Inclusion Currency project. The Colab notebooks are able to be run and played with by anyone who uses the link.
|
||||
|
||||
### Colab
|
||||
[Click here to get to interactive colab](https://colab.research.google.com/github/BlockScience/Community_Inclusion_Currencies/blob/master/Colab/CIC_Network_cadCAD_model.ipynb#scrollTo=UZEO2sP9Mhi-)
|
||||
[Click here to get to an interactive notebook](https://colab.research.google.com/github/BlockScience/Community_Inclusion_Currencies/blob/master/Colab/CIC_Network_cadCAD_model.ipynb#scrollTo=UZEO2sP9Mhi-)
|
||||
|
||||
[Click here to for an interactive notebook with a parameter sweep](https://colab.research.google.com/github/BlockScience/Community_Inclusion_Currencies/blob/master/Colab/CIC_Network_cadCAD_model_params.ipynb)
|
||||
|
||||
### Simulation work
|
||||
[Click here](Simulation/CIC_Network_cadCAD_model.ipynb)
|
||||
|
||||
#### Parameter sweep
|
||||
[Click here](Simulation_param/CIC_Network_cadCAD_model_params.ipynb)
|
||||
|
||||
### Theory work
|
||||
[Click here](Theory/cic_initialization.ipynb)
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 634 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
|
@ -0,0 +1,36 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from datetime import timedelta
|
||||
import numpy as np
|
||||
from typing import Dict, List
|
||||
|
||||
from cadCAD.configuration import append_configs
|
||||
from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim, access_block
|
||||
|
||||
from .genesis_states import genesis_states
|
||||
from .partial_state_update_block import partial_state_update_block
|
||||
|
||||
params: Dict[str, List[int]] = {
|
||||
'drip_frequency': [30,60,90] # in days
|
||||
}
|
||||
|
||||
|
||||
sim_config = config_sim({
|
||||
'N': 5,
|
||||
'T': range(100), #day
|
||||
'M': params,
|
||||
})
|
||||
|
||||
seeds = {
|
||||
'p': np.random.RandomState(26042019),
|
||||
}
|
||||
env_processes = {}
|
||||
|
||||
|
||||
append_configs(
|
||||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
seeds=seeds,
|
||||
env_processes=env_processes,
|
||||
partial_state_update_blocks=partial_state_update_block
|
||||
)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from .parts.initialization import *
|
||||
import pandas as pd
|
||||
|
||||
genesis_states = {
|
||||
# initial states of the economy
|
||||
'network': create_network(),# networkx market
|
||||
'KPIDemand': {},
|
||||
'KPISpend': {},
|
||||
'KPISpendOverDemand': {},
|
||||
'VelocityOfMoney':0,
|
||||
'startingBalance': {},
|
||||
'30_day_spend': {},
|
||||
'withdraw':{},
|
||||
'outboundAgents':[],
|
||||
'inboundAgents':[],
|
||||
'operatorFiatBalance': R0,
|
||||
'operatorCICBalance': S0,
|
||||
'fundsInProcess': {'timestep':[],'decision':[],'cic':[],'shilling':[]},
|
||||
'totalDistributedToAgents':0,
|
||||
'totalMinted':0,
|
||||
'totalBurned':0
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
from .parts.exogenousProcesses import *
|
||||
from .parts.kpis import *
|
||||
from .parts.system import *
|
||||
from .parts.operatorentity import *
|
||||
|
||||
partial_state_update_block = {
|
||||
# Exogenous
|
||||
'Exogenous': {
|
||||
'policies': {
|
||||
},
|
||||
'variables': {
|
||||
'startingBalance': startingBalance,
|
||||
'operatorFiatBalance': redCrossDrop,
|
||||
'30_day_spend': update_30_day_spend,
|
||||
'network':clear_agent_activity
|
||||
}
|
||||
},
|
||||
# Users
|
||||
'Behaviors': {
|
||||
'policies': {
|
||||
'action': choose_agents
|
||||
},
|
||||
'variables': {
|
||||
'network': update_agent_activity,
|
||||
'outboundAgents': update_outboundAgents,
|
||||
'inboundAgents':update_inboundAgents
|
||||
}
|
||||
},
|
||||
'Spend allocation': {
|
||||
'policies': {
|
||||
'action': spend_allocation
|
||||
},
|
||||
'variables': {
|
||||
'network': update_node_spend
|
||||
}
|
||||
},
|
||||
'Withdraw behavior': {
|
||||
'policies': {
|
||||
'action': withdraw_calculation
|
||||
},
|
||||
'variables': {
|
||||
'withdraw': update_withdraw,
|
||||
'network':update_network_withraw
|
||||
}
|
||||
},
|
||||
# Operator
|
||||
'Operator Disburse to Agents': {
|
||||
'policies': {
|
||||
'action': disbursement_to_agents
|
||||
},
|
||||
'variables': {
|
||||
'network':update_agent_tokens,
|
||||
'operatorCICBalance':update_operator_FromDisbursements,
|
||||
'totalDistributedToAgents':update_totalDistributedToAgents
|
||||
}
|
||||
},
|
||||
'Operator Inventory Control': {
|
||||
'policies': {
|
||||
'action': inventory_controller
|
||||
},
|
||||
'variables': {
|
||||
'operatorFiatBalance':update_operator_fiatBalance,
|
||||
'operatorCICBalance':update_operator_cicBalance,
|
||||
'totalMinted': update_totalMinted,
|
||||
'totalBurned':update_totalBurned,
|
||||
'fundsInProcess':update_fundsInProcess
|
||||
}
|
||||
},
|
||||
# KPIs
|
||||
'KPIs': {
|
||||
'policies': {
|
||||
'action':kpis
|
||||
},
|
||||
'variables':{
|
||||
'KPIDemand': update_KPIDemand,
|
||||
'KPISpend': update_KPISpend,
|
||||
'KPISpendOverDemand': update_KPISpendOverDemand
|
||||
}
|
||||
},
|
||||
'Velocity': {
|
||||
'policies': {
|
||||
'action':velocity_of_money
|
||||
},
|
||||
'variables':{
|
||||
|
||||
'VelocityOfMoney': update_velocity_of_money
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from .initialization import *
|
||||
from .supportingFunctions import *
|
||||
|
||||
|
||||
def startingBalance(params, step, sL, s, _input):
|
||||
'''
|
||||
Calculate agent starting balance every 30 days
|
||||
'''
|
||||
y = 'startingBalance'
|
||||
network = s['network']
|
||||
|
||||
startingBalance = {}
|
||||
|
||||
timestep = s['timestep']
|
||||
|
||||
division = timestep % 31 == 0
|
||||
|
||||
if timestep == 1:
|
||||
for i in agents:
|
||||
startingBalance[i] = network.nodes[i]['tokens']
|
||||
elif division == True:
|
||||
for i in agents:
|
||||
startingBalance[i] = network.nodes[i]['tokens']
|
||||
else:
|
||||
startingBalance = s['startingBalance']
|
||||
x = startingBalance
|
||||
|
||||
return (y, x)
|
||||
|
||||
def update_30_day_spend(params, step, sL, s,_input):
|
||||
'''
|
||||
Aggregate agent spend. Refresh every 30 days.
|
||||
'''
|
||||
y = '30_day_spend'
|
||||
network = s['network']
|
||||
|
||||
timestep = s['timestep']
|
||||
|
||||
division = timestep % 31 == 0
|
||||
|
||||
if division == True:
|
||||
outflowSpend, inflowSpend = iterateEdges(network,'spend')
|
||||
spend = outflowSpend
|
||||
else:
|
||||
spendOld = s['30_day_spend']
|
||||
outflowSpend, inflowSpend = iterateEdges(network,'spend')
|
||||
spend = DictionaryMergeAddition(spendOld,outflowSpend)
|
||||
|
||||
x = spend
|
||||
return (y, x)
|
||||
|
||||
def redCrossDrop(params, step, sL, s, _input):
|
||||
'''
|
||||
Every 30 days, the red cross drips to the grassroots operator node
|
||||
'''
|
||||
y = 'operatorFiatBalance'
|
||||
fiatBalance = s['operatorFiatBalance']
|
||||
|
||||
timestep = s['timestep']
|
||||
|
||||
division = timestep % params['drip_frequency'] == 0
|
||||
|
||||
if division == True:
|
||||
fiatBalance = fiatBalance + drip
|
||||
else:
|
||||
pass
|
||||
|
||||
x = fiatBalance
|
||||
return (y, x)
|
||||
|
||||
|
||||
def clear_agent_activity(params,step,sL,s,_input):
|
||||
'''
|
||||
Clear agent activity from the previous timestep
|
||||
'''
|
||||
y = 'network'
|
||||
network = s['network']
|
||||
|
||||
if s['timestep'] > 0:
|
||||
outboundAgents = s['outboundAgents']
|
||||
inboundAgents = s['inboundAgents']
|
||||
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
network[i][j]['demand'] = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
# Clear cic % demand edge weights
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
network[i][j]['fractionOfDemandInCIC'] = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# Clear utility edge types
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
network[i][j]['utility'] = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
# Clear cic % spend edge weights
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
network[i][j]['fractionOfActualSpendInCIC'] = 0
|
||||
except:
|
||||
pass
|
||||
# Clear spend edge types
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
network[i][j]['spend'] = 0
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
x = network
|
||||
return (y,x)
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
# import libraries
|
||||
import networkx as nx
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from .supportingFunctions import *
|
||||
|
||||
# Assumptions:
|
||||
# Amount received in shilling when withdraw occurs
|
||||
leverage = 1
|
||||
|
||||
# process time
|
||||
process_lag = 7 # timesteps
|
||||
|
||||
# red cross drip amount
|
||||
drip = 4000
|
||||
|
||||
# system initialization
|
||||
agents = ['a','b','c','d','e','f','g','h','i','j','k','l','m','o','p']
|
||||
|
||||
# system actors
|
||||
system = ['external','cic']
|
||||
|
||||
# chamas
|
||||
chama = ['chama_1','chama_2','chama_3','chama_4']
|
||||
|
||||
# traders
|
||||
traders = ['ta','tb','tc'] #only trading on the cic. Link to external and cic not to other agents
|
||||
|
||||
allAgents = agents + system
|
||||
|
||||
mixingAgents = ['a','b','c','d','e','f','g','h','i','j','k','l','m','o','p','external']
|
||||
|
||||
UtilityTypesOrdered ={'Food/Water':1,
|
||||
'Fuel/Energy':2,
|
||||
'Health':3,
|
||||
'Education':4,
|
||||
'Savings Group':5,
|
||||
'Shop':6}
|
||||
|
||||
utilityTypesProbability = {'Food/Water':0.6,
|
||||
'Fuel/Energy':0.10,
|
||||
'Health':0.03,
|
||||
'Education':0.015,
|
||||
'Savings Group':0.065,
|
||||
'Shop':0.19}
|
||||
|
||||
|
||||
R0 = 500 #thousand xDAI
|
||||
kappa = 4 #leverage
|
||||
P0 = 1/100 #initial price
|
||||
S0 = kappa*R0/P0
|
||||
V0 = invariant(R0,S0,kappa)
|
||||
P = spot_price(R0, V0, kappa)
|
||||
|
||||
# Price level
|
||||
priceLevel = 100
|
||||
|
||||
fractionOfDemandInCIC = 0.5
|
||||
fractionOfActualSpendInCIC = 0.5
|
||||
|
||||
def create_network():
|
||||
# Create network graph
|
||||
network = nx.DiGraph()
|
||||
|
||||
# Add nodes for n participants plus the external economy and the cic network
|
||||
for i in agents:
|
||||
network.add_node(i,type='Agent',tokens=400, native_currency = int(np.random.uniform(low=20, high=500, size=1)[0]))
|
||||
|
||||
|
||||
network.add_node('external',type='Contract',native_currency = 100000000,tokens = 0,delta_native_currency = 0, pos=(1,50))
|
||||
network.add_node('cic',type='Contract',tokens= S0, native_currency = R0,pos=(50,1))
|
||||
|
||||
for i in chama:
|
||||
network.add_node(i,type='Chama')
|
||||
|
||||
for i in traders:
|
||||
network.add_node(i,type='Trader',tokens=20, native_currency = 20,
|
||||
price_belief = 1, trust_level = 1)
|
||||
|
||||
# Create bi-directional edges between all participants
|
||||
for i in allAgents:
|
||||
for j in allAgents:
|
||||
if i!=j:
|
||||
network.add_edge(i,j)
|
||||
|
||||
# Create bi-directional edges between each trader and the external economy and the cic environment
|
||||
for i in traders:
|
||||
for j in system:
|
||||
if i!=j:
|
||||
network.add_edge(i,j)
|
||||
|
||||
# Create bi-directional edges between some agent and a chama node representing membershio
|
||||
for i in chama:
|
||||
for j in agents:
|
||||
if np.random.choice(['Member','Non_Member'],1,p=[.50,.50])[0] == 'Member':
|
||||
network.add_edge(i,j)
|
||||
|
||||
# Type colors
|
||||
colors = ['Red','Blue','Green','Orange']
|
||||
color_map = []
|
||||
for i in network.nodes:
|
||||
if network.nodes[i]['type'] == 'Agent':
|
||||
color_map.append('Red')
|
||||
elif network.nodes[i]['type'] == 'Cloud':
|
||||
color_map.append('Blue')
|
||||
elif network.nodes[i]['type'] == 'Contract':
|
||||
color_map.append('Green')
|
||||
elif network.nodes[i]['type'] == 'Trader':
|
||||
color_map.append('Yellow')
|
||||
elif network.nodes[i]['type'] == 'Chama':
|
||||
color_map.append('Orange')
|
||||
|
||||
pos = nx.spring_layout(network,pos=nx.get_node_attributes(network,'pos'),fixed=nx.get_node_attributes(network,'pos'),seed=10)
|
||||
nx.draw(network,node_color = color_map,pos=pos,with_labels=True,alpha=0.7)
|
||||
plt.savefig('images/graph.png')
|
||||
plt.show()
|
||||
return network
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
|
||||
import numpy as np
|
||||
from .initialization import *
|
||||
from .supportingFunctions import *
|
||||
import networkx as nx
|
||||
|
||||
|
||||
# Behaviors
|
||||
def kpis(params, step, sL, s):
|
||||
''''''
|
||||
# instantiate network state
|
||||
network = s['network']
|
||||
|
||||
KPIDemand = {}
|
||||
KPISpend = {}
|
||||
KPISpendOverDemand = {}
|
||||
for i in mixingAgents:
|
||||
demand = []
|
||||
for j in network.adj[i]:
|
||||
try:
|
||||
demand.append(network.adj[i][j]['demand'])
|
||||
except:
|
||||
pass
|
||||
|
||||
spend = []
|
||||
for j in network.adj[i]:
|
||||
try:
|
||||
spend.append(network.adj[i][j]['spend'])
|
||||
except:
|
||||
pass
|
||||
|
||||
sumDemand = sum(demand)
|
||||
sumSpend = sum(spend)
|
||||
try:
|
||||
spendOverDemand = sumSpend/sumDemand
|
||||
except:
|
||||
spendOverDemand = 0
|
||||
|
||||
KPIDemand[i] = sumDemand
|
||||
KPISpend[i] = sumSpend
|
||||
KPISpendOverDemand[i] = spendOverDemand
|
||||
|
||||
#print(nx.katz_centrality_numpy(G=network,weight='spend'))
|
||||
return {'KPIDemand':KPIDemand,'KPISpend':KPISpend,'KPISpendOverDemand':KPISpendOverDemand}
|
||||
|
||||
def velocity_of_money(params, step, sL, s):
|
||||
''''''
|
||||
# instantiate network state
|
||||
network = s['network']
|
||||
|
||||
KPISpend = s['KPISpend']
|
||||
|
||||
# TODO: Moving average for state variable
|
||||
T = []
|
||||
for i,j in KPISpend.items():
|
||||
T.append(j)
|
||||
|
||||
T = sum(T)
|
||||
|
||||
# TODO Moving average for state variable
|
||||
M = []
|
||||
for i in agents:
|
||||
M.append(network.nodes[i]['tokens'] + network.nodes[i]['native_currency'])
|
||||
|
||||
M = sum(M)
|
||||
|
||||
V_t = (priceLevel *T)/M
|
||||
|
||||
return {'V_t':V_t,'T':T,'M':M}
|
||||
|
||||
|
||||
# Mechanisms
|
||||
def update_KPIDemand(params, step, sL, s,_input):
|
||||
y = 'KPIDemand'
|
||||
x = _input['KPIDemand']
|
||||
return (y,x)
|
||||
|
||||
def update_KPISpend(params, step, sL, s,_input):
|
||||
y = 'KPISpend'
|
||||
x = _input['KPISpend']
|
||||
return (y,x)
|
||||
|
||||
def update_KPISpendOverDemand(params, step, sL, s,_input):
|
||||
y = 'KPISpendOverDemand'
|
||||
x = _input['KPISpendOverDemand']
|
||||
return (y,x)
|
||||
|
||||
|
||||
def update_velocity_of_money(params, step, sL, s,_input):
|
||||
y = 'VelocityOfMoney'
|
||||
x = _input['V_t']
|
||||
return (y,x)
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from cadCAD.configuration.utils import access_block
|
||||
from .initialization import *
|
||||
from .supportingFunctions import *
|
||||
from collections import OrderedDict
|
||||
|
||||
# Parameters
|
||||
FrequencyOfAllocation = 45 # every two weeks
|
||||
idealFiat = 5000
|
||||
idealCIC = 200000
|
||||
varianceCIC = 50000
|
||||
varianceFiat = 1000
|
||||
unadjustedPerAgent = 50
|
||||
|
||||
|
||||
|
||||
|
||||
agentAllocation = {'a':[1,1],'b':[1,1],'c':[1,1], # agent:[centrality,allocationValue]
|
||||
'd':[1,1],'e':[1,1],'f':[1,1],
|
||||
'g':[1,1],'h':[1,1],'i':[1,1],
|
||||
'j':[1,1],'k':[1,1],'l':[1,1],
|
||||
'm':[1,1],'o':[1,1],'p':[1,1]}
|
||||
|
||||
# Behaviors
|
||||
def disbursement_to_agents(params, step, sL, s):
|
||||
'''
|
||||
Distribute every FrequencyOfAllocation days to agents based off of centrality allocation metric
|
||||
'''
|
||||
fiatBalance = s['operatorFiatBalance']
|
||||
cicBalance = s['operatorCICBalance']
|
||||
timestep = s['timestep']
|
||||
|
||||
division = timestep % FrequencyOfAllocation == 0
|
||||
|
||||
if division == True:
|
||||
agentDistribution ={} # agent: amount distributed
|
||||
for i,j in agentAllocation.items():
|
||||
agentDistribution[i] = unadjustedPerAgent * agentAllocation[i][1]
|
||||
distribute = 'Yes'
|
||||
|
||||
else:
|
||||
agentDistribution = 0
|
||||
distribute = 'No'
|
||||
|
||||
|
||||
return {'distribute':distribute,'amount':agentDistribution}
|
||||
|
||||
|
||||
def inventory_controller(params, step, sL, s):
|
||||
'''
|
||||
Monetary policy hysteresis conservation allocation between fiat and cic reserves.
|
||||
|
||||
# TODO: If scarcity on both sides, add feedback to reduce percentage able to withdraw, frequency you can redeem, or redeem at less than par.
|
||||
'''
|
||||
fiatBalance = s['operatorFiatBalance']
|
||||
cicBalance = s['operatorCICBalance']
|
||||
timestep = s['timestep']
|
||||
fundsInProcess = s['fundsInProcess']
|
||||
|
||||
|
||||
updatedCIC = cicBalance
|
||||
updatedFiat = fiatBalance
|
||||
|
||||
#decision,amt = mint_burn_logic_control(idealCIC,updatedCIC,variance,updatedFiat)
|
||||
decision,amt = mint_burn_logic_control(idealCIC,updatedCIC,varianceCIC,updatedFiat,varianceFiat,idealFiat)
|
||||
|
||||
if decision == 'burn':
|
||||
try:
|
||||
deltaR, realized_price = withdraw(amt,updatedFiat,updatedCIC, V0, kappa)
|
||||
# update state
|
||||
# fiatBalance = fiatBalance - deltaR
|
||||
# cicBalance = cicBalance - amt
|
||||
fiatChange = abs(deltaR)
|
||||
cicChange = amt
|
||||
|
||||
except:
|
||||
print('Not enough to burn')
|
||||
|
||||
fiatChange = 0
|
||||
cicChange = 0
|
||||
|
||||
elif decision == 'mint':
|
||||
try:
|
||||
deltaS, realized_price = mint(amt,updatedFiat,updatedCIC, V0, kappa)
|
||||
# update state
|
||||
# fiatBalance = fiatBalance + amt
|
||||
# cicBalance = cicBalance + deltaS
|
||||
fiatChange = amt
|
||||
cicChange = abs(deltaS)
|
||||
|
||||
except:
|
||||
print('Not enough to mint')
|
||||
fiatChange = 0
|
||||
cicChange = 0
|
||||
|
||||
else:
|
||||
fiatChange = 0
|
||||
cicChange = 0
|
||||
decision = 'none'
|
||||
pass
|
||||
|
||||
if decision == 'mint':
|
||||
fundsInProcess['timestep'].append(timestep + process_lag)
|
||||
fundsInProcess['decision'].append(decision)
|
||||
fundsInProcess['cic'].append(fiatChange)
|
||||
fundsInProcess['shilling'].append(cicChange)
|
||||
elif decision == 'burn':
|
||||
fundsInProcess['timestep'].append(timestep +process_lag)
|
||||
fundsInProcess['decision'].append(decision)
|
||||
fundsInProcess['cic'].append(fiatChange)
|
||||
fundsInProcess['shilling'].append(cicChange)
|
||||
else:
|
||||
pass
|
||||
|
||||
return {'decision':decision,'fiatChange':fiatChange,'cicChange':cicChange,'fundsInProcess':fundsInProcess}
|
||||
|
||||
|
||||
|
||||
# Mechanisms
|
||||
def update_agent_tokens(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'network'
|
||||
network = s['network']
|
||||
|
||||
distribute = _input['distribute']
|
||||
amount = _input['amount']
|
||||
|
||||
if distribute == 'Yes':
|
||||
for i in agents:
|
||||
network.nodes[i]['tokens'] = network.nodes[i]['tokens'] + amount[i]
|
||||
else:
|
||||
pass
|
||||
|
||||
return (y,network)
|
||||
|
||||
def update_operator_FromDisbursements(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'operatorCICBalance'
|
||||
x = s['operatorCICBalance']
|
||||
timestep = s['timestep']
|
||||
|
||||
distribute = _input['distribute']
|
||||
amount = _input['amount']
|
||||
|
||||
if distribute == 'Yes':
|
||||
totalDistribution = []
|
||||
for i,j in amount.items():
|
||||
totalDistribution.append(j)
|
||||
|
||||
totalDistribution = sum(totalDistribution)
|
||||
x = x - totalDistribution
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_totalDistributedToAgents(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'totalDistributedToAgents'
|
||||
x = s['totalDistributedToAgents']
|
||||
timestep = s['timestep']
|
||||
|
||||
distribute = _input['distribute']
|
||||
amount = _input['amount']
|
||||
|
||||
if distribute == 'Yes':
|
||||
totalDistribution = []
|
||||
for i,j in amount.items():
|
||||
totalDistribution.append(j)
|
||||
|
||||
totalDistribution = sum(totalDistribution)
|
||||
x = x + totalDistribution
|
||||
else:
|
||||
pass
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_operator_fiatBalance(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'operatorFiatBalance'
|
||||
x = s['operatorFiatBalance']
|
||||
fundsInProcess = s['fundsInProcess']
|
||||
timestep = s['timestep']
|
||||
if _input['fiatChange']:
|
||||
try:
|
||||
if fundsInProcess['timestep'][0] == timestep + 1:
|
||||
if fundsInProcess['decision'][0] == 'mint':
|
||||
x = x - abs(fundsInProcess['shilling'][0])
|
||||
elif fundsInProcess['decision'][0] == 'burn':
|
||||
x = x + abs(fundsInProcess['shilling'][0])
|
||||
else:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_operator_cicBalance(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'operatorCICBalance'
|
||||
x = s['operatorCICBalance']
|
||||
fundsInProcess = s['fundsInProcess']
|
||||
timestep = s['timestep']
|
||||
|
||||
if _input['cicChange']:
|
||||
try:
|
||||
if fundsInProcess['timestep'][0] == timestep + 1:
|
||||
if fundsInProcess['decision'][0] == 'mint':
|
||||
x = x + abs(fundsInProcess['cic'][0])
|
||||
elif fundsInProcess['decision'][0] == 'burn':
|
||||
x = x - abs(fundsInProcess['cic'][0])
|
||||
else:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_totalMinted(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'totalMinted'
|
||||
x = s['totalMinted']
|
||||
timestep = s['timestep']
|
||||
try:
|
||||
if _input['fundsInProcess']['decision'][0] == 'mint':
|
||||
x = x + abs(_input['fundsInProcess']['cic'][0])
|
||||
elif _input['fundsInProcess']['decision'][0] == 'burn':
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_totalBurned(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'totalBurned'
|
||||
x = s['totalBurned']
|
||||
timestep = s['timestep']
|
||||
try:
|
||||
if _input['fundsInProcess']['decision'][0] == 'burn':
|
||||
x = x + abs(_input['fundsInProcess']['cic'][0])
|
||||
elif _input['fundsInProcess']['decision'][0] == 'mint':
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_fundsInProcess(params,step,sL,s,_input):
|
||||
'''
|
||||
'''
|
||||
y = 'fundsInProcess'
|
||||
x = _input['fundsInProcess']
|
||||
timestep = s['timestep']
|
||||
|
||||
if _input['fundsInProcess']:
|
||||
try:
|
||||
if x['timestep'][0] == timestep:
|
||||
del x['timestep'][0]
|
||||
del x['decision'][0]
|
||||
del x['cic'][0]
|
||||
del x['shilling'][0]
|
||||
else:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
return (y,x)
|
||||
|
||||
|
|
@ -0,0 +1,442 @@
|
|||
import numpy as np
|
||||
from scipy.stats import gamma
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import pandas as pd
|
||||
|
||||
default_kappa= 4
|
||||
default_exit_tax = .02
|
||||
|
||||
#value function for a given state (R,S)
|
||||
def invariant(R,S,kappa=default_kappa):
|
||||
|
||||
return (S**kappa)/R
|
||||
|
||||
#given a value function (parameterized by kappa)
|
||||
#and an invariant coeficient V0
|
||||
#return Supply S as a function of reserve R
|
||||
def reserve(S, V0, kappa=default_kappa):
|
||||
return (S**kappa)/V0
|
||||
|
||||
#given a value function (parameterized by kappa)
|
||||
#and an invariant coeficient V0
|
||||
#return Supply S as a function of reserve R
|
||||
def supply(R, V0, kappa=default_kappa):
|
||||
return (V0*R)**(1/kappa)
|
||||
|
||||
#given a value function (parameterized by kappa)
|
||||
#and an invariant coeficient V0
|
||||
#return a spot price P as a function of reserve R
|
||||
def spot_price(R, V0, kappa=default_kappa):
|
||||
return kappa*R**((kappa-1)/kappa)/V0**(1/kappa)
|
||||
|
||||
#for a given state (R,S)
|
||||
#given a value function (parameterized by kappa)
|
||||
#and an invariant coeficient V0
|
||||
#deposit deltaR to Mint deltaS
|
||||
#with realized price deltaR/deltaS
|
||||
def mint(deltaR, R,S, V0, kappa=default_kappa):
|
||||
deltaS = (V0*(R+deltaR))**(1/kappa)-S
|
||||
if deltaS ==0:
|
||||
realized_price = spot_price(R+deltaR, V0, kappa)
|
||||
else:
|
||||
realized_price = deltaR/deltaS
|
||||
deltaS = round(deltaS,2)
|
||||
return deltaS, realized_price
|
||||
|
||||
#for a given state (R,S)
|
||||
#given a value function (parameterized by kappa)
|
||||
#and an invariant coeficient V0
|
||||
#burn deltaS to Withdraw deltaR
|
||||
#with realized price deltaR/deltaS
|
||||
def withdraw(deltaS, R,S, V0, kappa=default_kappa):
|
||||
deltaR = R-((S-deltaS)**kappa)/V0
|
||||
if deltaS ==0:
|
||||
realized_price = spot_price(R+deltaR, V0, kappa)
|
||||
else:
|
||||
realized_price = deltaR/deltaS
|
||||
deltaR = round(deltaR,2)
|
||||
return deltaR, realized_price
|
||||
|
||||
|
||||
|
||||
def iterateEdges(network,edgeToIterate):
|
||||
'''
|
||||
Description:
|
||||
Iterate through a network on a weighted edge and return
|
||||
two dictionaries: the inflow and outflow for the given agents
|
||||
in the format:
|
||||
|
||||
{'Agent':amount}
|
||||
'''
|
||||
outflows = {}
|
||||
inflows = {}
|
||||
for i,j in network.edges:
|
||||
try:
|
||||
amount = network[i][j][edgeToIterate]
|
||||
if i in outflows:
|
||||
outflows[i] = outflows[i] + amount
|
||||
else:
|
||||
outflows[i] = amount
|
||||
if j in inflows:
|
||||
inflows[j] = inflows[j] + amount
|
||||
else:
|
||||
inflows[j] = amount
|
||||
except:
|
||||
pass
|
||||
return outflows,inflows
|
||||
|
||||
|
||||
def inflowAndOutflowDictionaryMerge(inflow,outflow):
|
||||
'''
|
||||
Description:
|
||||
Merge two dictionaries and return one dictionary with zero floor'''
|
||||
|
||||
merged = {}
|
||||
|
||||
inflowsKeys = [k for k,v in inflow.items() if k not in outflow]
|
||||
for i in inflowsKeys:
|
||||
merged[i] = inflow[i]
|
||||
outflowsKeys = [k for k,v in outflow.items() if k not in inflow]
|
||||
for i in outflowsKeys:
|
||||
merged[i] = outflow[i]
|
||||
overlapKeys = [k for k,v in inflow.items() if k in outflow]
|
||||
for i in overlapKeys:
|
||||
amt = outflow[i] - inflow[i]
|
||||
if amt < 0:
|
||||
merged[i] = 0
|
||||
else:
|
||||
merged[i] = amt
|
||||
pass
|
||||
|
||||
return merged
|
||||
|
||||
|
||||
def spendCalculation(agentToPay,agentToReceive,rankOrderDemand,maxSpendCurrency,maxSpendTokens,cicPercentage):
|
||||
'''
|
||||
Function to calculate if an agent can pay for demand given token and currency contraints
|
||||
'''
|
||||
if (rankOrderDemand[agentToReceive] * (1-cicPercentage)) > maxSpendCurrency[agentToPay]:
|
||||
verdict_currency = 'No'
|
||||
else:
|
||||
verdict_currency = 'Enough'
|
||||
|
||||
if (rankOrderDemand[agentToReceive] * cicPercentage) > maxSpendTokens[agentToPay]:
|
||||
verdict_cic = 'No'
|
||||
else:
|
||||
verdict_cic = 'Enough'
|
||||
|
||||
if verdict_currency == 'Enough'and verdict_cic == 'Enough':
|
||||
spend = rankOrderDemand[agentToReceive]
|
||||
|
||||
elif maxSpendCurrency[agentToPay] > 0:
|
||||
spend = maxSpendCurrency[agentToPay]
|
||||
else:
|
||||
spend = 0
|
||||
|
||||
return spend
|
||||
|
||||
|
||||
def spendCalculationExternal(agentToPay,agentToReceive,rankOrderDemand,maxSpendCurrency):
|
||||
'''
|
||||
'''
|
||||
if rankOrderDemand[agentToReceive] > maxSpendCurrency[agentToPay]:
|
||||
verdict_currency = 'No'
|
||||
else:
|
||||
verdict_currency = 'Enough'
|
||||
|
||||
if verdict_currency == 'Enough':
|
||||
spend = rankOrderDemand[agentToReceive]
|
||||
|
||||
elif maxSpendCurrency[agentToPay] > 0:
|
||||
spend = maxSpendCurrency[agentToPay]
|
||||
else:
|
||||
spend = 0
|
||||
|
||||
return spend
|
||||
|
||||
|
||||
def DictionaryMergeAddition(inflow,outflow):
|
||||
'''
|
||||
Description:
|
||||
Merge two dictionaries and return one dictionary'''
|
||||
|
||||
merged = {}
|
||||
|
||||
inflowsKeys = [k for k,v in inflow.items() if k not in outflow]
|
||||
for i in inflowsKeys:
|
||||
merged[i] = inflow[i]
|
||||
outflowsKeys = [k for k,v in outflow.items() if k not in inflow]
|
||||
for i in outflowsKeys:
|
||||
merged[i] = outflow[i]
|
||||
overlapKeys = [k for k,v in inflow.items() if k in outflow]
|
||||
for i in overlapKeys:
|
||||
merged[i] = outflow[i] + inflow[i]
|
||||
|
||||
return merged
|
||||
|
||||
def mint_burn_logic_control(ideal,actual,variance,fiat,fiat_variance,ideal_fiat):
|
||||
'''
|
||||
Inventory control function to test if the current balance is in an acceptable range. Tolerance range
|
||||
'''
|
||||
if ideal - variance <= actual <= ideal + (2*variance):
|
||||
decision = 'none'
|
||||
amount = 0
|
||||
else:
|
||||
if (ideal + variance) > actual:
|
||||
decision = 'mint'
|
||||
amount = (ideal + variance) - actual
|
||||
else:
|
||||
pass
|
||||
if actual > (ideal + variance):
|
||||
decision = 'burn'
|
||||
amount = actual - (ideal + variance)
|
||||
else:
|
||||
pass
|
||||
|
||||
if decision == 'mint':
|
||||
if fiat < (ideal_fiat - fiat_variance):
|
||||
if amount > fiat:
|
||||
decision = 'none'
|
||||
amount = 0
|
||||
else:
|
||||
pass
|
||||
if decision == 'none':
|
||||
if fiat < (ideal_fiat - fiat_variance):
|
||||
decision = 'mint'
|
||||
amount = (ideal_fiat-fiat_variance)
|
||||
else:
|
||||
pass
|
||||
|
||||
amount = round(amount,2)
|
||||
return decision, amount
|
||||
|
||||
#NetworkX functions
|
||||
def get_nodes_by_type(g, node_type_selection):
|
||||
return [node for node in g.nodes if g.nodes[node]['type']== node_type_selection]
|
||||
|
||||
def get_edges_by_type(g, edge_type_selection):
|
||||
return [edge for edge in g.edges if g.edges[edge]['type']== edge_type_selection]
|
||||
|
||||
def get_edges(g):
|
||||
return [edge for edge in g.edges if g.edges[edge]]
|
||||
|
||||
def get_nodes(g):
|
||||
'''
|
||||
df.network.apply(lambda g: np.array([g.nodes[j]['balls'] for j in get_nodes(g)]))
|
||||
'''
|
||||
return [node for node in g.nodes if g.nodes[node]]
|
||||
|
||||
def aggregate_runs(df,aggregate_dimension):
|
||||
'''
|
||||
Function to aggregate the monte carlo runs along a single dimension.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
Example run:
|
||||
mean_df,median_df,std_df,min_df = aggregate_runs(df,'timestep')
|
||||
'''
|
||||
df = df[df['substep'] == df.substep.max()]
|
||||
mean_df = df.groupby(aggregate_dimension).mean().reset_index()
|
||||
median_df = df.groupby(aggregate_dimension).median().reset_index()
|
||||
std_df = df.groupby(aggregate_dimension).std().reset_index()
|
||||
min_df = df.groupby(aggregate_dimension).min().reset_index()
|
||||
|
||||
return mean_df,median_df,std_df,min_df
|
||||
|
||||
|
||||
|
||||
def plot_averaged_runs(df,aggregate_dimension,x, y,run_count,lx=False,ly=False, suppMin=False):
|
||||
'''
|
||||
Function to plot the mean, median, etc of the monte carlo runs along a single variable.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
x = x axis variable for plotting
|
||||
y = y axis variable for plotting
|
||||
run_count = the number of monte carlo simulations
|
||||
lx = True/False for if the x axis should be logged
|
||||
ly = True/False for if the x axis should be logged
|
||||
suppMin: True/False for if the miniumum value should be plotted
|
||||
Note: Run aggregate_runs before using this function
|
||||
Example run:
|
||||
'''
|
||||
mean_df,median_df,std_df,min_df = aggregate_runs(df,aggregate_dimension)
|
||||
|
||||
plt.figure(figsize=(10,6))
|
||||
if not(suppMin):
|
||||
plt.plot(mean_df[x].values, mean_df[y].values,
|
||||
mean_df[x].values,median_df[y].values,
|
||||
mean_df[x].values,mean_df[y].values+std_df[y].values,
|
||||
mean_df[x].values,min_df[y].values)
|
||||
plt.legend(['mean', 'median', 'mean+ 1*std', 'min'],bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
|
||||
|
||||
else:
|
||||
plt.plot(mean_df[x].values, mean_df[y].values,
|
||||
mean_df[x].values,median_df[y].values,
|
||||
mean_df[x].values,mean_df[y].values+std_df[y].values,
|
||||
mean_df[x].values,mean_df[y].values-std_df[y].values)
|
||||
plt.legend(['mean', 'median', 'mean+ 1*std', 'mean - 1*std'],bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
|
||||
|
||||
plt.xlabel(x)
|
||||
plt.ylabel(y)
|
||||
title_text = 'Performance of ' + y + ' over all of ' + str(run_count) + ' Monte Carlo runs'
|
||||
plt.title(title_text)
|
||||
if lx:
|
||||
plt.xscale('log')
|
||||
|
||||
if ly:
|
||||
plt.yscale('log')
|
||||
|
||||
def plot_median_with_quantiles(df,aggregate_dimension,x, y):
|
||||
'''
|
||||
Function to plot the median and 1st and 3rd quartiles of the monte carlo runs along a single variable.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
x = x axis variable for plotting
|
||||
y = y axis variable for plotting
|
||||
|
||||
Example run:
|
||||
plot_median_with_quantiles(df,'timestep','timestep','AggregatedAgentSpend')
|
||||
'''
|
||||
|
||||
df = df[df['substep'] == df.substep.max()]
|
||||
firstQuantile = df.groupby(aggregate_dimension).quantile(0.25).reset_index()
|
||||
thirdQuantile = df.groupby(aggregate_dimension).quantile(0.75).reset_index()
|
||||
median_df = df.groupby(aggregate_dimension).median().reset_index()
|
||||
|
||||
fig, ax = plt.subplots(1,figsize=(10,6))
|
||||
ax.plot(median_df[x].values, median_df[y].values, lw=2, label='Median', color='blue')
|
||||
ax.fill_between(firstQuantile[x].values, firstQuantile[y].values, thirdQuantile[y].values, facecolor='black', alpha=0.2)
|
||||
ax.set_title(y + ' Median')
|
||||
ax.legend(loc='upper left')
|
||||
ax.set_xlabel('Timestep')
|
||||
ax.set_ylabel('Amount')
|
||||
ax.grid()
|
||||
|
||||
def plot_median_with_quantiles_annotation(df,aggregate_dimension,x, y):
|
||||
'''
|
||||
Function to plot the median and 1st and 3rd quartiles of the monte carlo runs along a single variable.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
x = x axis variable for plotting
|
||||
y = y axis variable for plotting
|
||||
|
||||
Example run:
|
||||
plot_median_with_quantiles(df,'timestep','timestep','AggregatedAgentSpend')
|
||||
'''
|
||||
|
||||
df = df[df['substep'] == df.substep.max()]
|
||||
firstQuantile = df.groupby(aggregate_dimension).quantile(0.25).reset_index()
|
||||
thirdQuantile = df.groupby(aggregate_dimension).quantile(0.75).reset_index()
|
||||
median_df = df.groupby(aggregate_dimension).median().reset_index()
|
||||
|
||||
fig, ax = plt.subplots(1,figsize=(10,6))
|
||||
ax.axvline(x=30,linewidth=2, color='r')
|
||||
ax.annotate('Agents can withdraw and Red Cross Drip occurs', xy=(30,2), xytext=(35, 1),
|
||||
arrowprops=dict(facecolor='black', shrink=0.05))
|
||||
|
||||
ax.axvline(x=60,linewidth=2, color='r')
|
||||
ax.axvline(x=90,linewidth=2, color='r')
|
||||
ax.plot(median_df[x].values, median_df[y].values, lw=2, label='Median', color='blue')
|
||||
ax.fill_between(firstQuantile[x].values, firstQuantile[y].values, thirdQuantile[y].values, facecolor='black', alpha=0.2)
|
||||
ax.set_title(y + ' Median')
|
||||
ax.legend(loc='upper left')
|
||||
ax.set_xlabel('Timestep')
|
||||
ax.set_ylabel('Amount')
|
||||
ax.grid()
|
||||
|
||||
|
||||
def first_five_plot(df,aggregate_dimension,x,y,run_count):
|
||||
'''
|
||||
A function that generates timeseries plot of at most the first five Monte Carlo runs.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
x = x axis variable for plotting
|
||||
y = y axis variable for plotting
|
||||
run_count = the number of monte carlo simulations
|
||||
Note: Run aggregate_runs before using this function
|
||||
Example run:
|
||||
first_five_plot(df,'timestep','timestep','revenue',run_count=100)
|
||||
'''
|
||||
mean_df,median_df,std_df,min_df = aggregate_runs(df,aggregate_dimension)
|
||||
plt.figure(figsize=(10,6))
|
||||
if run_count < 5:
|
||||
runs = run_count
|
||||
else:
|
||||
runs = 5
|
||||
for r in range(1,runs+1):
|
||||
legend_name = 'Run ' + str(r)
|
||||
plt.plot(df[df.run==r].timestep, df[df.run==r][y], label = legend_name )
|
||||
plt.plot(mean_df[x], mean_df[y], label = 'Mean', color = 'black')
|
||||
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
|
||||
plt.xlabel(x)
|
||||
plt.ylabel(y)
|
||||
title_text = 'Performance of ' + y + ' over the First ' + str(runs) + ' Monte Carlo Runs'
|
||||
plt.title(title_text)
|
||||
#plt.savefig(y +'_FirstFiveRuns.jpeg')
|
||||
|
||||
|
||||
def aggregate_runs_param_mc(df,aggregate_dimension):
|
||||
'''
|
||||
Function to aggregate the monte carlo runs along a single dimension.
|
||||
Parameters:
|
||||
df: dataframe name
|
||||
aggregate_dimension: the dimension you would like to aggregate on, the standard one is timestep.
|
||||
Example run:
|
||||
mean_df,median_df,std_df,min_df = aggregate_runs(df,'timestep')
|
||||
'''
|
||||
df = df[df['substep'] == df.substep.max()]
|
||||
mean_df = df.groupby(aggregate_dimension).mean().reset_index()
|
||||
median_df = df.groupby(aggregate_dimension).median().reset_index()
|
||||
#min_df = df.groupby(aggregate_dimension).min().reset_index()
|
||||
#max_df = df.groupby(aggregate_dimension).max().reset_index()
|
||||
return mean_df,median_df
|
||||
|
||||
def param_dfs(results,params,swept):
|
||||
mean_df,median_df = aggregate_runs_param_mc(results[0]['result'],'timestep')
|
||||
mean_df[swept] = params[0]
|
||||
median_df[swept] = params[0]
|
||||
#max_df[swept] = params[0]
|
||||
#min_df[swept] = params[0]
|
||||
for i in range(1,len(params)):
|
||||
mean_df_intermediate,median_df_intermediate = aggregate_runs_param_mc(results[i]['result'],'timestep')
|
||||
mean_df_intermediate[swept] = params[i]
|
||||
median_df_intermediate[swept] = params[i]
|
||||
#max_df_intermediate[swept] = params[i]
|
||||
#min_df_intermediate[swept] = params[i]
|
||||
mean_df= pd.concat([mean_df, mean_df_intermediate])
|
||||
median_df= pd.concat([median_df, median_df_intermediate])
|
||||
#max_df= pd.concat([max_df, max_df_intermediate])
|
||||
#min_df= pd.concat([min_df, min_df_intermediate])
|
||||
return mean_df,median_df
|
||||
|
||||
|
||||
def param_plot(results,state_var_x, state_var_y, parameter, save_plot = False,**kwargs):
|
||||
'''
|
||||
Results (df) is the dataframe (concatenated list of results dictionaries)
|
||||
length = intreger, number of parameter values
|
||||
Enter state variable name as a string for x and y. Enter the swept parameter name as a string.
|
||||
y_label kwarg for custom y-label and title reference
|
||||
x_label kwarg for custom x-axis label
|
||||
'''
|
||||
sns.scatterplot(x=state_var_x, y = state_var_y, hue = parameter, style= parameter, palette = 'coolwarm',alpha=1, data = results, legend="full")
|
||||
title_text = 'Effect of ' + parameter + ' Parameter Sweep on ' + state_var_y
|
||||
for key, value in kwargs.items():
|
||||
if key == 'y_label':
|
||||
plt.ylabel(value)
|
||||
title_text = 'Effect of ' + parameter + ' Parameter Sweep on ' + value
|
||||
if key == 'x_label':
|
||||
plt.xlabel(value)
|
||||
plt.title(title_text)
|
||||
if save_plot == True:
|
||||
filename = state_var_y + state_var_x + parameter + 'plot.png'
|
||||
# # plt.savefig('static/images/' + filename)
|
||||
# plt.savefig(filename)
|
||||
lgd = plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
|
||||
#title_text = 'Market Volatility versus Normalized Liquid Token Supply for All Runs'
|
||||
plt.title(title_text)
|
||||
plt.savefig('static/images/' + filename, bbox_extra_artists=(lgd,), bbox_inches='tight')
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from cadCAD.configuration.utils import access_block
|
||||
from .initialization import *
|
||||
from .supportingFunctions import *
|
||||
from collections import OrderedDict
|
||||
|
||||
# Parameters
|
||||
agentsMinus = 2
|
||||
# percentage of balance a user can redeem
|
||||
redeemPercentage = 0.5
|
||||
|
||||
# Behaviors
|
||||
def choose_agents(params, step, sL, s):
|
||||
'''
|
||||
Choose agents to interact during the given timestep and create their demand from a uniform distribution.
|
||||
Based on probability, choose utility.
|
||||
'''
|
||||
outboundAgents = np.random.choice(mixingAgents,size=len(mixingAgents)-agentsMinus).tolist()
|
||||
inboundAgents = np.random.choice(mixingAgents,size=len(mixingAgents)-agentsMinus).tolist()
|
||||
stepDemands = np.random.uniform(low=1, high=500, size=len(mixingAgents)-agentsMinus).astype(int)
|
||||
|
||||
|
||||
stepUtilities = np.random.choice(list(UtilityTypesOrdered.keys()),size=len(mixingAgents)-agentsMinus,p=list(utilityTypesProbability.values())).tolist()
|
||||
|
||||
return {'outboundAgents':outboundAgents,'inboundAgents':inboundAgents,'stepDemands':stepDemands,'stepUtilities':stepUtilities}
|
||||
|
||||
|
||||
def spend_allocation(params, step, sL, s):
|
||||
'''
|
||||
Take mixing agents, demand, and utilities and allocate agent shillings and tokens based on utility and scarcity.
|
||||
'''
|
||||
# instantiate network state
|
||||
network = s['network']
|
||||
|
||||
spendI = []
|
||||
spendJ = []
|
||||
spendAmount = []
|
||||
|
||||
# calculate max about of spend available to each agent
|
||||
maxSpendShilling = {}
|
||||
for i in mixingAgents:
|
||||
maxSpendShilling[i] = network.nodes[i]['native_currency']
|
||||
|
||||
maxSpendCIC = {}
|
||||
for i in mixingAgents:
|
||||
maxSpendCIC[i] = network.nodes[i]['tokens']
|
||||
|
||||
|
||||
for i in mixingAgents:
|
||||
rankOrder = {}
|
||||
rankOrderDemand = {}
|
||||
for j in network.adj[i]:
|
||||
try:
|
||||
rankOrder[j] = UtilityTypesOrdered[network.adj[i][j]['utility']]
|
||||
rankOrderDemand[j] = network.adj[i][j]['demand']
|
||||
rankOrder = dict(OrderedDict(sorted(rankOrder.items(), key=lambda v: v, reverse=False)))
|
||||
for k in rankOrder:
|
||||
# if i or j is external, we transact 100% in shilling
|
||||
if i == 'external':
|
||||
amt = spendCalculationExternal(i,j,rankOrderDemand,maxSpendShilling)
|
||||
spendI.append(i)
|
||||
spendJ.append(j)
|
||||
spendAmount.append(amt)
|
||||
maxSpendShilling[i] = maxSpendShilling[i] - amt
|
||||
elif j == 'external':
|
||||
amt = spendCalculationExternal(i,j,rankOrderDemand,maxSpendShilling)
|
||||
spendI.append(i)
|
||||
spendJ.append(j)
|
||||
spendAmount.append(amt)
|
||||
maxSpendShilling[i] = maxSpendShilling[i] - amt
|
||||
else:
|
||||
amt = spendCalculation(i,j,rankOrderDemand,maxSpendShilling,maxSpendCIC,fractionOfDemandInCIC)
|
||||
spendI.append(i)
|
||||
spendJ.append(j)
|
||||
spendAmount.append(amt)
|
||||
maxSpendShilling[i] = maxSpendShilling[i] - amt * (1- fractionOfDemandInCIC)
|
||||
maxSpendCIC[i] = maxSpendCIC[i] - (amt * fractionOfDemandInCIC)
|
||||
except:
|
||||
pass
|
||||
return {'spendI':spendI,'spendJ':spendJ,'spendAmount':spendAmount}
|
||||
|
||||
|
||||
def withdraw_calculation(params, step, sL, s):
|
||||
''''''
|
||||
# instantiate network state
|
||||
network = s['network']
|
||||
|
||||
# Assumptions:
|
||||
# * user is only able to withdraw up to 50% of balance, assuming they have spent 50% of balance
|
||||
# * Agents will withdraw as much as they can.
|
||||
withdraw = {}
|
||||
|
||||
fiftyThreshold = {}
|
||||
|
||||
startingBalance = s['startingBalance']
|
||||
|
||||
spend = s['30_day_spend']
|
||||
timestep = s['timestep']
|
||||
|
||||
division = timestep % 30 == 0
|
||||
|
||||
if division == True:
|
||||
for i,j in startingBalance.items():
|
||||
fiftyThreshold[i] = j * 0.5
|
||||
if s['timestep'] > 7:
|
||||
for i,j in fiftyThreshold.items():
|
||||
if spend[i] > 0 and fiftyThreshold[i] > 0:
|
||||
if spend[i] * fractionOfActualSpendInCIC >= fiftyThreshold[i]:
|
||||
spent = spend[i]
|
||||
amount = spent * redeemPercentage
|
||||
if network.nodes[i]['tokens'] > amount:
|
||||
withdraw[i] = amount
|
||||
elif network.nodes[i]['tokens'] < amount:
|
||||
withdraw[i] = network.nodes[i]['tokens']
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
return {'withdraw':withdraw}
|
||||
|
||||
# Mechanisms
|
||||
def update_agent_activity(params,step,sL,s,_input):
|
||||
'''
|
||||
Update the network for interacting agent, their demand, and utility.
|
||||
'''
|
||||
y = 'network'
|
||||
network = s['network']
|
||||
|
||||
outboundAgents = _input['outboundAgents']
|
||||
inboundAgents = _input['inboundAgents']
|
||||
stepDemands = _input['stepDemands']
|
||||
stepUtilities = _input['stepUtilities']
|
||||
|
||||
# create demand edge weights
|
||||
try:
|
||||
for i,j,l in zip(outboundAgents,inboundAgents,stepDemands):
|
||||
network[i][j]['demand'] = l
|
||||
except:
|
||||
pass
|
||||
|
||||
# Create cic % edge weights
|
||||
try:
|
||||
for i,j in zip(outboundAgents,inboundAgents):
|
||||
# if one of the agents is external, we will transact in 100% shilling
|
||||
if i == 'external':
|
||||
network[i][j]['fractionOfDemandInCIC'] = 1
|
||||
elif j == 'external':
|
||||
network[i][j]['fractionOfDemandInCIC'] = 1
|
||||
else:
|
||||
network[i][j]['fractionOfDemandInCIC'] = fractionOfDemandInCIC
|
||||
except:
|
||||
pass
|
||||
|
||||
# Create utility edge types
|
||||
try:
|
||||
for i,j,l in zip(outboundAgents,inboundAgents,stepUtilities):
|
||||
network[i][j]['utility'] = l
|
||||
except:
|
||||
pass
|
||||
|
||||
x = network
|
||||
return (y,x)
|
||||
|
||||
|
||||
def update_outboundAgents(params,step,sL,s,_input):
|
||||
'''
|
||||
Update outBoundAgents state variable
|
||||
'''
|
||||
y = 'outboundAgents'
|
||||
|
||||
x = _input['outboundAgents']
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_inboundAgents(params,step,sL,s,_input):
|
||||
'''
|
||||
Update inBoundAgents state variable
|
||||
'''
|
||||
y = 'inboundAgents'
|
||||
|
||||
x = _input['inboundAgents']
|
||||
return (y,x)
|
||||
|
||||
|
||||
def update_node_spend(params, step, sL, s,_input):
|
||||
'''
|
||||
Update network with actual spend of agents.
|
||||
'''
|
||||
y = 'network'
|
||||
network = s['network']
|
||||
|
||||
spendI = _input['spendI']
|
||||
spendJ = _input['spendJ']
|
||||
spendAmount = _input['spendAmount']
|
||||
|
||||
for i,j,l in zip(spendI,spendJ,spendAmount):
|
||||
network[i][j]['spend'] = l
|
||||
if i == 'external':
|
||||
network[i][j]['fractionOfActualSpendInCIC'] = 1
|
||||
elif j == 'external':
|
||||
network[i][j]['fractionOfActualSpendInCIC'] = 1
|
||||
else:
|
||||
network[i][j]['fractionOfActualSpendInCIC'] = fractionOfActualSpendInCIC
|
||||
|
||||
outflowSpend, inflowSpend = iterateEdges(network,'spend')
|
||||
|
||||
for i, j in inflowSpend.items():
|
||||
if i == 'external':
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] + inflowSpend[i]
|
||||
elif j == 'external':
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] + inflowSpend[i]
|
||||
else:
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] + inflowSpend[i] * (1- fractionOfDemandInCIC)
|
||||
network.nodes[i]['tokens'] = network.nodes[i]['tokens'] + (inflowSpend[i] * fractionOfDemandInCIC)
|
||||
|
||||
for i, j in outflowSpend.items():
|
||||
if i == 'external':
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] - outflowSpend[i]
|
||||
elif j == 'external':
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] - outflowSpend[i]
|
||||
else:
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] - outflowSpend[i]* (1- fractionOfDemandInCIC)
|
||||
network.nodes[i]['tokens'] = network.nodes[i]['tokens'] - (outflowSpend[i] * fractionOfDemandInCIC)
|
||||
|
||||
# Store the net of the inflow and outflow per step
|
||||
network.nodes['external']['delta_native_currency'] = sum(inflowSpend.values()) - sum(outflowSpend.values())
|
||||
|
||||
x = network
|
||||
return (y,x)
|
||||
|
||||
|
||||
def update_withdraw(params, step, sL, s,_input):
|
||||
'''
|
||||
Update flow sstate variable with the aggregated amount of shillings withdrawn
|
||||
'''
|
||||
y = 'withdraw'
|
||||
x = s['withdraw']
|
||||
if _input['withdraw']:
|
||||
x = _input['withdraw']
|
||||
else:
|
||||
x = 0
|
||||
|
||||
return (y,x)
|
||||
|
||||
def update_network_withraw(params, step, sL, s,_input):
|
||||
'''
|
||||
Update network for agents withdrawing
|
||||
'''
|
||||
y = 'network'
|
||||
network = s['network']
|
||||
withdraw = _input['withdraw']
|
||||
|
||||
if withdraw:
|
||||
for i,j in withdraw.items():
|
||||
# update agent nodes
|
||||
network.nodes[i]['tokens'] = network.nodes[i]['tokens'] - j
|
||||
network.nodes[i]['native_currency'] = network.nodes[i]['native_currency'] + (j * leverage)
|
||||
|
||||
withdrawnCICSum = []
|
||||
for i,j in withdraw.items():
|
||||
withdrawnCICSum.append(j)
|
||||
|
||||
# update cic node
|
||||
network.nodes['cic']['native_currency'] = network.nodes[i]['native_currency'] - (sum(withdrawnCICSum) * leverage)
|
||||
network.nodes['cic']['tokens'] = network.nodes[i]['tokens'] + (sum(withdrawnCICSum) * leverage)
|
||||
|
||||
else:
|
||||
pass
|
||||
x = network
|
||||
return (y,x)
|
||||
|
||||