eagle0504's picture
Upload folder using huggingface_hub
0fb1235 verified
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
def calculate_metrics(a, b, c, d, price_control=None):
"""
Calculates equilibrium, consumer surplus, producer surplus, and deadweight loss.
Args:
a (float): Y-intercept of the supply curve (P = a + bQ).
b (float): Slope of the supply curve.
c (float): Y-intercept of the demand curve (P = c - dQ).
d (float): Slope of the demand curve.
price_control (float, optional): An enforced price (floor or ceiling). Defaults to None.
Returns:
tuple: (Q_eq, P_eq, CS, PS, TS, DWL, Q_traded, price_control)
Returns (0, 0, 0, 0, 0, 0, 0, price_control) if parameters are invalid.
"""
# Ensure positive slopes for realistic curves
b = max(0.01, b) # Supply slope
d = max(0.01, d) # Demand slope
# Check for valid parameters that lead to an intersection with positive quantity
if (c - a) <= 0: # Demand intercept must be above supply intercept for Q_eq > 0
return 0, 0, 0, 0, 0, 0, 0, price_control
# 1. Equilibrium Calculation
Q_eq = (c - a) / (d + b)
P_eq = a + b * Q_eq
# Ensure valid equilibrium (positive quantity and price)
if Q_eq < 0.01 or P_eq < 0.01: # Check for near zero or negative values
return 0, 0, 0, 0, 0, 0, 0, price_control
# 2. Consumer Surplus (CS)
# Area of the triangle above equilibrium price and below demand curve
CS = 0.5 * Q_eq * (c - P_eq)
CS = max(0, CS) # Ensure non-negative
# 3. Producer Surplus (PS)
# Area of the triangle below equilibrium price and above supply curve
PS = 0.5 * Q_eq * (P_eq - a)
PS = max(0, PS) # Ensure non-negative
# 4. Total Surplus
TS = CS + PS
# 5. Deadweight Loss (DWL) and Quantity Traded under Price Control
DWL = 0
Q_traded = Q_eq # Initialize quantity traded to equilibrium quantity
if price_control is not None and price_control > 0: # If a price control is set
# Calculate quantity demanded and supplied at the controlled price
Q_demanded_at_pc = (c - price_control) / d
Q_supplied_at_pc = (price_control - a) / b
# The actual quantity traded is the minimum of Qd and Qs at the controlled price,
# ensuring it's non-negative (cannot trade negative quantity).
Q_traded = min(max(0, Q_demanded_at_pc), max(0, Q_supplied_at_pc))
# Calculate DWL if trade is restricted from equilibrium
if Q_traded < Q_eq:
# Price on the demand curve at the restricted quantity traded
P_demand_at_Q_traded = c - d * Q_traded
# Price on the supply curve at the restricted quantity traded
P_supply_at_Q_traded = a + b * Q_traded
# DWL is the area of the triangle between demand and supply curves
# from Q_traded to Q_eq. Height is the vertical distance between D & S at Q_traded.
DWL = 0.5 * (Q_eq - Q_traded) * (P_demand_at_Q_traded - P_supply_at_Q_traded)
DWL = max(0, DWL) # Ensure DWL is non-negative
return Q_eq, P_eq, CS, PS, TS, DWL, Q_traded, price_control
st.set_page_config(layout="wide", page_title="Supply & Demand Model")
st.title("πŸ“Š Econ 101: Supply and Demand Model")
st.sidebar.header("βš™οΈ Adjust Model Parameters")
# Sliders for demand curve: P = c - dQ
st.sidebar.subheader("Demand Curve: P = `c` - `d`Q")
c = st.sidebar.slider("c (Demand Intercept)", 10.0, 200.0, 100.0, 1.0, help="Maximum price consumers are willing to pay for 0 quantity.")
d = st.sidebar.slider("d (Demand Slope)", 0.1, 10.0, 2.0, 0.1, help="How much price decreases for each unit demanded.")
# Sliders for supply curve: P = a + bQ
st.sidebar.subheader("Supply Curve: P = `a` + `b`Q")
a = st.sidebar.slider("a (Supply Intercept)", 0.0, 100.0, 10.0, 1.0, help="Minimum price producers are willing to accept for 0 quantity.")
b = st.sidebar.slider("b (Supply Slope)", 0.1, 10.0, 1.0, 0.1, help="How much price increases for each unit supplied.")
# Price control slider
st.sidebar.subheader("Enforced Price (e.g., Price Floor/Ceiling)")
price_control = st.sidebar.slider("Set Enforced Price (0 for none)", 0.0, 200.0, 0.0, 0.5,
help="Set a price floor or ceiling. If 0, no price control is active.")
# Perform calculations
Q_eq, P_eq, CS, PS, TS, DWL, Q_traded, pc_used = calculate_metrics(a, b, c, d, price_control)
# Display results and plot
col1, col2 = st.columns([1, 2])
with col1:
st.header("πŸ“ˆ Economic Metrics")
if Q_eq == 0 and P_eq == 0 and CS == 0: # Indicates invalid parameters from calculate_metrics
st.error("Invalid parameters: Please adjust 'c' to be greater than 'a' to ensure a valid equilibrium.")
else:
st.metric("Equilibrium Quantity (Qe)", f"{Q_eq:.2f}")
st.metric("Equilibrium Price (Pe)", f"${P_eq:.2f}")
st.metric("Consumer Surplus (CS)", f"${CS:.2f}")
st.metric("Producer Surplus (PS)", f"${PS:.2f}")
st.metric("Total Surplus (TS)", f"${TS:.2f}")
if price_control > 0: # Only show these metrics if a price control is active
st.subheader("Metrics with Price Control")
st.metric("Enforced Price", f"${pc_used:.2f}")
st.metric("Quantity Traded (Q_traded)", f"{Q_traded:.2f}")
st.metric("Deadweight Loss (DWL)", f"${DWL:.2f}")
if DWL > 0:
st.warning("Deadweight Loss indicates market inefficiency due to the enforced price.")
else:
st.info("No Deadweight Loss with current enforced price (or price is ineffective).")
else:
st.info("Set 'Enforced Price' to calculate Deadweight Loss.")
with col2:
st.header("πŸ“‰ Supply and Demand Graph")
fig, ax = plt.subplots(figsize=(12, 8))
# Determine appropriate x and y axis limits for plotting
max_Q_plot = max(Q_eq * 1.5, (c / d) * 1.1, 20) # Extends beyond equilibrium and demand intercept
max_P_plot = max(P_eq * 1.5, c * 1.1, 150) # Extends beyond equilibrium and demand intercept
Q_values = np.linspace(0, max_Q_plot, 400)
# Demand Curve: P = c - dQ
P_demand = c - d * Q_values
# Supply Curve: P = a + bQ
P_supply = a + b * Q_values
# Filter out negative prices/quantities for plotting realism
P_demand[P_demand < 0] = np.nan # Don't plot negative prices
P_supply[P_supply < 0] = np.nan # Don't plot negative prices
Q_values[Q_values < 0] = np.nan # Don't plot negative quantities
ax.plot(Q_values, P_demand, label=f'Demand: P = {c:.1f} - {d:.1f}Q', color='blue', linewidth=2)
ax.plot(Q_values, P_supply, label=f'Supply: P = {a:.1f} + {b:.1f}Q', color='red', linewidth=2)
# Plot Equilibrium Point and lines
if Q_eq > 0 and P_eq > 0:
ax.plot(Q_eq, P_eq, 'go', markersize=8, label=f'Equilibrium (Q={Q_eq:.2f}, P=${P_eq:.2f})')
ax.vlines(Q_eq, 0, P_eq, linestyle=':', color='gray', linewidth=1)
ax.hlines(P_eq, 0, Q_eq, linestyle=':', color='gray', linewidth=1)
# Consumer Surplus Area
Q_cs_plot = np.linspace(0, Q_eq, 100)
P_cs_plot = c - d * Q_cs_plot
ax.fill_between(Q_cs_plot, P_eq, P_cs_plot, where=P_cs_plot > P_eq, color='skyblue', alpha=0.3, label='Consumer Surplus')
# Producer Surplus Area
Q_ps_plot = np.linspace(0, Q_eq, 100)
P_ps_plot = a + b * Q_ps_plot
ax.fill_between(Q_ps_plot, P_ps_plot, P_eq, where=P_eq > P_ps_plot, color='lightcoral', alpha=0.3, label='Producer Surplus')
# Plot Price Control and Deadweight Loss
if price_control > 0 and Q_traded < Q_eq:
ax.hlines(pc_used, 0, Q_traded, linestyle='--', color='purple', label=f'Enforced Price (${pc_used:.2f})', linewidth=1.5)
ax.vlines(Q_traded, 0, pc_used, linestyle='--', color='purple', linewidth=1.5)
# Identify the points for the DWL triangle
# Price on demand curve at Q_traded
P_demand_at_Q_traded_plot = c - d * Q_traded
# Price on supply curve at Q_traded
P_supply_at_Q_traded_plot = a + b * Q_traded
# Shade DWL triangle
# The points for the DWL triangle are (Q_traded, P_supply_at_Q_traded), (Q_traded, P_demand_at_Q_traded), and (Q_eq, P_eq)
# Plotting the triangle as a fill_between area between Q_traded and Q_eq
Q_dwl_fill = np.linspace(Q_traded, Q_eq, 100)
P_demand_dwl_fill = c - d * Q_dwl_fill
P_supply_dwl_fill = a + b * Q_dwl_fill
ax.fill_between(Q_dwl_fill, P_supply_dwl_fill, P_demand_dwl_fill, color='red', alpha=0.5, label='Deadweight Loss')
# Mark points relevant to price control
# ax.plot(Q_traded, c - d * Q_traded, 'kx', markersize=8, label=f'Demand at PC ({Q_traded:.2f})')
# ax.plot(Q_traded, a + b * Q_traded, 'bx', markersize=8, label=f'Supply at PC ({Q_traded:.2f})')
ax.set_xlabel("Quantity (Q)", fontsize=12)
ax.set_ylabel("Price (P)", fontsize=12)
ax.set_title("Supply and Demand Equilibrium", fontsize=14)
ax.set_ylim(bottom=0, top=max_P_plot)
ax.set_xlim(left=0, right=max_Q_plot)
ax.legend(loc='upper right')
ax.grid(True, linestyle='--', alpha=0.7)
st.pyplot(fig)