mathiassandnes Claude Opus 4.5 commited on
Commit
4e38411
·
1 Parent(s): 5036ebc

Add pricing sensitivity analysis section

Browse files

New Section 6 visualizes:
- Profit per product at different monthly prices (line chart)
- Customer cost vs retail price comparison (bar chart)
- Current price highlighted with star marker
- Break-even price calculation
- Customer savings/premium indicator

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +129 -0
app.py CHANGED
@@ -798,6 +798,135 @@ st.markdown(f"""
798
  - Break-even happens after just **1 cycle** (circular starts equal, then pulls ahead)
799
  """)
800
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801
  # ============================================================
802
  # FOOTER
803
  # ============================================================
 
798
  - Break-even happens after just **1 cycle** (circular starts equal, then pulls ahead)
799
  """)
800
 
801
+ # ============================================================
802
+ # SECTION 6: PRICING SENSITIVITY
803
+ # ============================================================
804
+ st.markdown("---")
805
+ st.header("Pricing Analysis: Finding the Sweet Spot")
806
+
807
+ st.markdown("How does monthly lease price affect profitability and customer value?")
808
+
809
+ # Calculate metrics at different price points
810
+ price_range = list(range(15, 51, 5)) # €15 to €50
811
+ pricing_data = []
812
+
813
+ for test_price in price_range:
814
+ test_revenue = test_price * lease_term
815
+ test_total_revenue = test_revenue * max_cycles # Over full product lifecycle
816
+
817
+ # Per-product profit (circular model)
818
+ circ_cost = new_unit_cost + (max_cycles - 1) * refurb_unit_cost
819
+ circ_profit = test_total_revenue - circ_cost
820
+ profit_margin = (circ_profit / test_total_revenue * 100) if test_total_revenue > 0 else 0
821
+
822
+ # Customer perspective: lease vs buy
823
+ customer_total_cost = test_revenue # What customer pays for one lease term
824
+ vs_retail = retail_price - customer_total_cost if retail_price > 0 else 0
825
+
826
+ pricing_data.append({
827
+ "Monthly Price": test_price,
828
+ "Revenue per Cycle": test_revenue,
829
+ "Total Revenue (all cycles)": test_total_revenue,
830
+ "Profit per Product": circ_profit,
831
+ "Profit Margin %": profit_margin,
832
+ "Customer Saves vs Buy": vs_retail,
833
+ })
834
+
835
+ pricing_df = pd.DataFrame(pricing_data)
836
+
837
+ price_col1, price_col2 = st.columns(2)
838
+
839
+ with price_col1:
840
+ st.subheader("Profit vs Monthly Price")
841
+
842
+ fig_price_profit = go.Figure()
843
+
844
+ # Profit line
845
+ fig_price_profit.add_trace(go.Scatter(
846
+ x=pricing_df["Monthly Price"],
847
+ y=pricing_df["Profit per Product"],
848
+ mode="lines+markers",
849
+ name="Profit per Product",
850
+ line=dict(color="#2ecc71", width=3),
851
+ marker=dict(size=10),
852
+ hovertemplate="€%{x}/mo → €%{y:,.0f} profit<extra></extra>"
853
+ ))
854
+
855
+ # Mark current price
856
+ current_profit = new_unit_cost + (max_cycles - 1) * refurb_unit_cost
857
+ current_profit = (lease_price * lease_term * max_cycles) - current_profit
858
+
859
+ fig_price_profit.add_trace(go.Scatter(
860
+ x=[lease_price],
861
+ y=[current_profit],
862
+ mode="markers",
863
+ name=f"Current (€{lease_price}/mo)",
864
+ marker=dict(size=16, color="#e74c3c", symbol="star"),
865
+ ))
866
+
867
+ # Break-even line
868
+ break_even_price = circ_cost / (lease_term * max_cycles)
869
+ fig_price_profit.add_hline(
870
+ y=0, line_dash="dash", line_color="gray",
871
+ annotation_text="Break-even"
872
+ )
873
+
874
+ fig_price_profit.update_layout(
875
+ xaxis_title="Monthly Lease Price (EUR)",
876
+ yaxis_title="Profit per Product Lifecycle (EUR)",
877
+ hovermode="x unified",
878
+ )
879
+
880
+ st.plotly_chart(fig_price_profit, width="stretch")
881
+
882
+ st.markdown(f"**Break-even price:** €{break_even_price:.0f}/month (over {max_cycles} cycles)")
883
+
884
+ with price_col2:
885
+ st.subheader("Customer Value vs Retail")
886
+
887
+ fig_customer = go.Figure()
888
+
889
+ # What customer pays (bar)
890
+ fig_customer.add_trace(go.Bar(
891
+ x=pricing_df["Monthly Price"],
892
+ y=pricing_df["Revenue per Cycle"],
893
+ name="Lease Cost (1 term)",
894
+ marker_color="#3498db",
895
+ hovertemplate="€%{x}/mo × {lease_term}mo = €%{y}<extra></extra>"
896
+ ))
897
+
898
+ # Retail price reference line
899
+ if retail_price > 0:
900
+ fig_customer.add_hline(
901
+ y=retail_price, line_dash="dash", line_color="#e74c3c", line_width=2,
902
+ annotation_text=f"Retail: €{retail_price}",
903
+ annotation_position="right"
904
+ )
905
+
906
+ # Mark current price
907
+ fig_customer.add_trace(go.Scatter(
908
+ x=[lease_price],
909
+ y=[lease_price * lease_term],
910
+ mode="markers",
911
+ name=f"Current (€{lease_price}/mo)",
912
+ marker=dict(size=14, color="#e74c3c", symbol="star"),
913
+ ))
914
+
915
+ fig_customer.update_layout(
916
+ xaxis_title="Monthly Lease Price (EUR)",
917
+ yaxis_title="Total Customer Cost (EUR)",
918
+ hovermode="x unified",
919
+ )
920
+
921
+ st.plotly_chart(fig_customer, width="stretch")
922
+
923
+ if retail_price > 0:
924
+ current_total = lease_price * lease_term
925
+ if current_total < retail_price:
926
+ st.success(f"At €{lease_price}/mo, customers **save €{retail_price - current_total}** vs buying ({(retail_price - current_total)/retail_price*100:.0f}% less)")
927
+ else:
928
+ st.warning(f"At €{lease_price}/mo, customers pay **€{current_total - retail_price} more** than retail (premium for flexibility)")
929
+
930
  # ============================================================
931
  # FOOTER
932
  # ============================================================