Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,14 @@ We can easily add a tax to the market and visualize its effects.

# Apply a $2.50 tax
market.tax = 2.5
print(f"Consumer burden: ${market.consumer_tax_burden:.2f}")
print(f"Producer burden: ${market.producer_tax_burden:.2f}")
print(f"Consumer share: {market.consumer_tax_share:.0%}")
market.plot(surplus=True)

This plot shows the higher price paid by consumers, the lower price
received by producers, and the shaded tax revenue rectangle.
received by producers, and the shaded tax revenue rectangle. The burden
properties show how much of the tax is borne by each side of the market.

.. image:: _static/tax_example_plot.svg
:alt: Market plot with tax revenue shaded
Expand Down
32 changes: 32 additions & 0 deletions freeride/equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,38 @@ def govt_revenue(self):
def govt_expenditure(self):
return -self.govt_revenue

@property
def tax_wedge(self):
return self.__p_consumer - self.__p_producer

@property
def consumer_tax_burden(self):
if self.__tax == 0:
return 0.0
return self.__p_consumer - self._free_market_price()

@property
def producer_tax_burden(self):
if self.__tax == 0:
return 0.0
return self._free_market_price() - self.__p_producer

@property
def consumer_tax_share(self):
if self.__tax == 0:
return 0.0
return self.consumer_tax_burden / self.__tax

@property
def producer_tax_share(self):
if self.__tax == 0:
return 0.0
return self.producer_tax_burden / self.__tax

def _free_market_price(self):
p_star, _ = self._find_intersection(self.demand, self.supply)
return p_star

@property
def total_surplus(self):
return (
Expand Down
43 changes: 43 additions & 0 deletions tests/test_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,46 @@ def test_nonbinding_floor(self):
self.assertEqual(eq_floor.q, self.equilibrium.q)
if hasattr(eq_floor, "excess_supply"):
self.assertEqual(eq_floor.excess_supply, 0)

def test_symmetric_tax_incidence(self):
demand = Demand(10, -1)
supply = Supply(0, 1)
eq_tax = Equilibrium(demand, supply, tax=2)
self.assertAlmostEqual(eq_tax.tax_wedge, 2.0)
self.assertAlmostEqual(eq_tax.consumer_tax_burden, 1.0)
self.assertAlmostEqual(eq_tax.producer_tax_burden, 1.0)
self.assertAlmostEqual(eq_tax.consumer_tax_share, 0.5)
self.assertAlmostEqual(eq_tax.producer_tax_share, 0.5)

def test_inelastic_demand_places_more_tax_burden_on_consumers(self):
demand = Demand(10, -4)
supply = Supply(0, 1)
eq_tax = Equilibrium(demand, supply, tax=2)
self.assertAlmostEqual(eq_tax.consumer_tax_burden, 1.6)
self.assertAlmostEqual(eq_tax.producer_tax_burden, 0.4)
self.assertGreater(
eq_tax.consumer_tax_share,
eq_tax.producer_tax_share,
)
self.assertAlmostEqual(
eq_tax.consumer_tax_share + eq_tax.producer_tax_share,
1.0,
)

def test_no_tax_has_zero_tax_incidence(self):
self.assertAlmostEqual(self.equilibrium.tax_wedge, 0.0)
self.assertAlmostEqual(self.equilibrium.consumer_tax_burden, 0.0)
self.assertAlmostEqual(self.equilibrium.producer_tax_burden, 0.0)
self.assertAlmostEqual(self.equilibrium.consumer_tax_share, 0.0)
self.assertAlmostEqual(self.equilibrium.producer_tax_share, 0.0)

def test_subsidy_incidence_uses_signed_burdens(self):
demand = Demand(10, -1)
supply = Supply(0, 1)
eq_subsidy = Equilibrium(demand, supply)
eq_subsidy.subsidy = 2
self.assertAlmostEqual(eq_subsidy.tax_wedge, -2.0)
self.assertAlmostEqual(eq_subsidy.consumer_tax_burden, -1.0)
self.assertAlmostEqual(eq_subsidy.producer_tax_burden, -1.0)
self.assertAlmostEqual(eq_subsidy.consumer_tax_share, 0.5)
self.assertAlmostEqual(eq_subsidy.producer_tax_share, 0.5)
Loading