from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from models import Constants class InstructionsBank(Page): def is_displayed(self): return self.round_number == 1 and self.player.get_role() in ['Region I', 'Region II'] def vars_for_template(self): if self.session.config['has_accountant']: number_other = (Constants.players_per_group / 2) - 1 else: number_other = Constants.players_per_group - 1 return dict( has_accountant=self.session.config['has_accountant'], forecast_prize=c(self.session.config['forecast_prize']).to_real_world_currency(self.session), number_other=number_other, endowment=self.session.config['endowment'], multiplier=self.session.config['asset_return'], allocation_prize=c(self.session.config['allocation_prize']).to_real_world_currency(self.session), max_payoff=c(2 * self.session.config['allocation_prize']).to_real_world_currency(self.session), ) def before_next_page(self): self.player.set_default() class InstructionsAccountant(Page): def is_displayed(self): return self.round_number == 1 and self.player.get_role() in ['Region I Accountant', 'Region II Accountant'] def vars_for_template(self): return dict( forecast_prize=c(self.session.config['forecast_prize']).to_real_world_currency(self.session), endowment=self.session.config['endowment'], multiplier=self.session.config['asset_return'], allocation_prize=c(self.session.config['allocation_prize']).to_real_world_currency(self.session), max_payoff=c(2 * self.session.config['allocation_prize']).to_real_world_currency(self.session), accountant_prize=c(self.session.config['accountant_prize']).to_real_world_currency(self.session), ) def before_next_page(self): self.player.set_default() class Period1Bank(Page): form_model = 'player' form_fields = ['asset'] def is_displayed(self): return self.player.get_role() in ['Region I', 'Region II'] def vars_for_template(self): region = self.player.get_role() return dict( region=region, deposit=self.session.config['deposit'], default_asset=self.player.set_default(), default_cash=self.session.config['deposit'] - self.player.default_asset, round_number=self.player.round_number, ) def before_next_page(self): self.player.initial_asset = self.player.asset self.player.cash = self.session.config['deposit']-self.player.asset class BetweenPeriods(WaitPage): def after_all_players_arrive(self): self.group.set_state() for player in self.group.get_players(): player.set_impatient() if player.get_role() in ['Region I Accountant', 'Region II Accountant']: partner = player.get_partner() player.cash = partner.cash player.asset = partner.asset player.initial_asset = partner.initial_asset class Period2Bank(Page): def is_displayed(self): return self.player.get_role() in ['Region I', 'Region II'] def vars_for_template(self): region = self.player.get_role() return dict( region=region, round_number=self.player.round_number, cash=self.session.config['deposit'] - self.player.asset, asset=self.player.asset, impatient=self.player.impatient, multiplier=self.session.config['asset_return'], default_price=self.session.config['default_price'], ) class Period2Accountant(Page): def is_displayed(self): return self.player.get_role() in ['Region I Accountant', 'Region II Accountant'] def vars_for_template(self): # Remove the word "Accountant" region = self.player.get_role()[:-11] return dict( region=region, round_number=self.player.round_number, cash=self.session.config['deposit'] - self.player.asset, asset=self.player.asset, impatient=self.player.impatient, multiplier=self.session.config['asset_return'], default_price=self.session.config['default_price'], ) class buyOrSell(Page): form_model = 'player' form_fields = ['decision', 'bid', 'price_guess'] def is_displayed(self): return self.player.get_role() in ['Region I', 'Region II'] def error_message(self, values): if values['bid'] < 0: return "Your offer cannot be less than 0." elif values['decision'] == 'Buy' and values['bid'] > self.player.cash: return "You cannot bid more than the " + str(self.player.cash) + " you have in cash." elif values['decision'] == 'Sell' and values['bid'] > self.player.asset: return "You cannot offer to sell more than the " + str(self.player.asset) + " you have in assets." elif values['price_guess'] < 0: return "You cannot predict a negative price for the asset." def vars_for_template(self): return dict( round_number=self.player.round_number, deposit=self.session.config['deposit'], cash=self.session.config['deposit'] - self.player.asset, asset=self.player.asset, impatient=self.player.impatient, multiplier=self.session.config['asset_return'], default_price=self.session.config['default_price'], allocation_prize=self.session.config['allocation_prize'], ) class buyOrSellAccountant(Page): form_model = 'player' form_fields = ['decision_guess', 'bid_guess', 'liquid', 'solvent'] def is_displayed(self): return self.player.get_role() in ['Region I Accountant', 'Region II Accountant'] def error_message(self, values): if values['bid_guess'] < 0: return "The offer cannot be less than 0." elif values['decision_guess'] == 'Buy' and values['bid_guess'] > self.player.cash: return "The bank cannot bid more than the " + str(self.player.cash) + " they have in cash." elif values['decision_guess'] == 'Sell' and values['bid_guess'] > self.player.asset: return "The bank cannot offer to sell more than the " + str(self.player.asset) + " they have in assets." def vars_for_template(self): return dict( round_number=self.player.round_number, deposit=self.session.config['deposit'], cash=self.session.config['deposit'] - self.player.asset, asset=self.player.asset, impatient=self.player.impatient, multiplier=self.session.config['asset_return'], default_price=self.session.config['default_price'], allocation_prize=self.session.config['allocation_prize'], ) def before_next_page(self): partner = self.player.get_partner() # You tell them whether they are solvent partner.solvent = self.player.solvent partner.liquid = self.player.liquid # They tell you what they bid in the market self.player.decision = partner.decision self.player.bid = partner.bid class DeterminePrice(WaitPage): def after_all_players_arrive(self): total_bids = 0 total_offers = 0 for player in self.group.get_players(): if player.get_role() in ["Region I", "Region II"]: if player.decision == 'Buy': total_bids += player.bid else: total_offers += player.bid self.group.total_offer = total_offers self.group.total_bid = total_bids if total_bids > 0 and total_offers > 0: self.group.price = round(total_bids/total_offers, 2) else: self.group.price = None price = self.group.price for player in self.group.get_players(): if player.get_role() in ["Region I", "Region II"]: if player.decision == 'Buy': your_bid = player.bid if price is not None: your_new_assets = int(your_bid / price) your_new_cash = -int(your_bid / price) * price else: your_new_assets = 0 your_new_cash = 0 else: your_offer = player.bid if price is not None: your_new_assets = -your_offer your_new_cash = your_offer * price else: your_new_assets = 0 your_new_cash = 0 player.asset += your_new_assets player.cash += your_new_cash class AssetMarket(Page): def is_displayed(self): return self.player.get_role() in ["Region I", "Region II"] def vars_for_template(self): price = self.group.price if self.player.decision == 'Buy': your_offer = 0 your_bid = self.player.bid if price is not None: your_new_assets = int(your_bid / price) your_new_cash = -int(your_bid / price) * price else: your_new_assets = 0 your_new_cash = 0 else: your_offer = self.player.bid your_bid = 0 if price is not None: your_new_assets = -your_offer your_new_cash = your_offer * price else: your_new_assets = 0 your_new_cash = 0 your_old_assets = self.player.asset - your_new_assets your_old_cash = self.player.cash - your_new_cash if price is not None: prediction_error = abs(self.player.price_guess - price) else: prediction_error = 0 if not self.session.config['has_accountant']: self.player.liquid = self.player.cash >= self.player.impatient self.player.solvent = self.player.cash + self.session.config['asset_return'] * self.player.asset >= self.session.config['deposit'] round_payoff = self.session.config['allocation_prize'] * (self.player.cash + self.session.config['asset_return'] * self.player.asset - self.player.impatient)/(100-self.player.impatient) if not self.player.liquid or not self.player.solvent: round_payoff = 0 round_payoff = c(round_payoff).to_real_world_currency(self.session) self.player.allocation_payoff = c(round_payoff).to_real_world_currency(self.session) if price is not None: self.player.prediction_payoff = c(round(self.session.config['forecast_prize']/(1+prediction_error),2)).to_real_world_currency(self.session) else: self.player.prediction_payoff = c(0).to_real_world_currency(self.session) return dict( your_offer=your_offer, your_bid=your_bid, trade_happens=(price is not None), price=price, total_bid=self.group.total_bid, total_offer=self.group.total_offer, your_new_assets=your_new_assets, your_new_cash=round(your_new_cash, 2), your_old_assets=your_old_assets, your_old_cash=round(your_old_cash, 2), your_assets=self.player.asset, your_cash=round(self.player.cash, 2), prediction_error=round(prediction_error, 2), prediction_prize=self.player.prediction_payoff, solvent=self.player.solvent, liquid=self.player.liquid, round_payoff=round_payoff, ) def before_next_page(self): partner = self.player.get_partner() partner.asset = self.player.asset partner.cash = self.player.cash partner.decision = self.player.decision partner.bid = self.player.bid class WaitForResults(WaitPage): def vars_for_template(self): return dict( ) class Results(Page): def is_displayed(self): return self.player.get_role() in ['Region I', 'Region II'] def vars_for_template(self): return dict( has_accountant=self.session.config['has_accountant'], assets=self.player.asset, cash=self.player.cash, round=self.round_number, impatient=self.player.impatient, player_in_previous_rounds=self.player.in_previous_rounds(), prediction_payoff=c(self.player.prediction_payoff).to_real_world_currency(self.session), solvent=self.player.solvent, liquid=self.player.liquid, allocation_payoff=c(self.player.allocation_payoff).to_real_world_currency(self.session), ) class ResultsAccountant(Page): def is_displayed(self): return self.player.get_role() in ['Region I Accountant', 'Region II Accountant'] def vars_for_template(self): truly_liquid = self.player.cash >= self.player.impatient truly_solvent = self.player.cash + self.session.config['asset_return'] * self.player.asset >= \ self.session.config['deposit'] self.player.liquid_correct = (self.player.liquid == truly_liquid) self.player.solvent_correct = (self.player.solvent == truly_solvent) self.player.allocation_payoff = 0 if self.player.liquid_correct: self.player.allocation_payoff += self.session.config['accountant_prize'] if self.player.solvent_correct: self.player.allocation_payoff += self.session.config['accountant_prize'] self.player.allocation_payoff = c(self.player.allocation_payoff).to_real_world_currency(self.session) if self.player.decision == self.player.decision_guess: prediction_error = abs(self.player.bid - self.player.bid_guess) else: prediction_error = self.player.bid + self.player.bid_guess self.player.prediction_payoff = c(self.session.config['forecast_prize']/(1+prediction_error)).to_real_world_currency(self.session) return dict( round=self.round_number, bid_guess=self.player.bid_guess, bid=self.player.bid, decision_guess=self.player.decision_guess.lower(), decision=self.player.decision.lower(), prediction_error=prediction_error, prediction_payoff=self.player.prediction_payoff, assets=self.player.asset, cash=self.player.cash, impatient=self.player.impatient, liquid=self.player.liquid, solvent=self.player.solvent, truly_liquid=truly_liquid, truly_solvent=truly_solvent, liquid_correct=self.player.liquid_correct, solvent_correct=self.player.solvent_correct, accountant_prize=c(self.session.config['accountant_prize']).to_real_world_currency(self.session), payoff=c(self.player.allocation_payoff).to_real_world_currency(self.session), player_in_previous_rounds=self.player.in_previous_rounds(), ) class WaitforFinalResults(WaitPage): def is_displayed(self): return self.round_number == Constants.num_rounds class FinalResults(Page): def is_displayed(self): return self.round_number == Constants.num_rounds def vars_for_template(self): paying_rounds_allocation = self.session.vars['paying_rounds_allocation'] paying_rounds_prediction = self.session.vars['paying_rounds_prediction'] num_paying_rounds_allocation = len(paying_rounds_allocation) num_paying_rounds_prediction = len(paying_rounds_prediction) earnings_prediction = 0 for round in paying_rounds_prediction: earnings_prediction += self.player.in_round(round).prediction_payoff self.player.in_round(round).payoff = self.player.in_round(round).prediction_payoff earnings_allocation = 0 for round in paying_rounds_allocation: earnings_allocation += self.player.in_round(round).allocation_payoff self.player.in_round(round).payoff = self.player.in_round(round).allocation_payoff return dict( is_accountant=(self.player.get_role() in ['Region I Accountant', 'Region II Accountant']), num_paying_rounds_allocation=num_paying_rounds_allocation, num_paying_rounds_prediction=num_paying_rounds_prediction, paying_rounds_allocation=paying_rounds_allocation, paying_rounds_prediction=paying_rounds_prediction, player_in_all_rounds=self.player.in_all_rounds(), earnings_prediction=c(earnings_prediction).to_real_world_currency(self.session), earnings_allocation=c(earnings_allocation).to_real_world_currency(self.session), total_earnings=c(self.participant.payoff).to_real_world_currency(self.session), show_up_fee=self.session.config['participation_fee'], total_payoff=self.participant.payoff_plus_participation_fee(), ) page_sequence = [InstructionsBank, InstructionsAccountant, Period1Bank, BetweenPeriods, Period2Bank, Period2Accountant, buyOrSell, buyOrSellAccountant, DeterminePrice, AssetMarket, WaitForResults, Results, ResultsAccountant, WaitforFinalResults, FinalResults]