import gradio as gr CUSTOM_CSS = """ body, .gradio-container { background: linear-gradient(180deg, #08101e 0%, #0b1020 100%); color: white !important; font-family: Inter, Arial, sans-serif; } .hero { padding: 24px; border: 1px solid rgba(255,255,255,0.08); border-radius: 20px; background: rgba(255,255,255,0.03); margin-bottom: 16px; } .metric { padding: 16px; border-radius: 16px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); } """ def analyze_thesis(ticker, direction, horizon, position_size, thesis): thesis_lower = thesis.lower() thesis_score = 60 evidence_score = 55 risk_score = 45 if "valuation" in thesis_lower: thesis_score += 8 evidence_score += 8 if "risk" in thesis_lower: thesis_score += 6 if "earnings" in thesis_lower or "catalyst" in thesis_lower: evidence_score += 8 if "guaranteed" in thesis_lower or "100%" in thesis_lower: risk_score += 20 thesis_score -= 10 if position_size > 20: risk_score += 15 thesis_score = max(0, min(100, thesis_score)) evidence_score = max(0, min(100, evidence_score)) risk_score = max(0, min(100, risk_score)) capital_readiness = max(0, min(100, int(thesis_score * 0.5 + evidence_score * 0.3 + (100 - risk_score) * 0.2))) contradictions = [] if direction == "Bullish" and "overvalued" in thesis_lower: contradictions.append("Bullish view conflicts with 'overvalued' wording.") if direction == "Bearish" and "undervalued" in thesis_lower: contradictions.append("Bearish view conflicts with 'undervalued' wording.") if not contradictions: contradictions.append("No major contradiction detected.") missing = [] if "valuation" not in thesis_lower: missing.append("Valuation context missing.") if "risk" not in thesis_lower: missing.append("Risk definition missing.") if "stop loss" not in thesis_lower and "invalidation" not in thesis_lower: missing.append("Exit or invalidation plan missing.") if "macro" not in thesis_lower: missing.append("Macro sensitivity not discussed.") counter_case = [] if direction == "Bullish": counter_case = [ "Positive expectations may already be priced in.", "Weak guidance can break the thesis quickly.", "Position sizing may be too aggressive for current evidence." ] else: counter_case = [ "Negative sentiment may already be priced in.", "A strong earnings beat can invalidate the bearish view.", "Bear thesis may underestimate business resilience." ] memo = f""" # BetaTwins Memo **Ticker:** {ticker} **Direction:** {direction} **Horizon:** {horizon} **Position Size:** {position_size}% ## Thesis {thesis} ## Scores - Thesis Score: {thesis_score}/100 - Evidence Score: {evidence_score}/100 - Risk Score: {risk_score}/100 - Capital Readiness: {capital_readiness}/100 ## Contradictions - """ + "\n- ".join(contradictions) + """ ## Missing Factors - """ + "\n- ".join(missing) + """ ## Counter-Case - """ + "\n- ".join(counter_case) summary = f""" ### Executive Summary **{ticker}** thesis reviewed with a **{direction.lower()}** stance. - Thesis Score: **{thesis_score}** - Evidence Score: **{evidence_score}** - Risk Score: **{risk_score}** - Capital Readiness: **{capital_readiness}** """ return ( f"
Thesis Score
{thesis_score}
", f"
Evidence Score
{evidence_score}
", f"
Risk Score
{risk_score}
", f"
Capital Readiness
{capital_readiness}
", summary, "\n".join([f"- {x}" for x in contradictions]), "\n".join([f"- {x}" for x in missing]), "\n".join([f"- {x}" for x in counter_case]), memo ) with gr.Blocks(css=CUSTOM_CSS, title="BetaTwins AI") as demo: gr.HTML("""

Stress-test every investment thesis before capital is deployed.

BetaTwins helps traders, investors, and fintech teams detect weak reasoning, hidden assumptions, and missing risks before a decision becomes expensive.

""") with gr.Tabs(): with gr.Tab("Analyzer"): with gr.Row(): with gr.Column(scale=4): ticker = gr.Textbox(label="Ticker", placeholder="e.g. NVDA") direction = gr.Dropdown(["Bullish", "Bearish", "Neutral"], value="Bullish", label="Direction") horizon = gr.Dropdown(["Short-Term", "Swing", "Long-Term"], value="Swing", label="Time Horizon") position_size = gr.Slider(1, 100, value=10, step=1, label="Position Size %") thesis = gr.Textbox(label="Investment Thesis", lines=10, placeholder="Write your thesis here...") run_btn = gr.Button("Run Analysis") with gr.Column(scale=6): with gr.Row(): score1 = gr.HTML() score2 = gr.HTML() score3 = gr.HTML() score4 = gr.HTML() with gr.Tabs(): with gr.Tab("Executive Summary"): summary = gr.Markdown() with gr.Tab("Contradictions"): contradictions = gr.Markdown() with gr.Tab("Missing Factors"): missing = gr.Markdown() with gr.Tab("Counter-Case"): counter = gr.Markdown() with gr.Tab("Memo"): memo = gr.Markdown() run_btn.click( fn=analyze_thesis, inputs=[ticker, direction, horizon, position_size, thesis], outputs=[score1, score2, score3, score4, summary, contradictions, missing, counter, memo] ) with gr.Tab("About"): gr.Markdown(""" ## BetaTwins AI AI-powered thesis stress testing for smarter investment decisions. ### What it does - Scores thesis quality - Detects contradictions - Finds missing factors - Generates counter-case - Builds memo-style output ### Disclaimer This tool is for research and decision-support only. It is not financial advice. """) if __name__ == "__main__": demo.launch()