fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11.5, 4.8))
# Panel 1: Valuation Surface in Opportunity Space (U, D)
U_vals = np.linspace(1.005, 1.10, 50)
D_vals = np.linspace(0.95, 1.004, 50)
U_grid, D_grid = np.meshgrid(U_vals, D_vals)
Z = np.zeros(U_grid.shape)
for i in range(U_grid.shape[0]):
for j in range(U_grid.shape[1]):
u = U_grid[i, j]
d = D_grid[i, j]
alpha = np.clip((u - U_p) / (U_c - U_p), 0, 1)
r = (1 - alpha) * R_p + alpha * R_c
Z[i, j] = binomial_option_pricing(K, S_0, T, u, d, r, dt)
contour = ax1.contourf(U_grid, D_grid, Z, levels=20, cmap='viridis')
cbar = fig.colorbar(contour, ax=ax1)
cbar.set_label('Option Fair Value', fontsize=9)
val_p = binomial_option_pricing(K, S_0, T, U_p, D_p, R_p, dt)
val_c = binomial_option_pricing(K, S_0, T, U_c, D_c, R_c, dt)
ax1.scatter([U_p], [D_p], color='#2563eb', s=90, edgecolors='white', lw=1.5, zorder=5,
label=f'Proletariat ($f_p={val_p:.3f}$)')
ax1.scatter([U_c], [D_c], color='#dc2626', s=90, edgecolors='white', lw=1.5, zorder=5,
label=f'Capitalist ($f_c={val_c:.3f}$)')
ax1.set_xlabel('Up Factor ($U$)', fontsize=9.5)
ax1.set_ylabel('Down Factor ($D$)', fontsize=9.5)
ax1.set_title('Option Valuation Surface in Opportunity Space', fontsize=10.5, pad=10)
ax1.legend(loc='lower left', frameon=True, facecolor='white', edgecolor='#cccccc', fontsize=8.5)
ax1.grid(True, alpha=0.25, linestyle=':')
# Panel 2: Value Disparity vs. Qualification Strike Hurdle (K)
K_vals = np.linspace(0.8, 1.35, 50)
fp_K = [binomial_option_pricing(k, S_0, T, U_p, D_p, R_p, dt) for k in K_vals]
fc_K = [binomial_option_pricing(k, S_0, T, U_c, D_c, R_c, dt) for k in K_vals]
ratio_K = [c / p if p > 1e-4 else np.nan for c, p in zip(fc_K, fp_K)]
ax2_twin = ax2.twinx()
l1 = ax2.plot(K_vals, fc_K, color='#dc2626', lw=2.0, label='Capitalist ($f_c$)')
l2 = ax2.plot(K_vals, fp_K, color='#2563eb', lw=2.0, label='Proletariat ($f_p$)')
l3 = ax2_twin.plot(K_vals, ratio_K, color='#7c3aed', lw=1.8, linestyle='--', label='Disparity Ratio ($f_c / f_p$)')
ax2.axvline(1.0, color='#666666', linestyle=':', lw=1.2, label='Baseline Hurdle ($K=1.0$)')
ax2.scatter([1.0], [val_c], color='#dc2626', s=55, zorder=5)
ax2.scatter([1.0], [val_p], color='#2563eb', s=55, zorder=5)
ax2.set_xlabel('Qualification Strike Hurdle ($K$)', fontsize=9.5)
ax2.set_ylabel('Option Fair Value', fontsize=9.5)
ax2_twin.set_ylabel('Disparity Multiple ($f_c / f_p$)', color='#7c3aed', fontsize=9.5)
ax2_twin.tick_params(axis='y', labelcolor='#7c3aed')
ax2.set_title('Value Disparity vs. Qualification Hurdle ($K$)', fontsize=10.5, pad=10)
ax2.grid(True, alpha=0.25, linestyle=':')
lines = l1 + l2 + l3
labels = [l.get_label() for l in lines]
ax2.legend(lines, labels, loc='upper right', frameon=True, facecolor='white', edgecolor='#cccccc', fontsize=8.5)
plt.tight_layout()
plt.show()