Skip to content

Commit

Permalink
Option for agents to run negative balances
Browse files Browse the repository at this point in the history
  • Loading branch information
charwick committed Jul 27, 2022
1 parent 4286ebf commit af9be1b
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 57 deletions.
102 changes: 52 additions & 50 deletions helipad/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,37 +55,38 @@ def trade(self, partner, good1, amt1, good2, amt2):
message = None
prim1, prim2 = self.primitive.title(), partner.primitive.title()
if amt2 != 0: price = amt1 / amt2
if amt1 > self.stocks[good1]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim1, self.id, good1, prim2, partner.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good1, self.stocks[good1])
amt1 = self.stocks[good1]
if amt2 != 0: amt2 = amt1 / price
elif -amt1 > partner.stocks[good1]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim2, partner.id, good1, prim1, self.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good1, partner.stocks[good1])
amt1 = -partner.stocks[good1]
if amt2 != 0: amt2 = amt1 / price
if amt2 > partner.stocks[good2]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim2, partner.id, good2, prim1, self.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good2, partner.stocks[good2])
amt2 = partner.stocks[good2]
amt1 = price * amt2
elif -amt2 > self.stocks[good2]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim1, self.id, good2, prim2, partner.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good2, self.stocks[good2])
amt2 = -self.stocks[good2]
amt1 = price * amt2

if message is not None:
if self.overdraft == 'stop': raise ValueError(message)
if 'fail' in self.overdraft:
go = False
message += _(' Cancelling trade…')
elif 'warn' in self.overdraft: warnings.warn(message, None, 2)
if self.overdraft != 'allow':
if amt1 > self.stocks[good1]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim1, self.id, good1, prim2, partner.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good1, self.stocks[good1])
amt1 = self.stocks[good1]
if amt2 != 0: amt2 = amt1 / price
elif -amt1 > partner.stocks[good1]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim2, partner.id, good1, prim1, self.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good1, partner.stocks[good1])
amt1 = -partner.stocks[good1]
if amt2 != 0: amt2 = amt1 / price
if amt2 > partner.stocks[good2]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim2, partner.id, good2, prim1, self.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good2, partner.stocks[good2])
amt2 = partner.stocks[good2]
amt1 = price * amt2
elif -amt2 > self.stocks[good2]:
message = _('{0} {1} does not have sufficient {2} to give {3} {4}.').format(prim1, self.id, good2, prim2, partner.id)
if 'continue' in self.overdraft:
message += _(' Continuing with available {0} of {1}…').format(good2, self.stocks[good2])
amt2 = -self.stocks[good2]
amt1 = price * amt2

if message is not None:
if self.overdraft == 'stop': raise ValueError(message)
if 'fail' in self.overdraft:
go = False
message += _(' Cancelling trade…')
elif 'warn' in self.overdraft: warnings.warn(message, None, 2)

if go:
self.stocks[good1] -= amt1
Expand Down Expand Up @@ -124,25 +125,26 @@ def pay(self, recipient, amount):
#Budget constraints
prim1, prim2 = self.primitive.title(), recipient.primitive.title()
message = None
if amount > self.balance:
message = _('{0} {1} does not have sufficient funds to pay {2} {3}.').format(prim1, self.id, prim2, recipient.id)
if self.overdraft == 'stop': raise ValueError(message)
if 'continue' in self.overdraft:
amount = self.balance
message += _(' Continuing with available balance of {}…').format(self.balance)
elif -amount > recipient.balance:
message = _('{0} {1} does not have sufficient funds to pay {2} {3}.').format(prim2, recipient.id, prim1, self.id)
if 'continue' in self.overdraft:
amount = -recipient.balance
message += _(' Continuing with available balance of {}…').format(recipient.balance)

if message is not None:
if self.overdraft == 'stop': raise ValueError(message)
if 'fail' in self.overdraft:
go = False
message += _(' Cancelling trade…')
if 'warn' in self.overdraft:
warnings.warn(message, None, 2)
if self.overdraft != 'allow':
if amount > self.balance:
message = _('{0} {1} does not have sufficient funds to pay {2} {3}.').format(prim1, self.id, prim2, recipient.id)
if self.overdraft == 'stop': raise ValueError(message)
if 'continue' in self.overdraft:
amount = self.balance
message += _(' Continuing with available balance of {}…').format(self.balance)
elif -amount > recipient.balance:
message = _('{0} {1} does not have sufficient funds to pay {2} {3}.').format(prim2, recipient.id, prim1, self.id)
if 'continue' in self.overdraft:
amount = -recipient.balance
message += _(' Continuing with available balance of {}…').format(recipient.balance)

