This commit is contained in:
Andrew Clark 2020-05-10 15:40:47 -04:00
parent 99f94f2aa9
commit 9195bd14bd
43 changed files with 10446 additions and 0 deletions

View File

@ -1,2 +1,9 @@
# Community_Inclusion_Currencies
Repository for Complex Systems model of the Grassroots Economics Community Inclusion Currency project.
### Simulation work
[Click here](Simulation/CIC Network cadCAD model.ipynb)
### Theory work
[Click here](Theory/cic_initialization.ipynb)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
Simulation/images/graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View File

@ -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]] = {
'month': [0,12,36,50,100]
}
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
)

View File

@ -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
}

View File

@ -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
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -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 % 30 == 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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,378 @@
import numpy as np
from scipy.stats import gamma
import matplotlib.pyplot as plt
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')

View File

@ -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)

File diff suppressed because one or more lines are too long

BIN
Theory/CICecosubsystem.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
Theory/CICecosystem.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
Theory/CICinvariant.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

File diff suppressed because one or more lines are too long