From af9be1b493a3161c792cd02b4fae911c5a9d85fd Mon Sep 17 00:00:00 2001 From: charwick <1117120+charwick@users.noreply.github.com> Date: Tue, 26 Jul 2022 23:40:55 -0400 Subject: [PATCH] Option for agents to run negative balances --- helipad/agent.py | 102 ++++++++++++++++++++------------------- helipad/cpanelJupyter.py | 2 +- helipad/cpanelTkinter.py | 2 +- helipad/model.py | 11 +++-- helipad/param.py | 2 +- 5 files changed, 62 insertions(+), 57 deletions(-) diff --git a/helipad/agent.py b/helipad/agent.py index 906ccc5..e993dc6 100644 --- a/helipad/agent.py +++ b/helipad/agent.py @@ -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 @@ -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 diff --git a/helipad/cpanelJupyter.py b/helipad/cpanelJupyter.py index 08bee91..2825e5b 100644 --- a/helipad/cpanelJupyter.py +++ b/helipad/cpanelJupyter.py @@ -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]) diff --git a/helipad/cpanelTkinter.py b/helipad/cpanelTkinter.py index 5607b7d..040d8ab 100644 --- a/helipad/cpanelTkinter.py +++ b/helipad/cpanelTkinter.py @@ -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(): diff --git a/helipad/model.py b/helipad/model.py index 62565ab..2760540 100644 --- a/helipad/model.py +++ b/helipad/model.py @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/helipad/param.py b/helipad/param.py index 3dada2c..222a428 100644 --- a/helipad/param.py +++ b/helipad/param.py @@ -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):