if message is not None:
if self.overdraft == 'stop': raise ValueError(message)
if 'fail' in self.overdraft:
go = False
message += _(' Cancelling trade…')
if 'warn' in self.overdraft:
warnings.warn(message, None, 2)

if go and amount:
recipient.stocks[self.model.goods.money] += amount
Expand Down
2 changes: 1 addition & 1 deletion helipad/cpanelJupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def sfuncButton(slef, *args): slef.do(self.model)
children.append(HBox(buttons))
model.params['shocks'].element = Accordion(children=[VBox(children)])
model.shocks.element = model.params['shocks'].element
model.params['shocks'].element.set_title(0, _('Shocks'))
model.params['shocks'].element.set_title(0, model.params['shocks'].title)
self.children += model.params['shocks'].element,

cbot = self.model.doHooks('CpanelBottom', [self, None])
Expand Down
2 changes: 1 addition & 1 deletion helipad/cpanelTkinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def renderParam(frame, param, item=None, bg='#EEEEEE'):

#Shock checkboxes and buttons
if len(self.model.shocks):
self.model.shocks.element = expandableFrame(self, text=_('Shocks'), padx=5, pady=8, font=font, bg=bgcolors[fnum%2])
self.model.shocks.element = expandableFrame(self, text=self.model.shocks.title, padx=5, pady=8, font=font, bg=bgcolors[fnum%2])
self.model.shocks.element.checks = {}
self.model.params['shocks'].element = self.model.shocks.element
for shock in self.model.shocks.shocksExceptButtons.values():
Expand Down
11 changes: 7 additions & 4 deletions helipad/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from random import shuffle, choice
from numpy import random

from helipad.visualize import BaseVisualization
from helipad.visualize import BaseVisualization, Charts, TimeSeries
from helipad.helpers import *
from helipad.param import Params, Shocks
from helipad.data import Data
Expand All @@ -19,11 +19,15 @@ class Helipad:
runInit = True #for multiple inheritance. Has to be a static property

def __init__(self, locale='en'):
#Have to do this first so that _() is available early
if not hasattr(self, 'breed'):
gettext.translation('helipad', localedir=os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))+'/locales', languages=[locale]).install()

self.data = Data(self)
self.params = Params(self)
self.shocks = Shocks(self)
self.events = Events()
self.hooks = Hooks()
self.params = Params(self)
self.primitives = Primitives(self)
self.goods = Goods(self) #List of goods

Expand Down Expand Up @@ -54,7 +58,6 @@ def eventdec(name, fn, kwargs): self.events.add(name, fn, **kwargs)

#A few things that only make sense to do if it's the topmost model
if not hasattr(self, 'breed'):
gettext.translation('helipad', localedir=os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))+'/locales', languages=[locale]).install()

#Privileged parameters
#Toggle the progress bar between determinate and indeterminate when stopafter gets changed
Expand Down Expand Up @@ -170,7 +173,7 @@ def reporter(model): return param.get(item)

if self.goods.money is not None:
self.data.addReporter('M0', self.data.agentReporter('stocks', 'all', good=self.goods.money, stat='sum'))
if self.visual is not None and hasattr(self.visual, 'plots') and 'money' in self.visual.plots:
if self.visual is not None and isinstance(self.visual, TimeSeries) and hasattr(self.visual, 'plots') and 'money' in self.visual.plots:
self.visual.plots['money'].addSeries('M0', _('Monetary Base'), self.goods[self.goods.money].color)

#Unconditional variables to report
Expand Down
2 changes: 1 addition & 1 deletion helipad/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ class Shocks(CheckgridParam, fStoreWithInterface):

def __init__(self, model):
dict.__init__(self)
CheckgridParam.__init__(self, name='shocks', title='Shocks', type='checkgrid', opts={}, dflt={}, runtime=True, config=True, per=None)
CheckgridParam.__init__(self, name='shocks', title=_('Shocks'), type='checkgrid', opts={}, dflt={}, runtime=True, config=True, per=None)
self.model = model

class Shock(Item):
Expand Down

0 comments on commit af9be1b

Please sign in to comment.