diff --git a/docs/tutorials/price_controls.rst b/docs/tutorials/price_controls.rst index 1fa2ddc..a599b08 100644 --- a/docs/tutorials/price_controls.rst +++ b/docs/tutorials/price_controls.rst @@ -62,15 +62,10 @@ A binding price ceiling creates a shortage because quantity demanded exceeds qua # Apply a binding price ceiling at $8 ceiling_market = Market(demand, supply, ceiling=8) - # Calculate shortage - q_demanded = demand.q(8) - q_supplied = supply.q(8) - shortage = q_demanded - q_supplied - print(f"With Price Ceiling at $8:") - print(f" Quantity Supplied: {q_supplied:.0f}") - print(f" Quantity Demanded: {q_demanded:.0f}") - print(f" Shortage: {shortage:.0f} units") + print(f" Quantity Supplied: {ceiling_market.quantity_supplied:.0f}") + print(f" Quantity Demanded: {ceiling_market.quantity_demanded:.0f}") + print(f" Shortage: {ceiling_market.shortage:.0f} units") print(f" Deadweight Loss: ${ceiling_market.dwl:.2f}") # Visualize the market with ceiling @@ -99,15 +94,10 @@ A binding price floor creates a surplus because quantity supplied exceeds quanti # Apply a binding price floor at $12 floor_market = Market(demand, supply, floor=12) - # Calculate surplus - q_demanded = demand.q(12) - q_supplied = supply.q(12) - surplus = q_supplied - q_demanded - print(f"With Price Floor at $12:") - print(f" Quantity Demanded: {q_demanded:.0f}") - print(f" Quantity Supplied: {q_supplied:.0f}") - print(f" Surplus: {surplus:.0f} units") + print(f" Quantity Demanded: {floor_market.quantity_demanded:.0f}") + print(f" Quantity Supplied: {floor_market.quantity_supplied:.0f}") + print(f" Surplus: {floor_market.surplus:.0f} units") print(f" Deadweight Loss: ${floor_market.dwl:.2f}") # Visualize the market with floor @@ -150,4 +140,4 @@ Price controls only affect the market when they're binding: Non-binding ceiling at $15: P = $10.00, Q = 10 Non-binding floor at $7: P = $10.00, Q = 10 - Free market: P = $10.00, Q = 10 \ No newline at end of file + Free market: P = $10.00, Q = 10 diff --git a/freeride/equilibrium.py b/freeride/equilibrium.py index 5665a8f..8f40419 100644 --- a/freeride/equilibrium.py +++ b/freeride/equilibrium.py @@ -217,6 +217,30 @@ def _valid_in_domain(self, dpiece, spiece, q_val): def excess_demand(self, p): return self.demand.q(p) - self.supply.q(p) + @property + def quantity_demanded(self): + return self.demand.q(self.__p_consumer) + + @property + def quantity_supplied(self): + return self.supply.q(self.__p_producer) + + @property + def shortage(self): + return max(self.quantity_demanded - self.quantity_supplied, 0) + + @property + def surplus(self): + return max(self.quantity_supplied - self.quantity_demanded, 0) + + @property + def excess_demand_quantity(self): + return self.shortage + + @property + def excess_supply(self): + return self.surplus + # ================== Surplus ==================# @property def consumer_surplus(self): diff --git a/tests/test_equilibrium.py b/tests/test_equilibrium.py index 343d76d..09161ca 100644 --- a/tests/test_equilibrium.py +++ b/tests/test_equilibrium.py @@ -16,19 +16,55 @@ def test_equilibrium_price(self): def test_equilibrium_quantity(self): self.assertTrue(self.equilibrium.q == 8.0) + def test_free_market_has_no_market_gap(self): + self.assertAlmostEqual(self.equilibrium.quantity_demanded, 8.0) + self.assertAlmostEqual(self.equilibrium.quantity_supplied, 8.0) + self.assertAlmostEqual(self.equilibrium.shortage, 0.0) + self.assertAlmostEqual(self.equilibrium.surplus, 0.0) + + def test_binding_ceiling_gap_properties(self): + ceiling_price = 5 + eq_ceiling = Equilibrium( + self.demand_curve, + self.supply_curve, + ceiling=ceiling_price, + ) + self.assertEqual(eq_ceiling.p, ceiling_price) + self.assertAlmostEqual(eq_ceiling.q, 6.0) + self.assertAlmostEqual(eq_ceiling.quantity_demanded, 10.0) + self.assertAlmostEqual(eq_ceiling.quantity_supplied, 6.0) + self.assertAlmostEqual(eq_ceiling.shortage, 4.0) + self.assertAlmostEqual(eq_ceiling.surplus, 0.0) + self.assertAlmostEqual(eq_ceiling.excess_demand_quantity, 4.0) + def test_binding_floor(self): floor_price = 7 eq_floor = Equilibrium(self.demand_curve, self.supply_curve, floor=floor_price) self.assertEqual(eq_floor.p, floor_price) self.assertEqual(eq_floor.q, self.demand_curve.q(floor_price)) - if hasattr(eq_floor, "excess_supply"): - expected_excess = self.supply_curve.q(floor_price) - self.demand_curve.q(floor_price) - self.assertEqual(eq_floor.excess_supply, expected_excess) + self.assertAlmostEqual(eq_floor.quantity_demanded, 6.0) + self.assertAlmostEqual(eq_floor.quantity_supplied, 10.0) + self.assertAlmostEqual(eq_floor.shortage, 0.0) + self.assertAlmostEqual(eq_floor.surplus, 4.0) + self.assertAlmostEqual(eq_floor.excess_supply, 4.0) def test_nonbinding_floor(self): floor_price = 5 eq_floor = Equilibrium(self.demand_curve, self.supply_curve, floor=floor_price) self.assertEqual(eq_floor.p, self.equilibrium.p) self.assertEqual(eq_floor.q, self.equilibrium.q) - if hasattr(eq_floor, "excess_supply"): - self.assertEqual(eq_floor.excess_supply, 0) + self.assertAlmostEqual(eq_floor.shortage, 0.0) + self.assertAlmostEqual(eq_floor.surplus, 0.0) + self.assertAlmostEqual(eq_floor.excess_supply, 0.0) + + def test_nonbinding_ceiling_has_no_market_gap(self): + ceiling_price = 7 + eq_ceiling = Equilibrium( + self.demand_curve, + self.supply_curve, + ceiling=ceiling_price, + ) + self.assertEqual(eq_ceiling.p, self.equilibrium.p) + self.assertEqual(eq_ceiling.q, self.equilibrium.q) + self.assertAlmostEqual(eq_ceiling.shortage, 0.0) + self.assertAlmostEqual(eq_ceiling.surplus, 0.0)