File size: 5,676 Bytes
a2cbcac
c010f04
 
82de0b8
91ae34d
c010f04
8ed954c
 
 
 
a2cbcac
 
 
68150f2
8ed954c
68150f2
1a31ade
8ed954c
 
 
 
 
 
68150f2
 
a2cbcac
8ed954c
a2cbcac
 
1a31ade
8ed954c
 
1a31ade
8ed954c
68150f2
 
8ed954c
68150f2
8ed954c
68150f2
8ed954c
3c57e36
 
8ed954c
3c57e36
 
 
 
8ed954c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d05bb9b
68150f2
8ed954c
91ae34d
 
8ed954c
d05bb9b
881d24a
8ed954c
d05bb9b
8ed954c
1a31ade
d5ba3a3
8ed954c
 
 
 
 
 
1a31ade
8ed954c
 
1a31ade
a900115
8ed954c
d05bb9b
 
 
8ed954c
d05bb9b
1a31ade
 
8ed954c
91ae34d
d5ba3a3
8ed954c
 
 
 
 
 
 
 
 
91ae34d
881d24a
8ed954c
 
 
91ae34d
 
1a31ade
91ae34d
8ed954c
91ae34d
 
8ed954c
91ae34d
8ed954c
 
c010f04
8ed954c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import chainlit as cl
import gc
import matplotlib.pyplot as plt
from src.agent import app
from src.social_scout import fetch_tickers_from_social
from src.scanner import get_trending_stocks
from src.core.logger import get_logger

logger = get_logger(__name__)


@cl.on_chat_start
async def start():
    welcome_msg = """
    **PrimoGreedy v7.0: Screener + Brave Edition**
    
    **Commands:**
    1. `AUTO` -> Smart Scan (yFinance screener + Brave trending)
    2. `@Handle` -> Social Scout
    3. `PORTFOLIO` -> View Agent Track Record
    4. `BACKTEST` -> Run backtest on paper portfolio
    5. `NVDA` -> Single Ticker Scout
    6. Ask a question -> Chat with Agent
    """
    await cl.Message(content=welcome_msg).send()


@cl.on_message
async def main(message: cl.Message):
    user_input = message.content.strip()

    # 1. AUTO SCAN
    if user_input.upper() == "AUTO":
        await cl.Message(content="Scanning global markets with screener + Brave trending...").send()
        tickers = await cl.make_async(get_trending_stocks)()
        if not tickers:
            await cl.Message(content="No trending data found.").send()
            return
        await cl.Message(content=f"**Hot List:** {', '.join(tickers)}").send()

    # 2. PORTFOLIO
    elif user_input.upper() == "PORTFOLIO":
        from src.portfolio_tracker import evaluate_portfolio
        await cl.Message(content="Fetching live prices for the Agent's historical calls...").send()
        report = await cl.make_async(evaluate_portfolio)()
        await cl.Message(content=report).send()
        return

    # 3. BACKTEST (Proposal G)
    elif user_input.upper() == "BACKTEST":
        await cl.Message(content="Running backtest on paper portfolio...").send()
        try:
            from src.backtesting.portfolio_bridge import backtest_portfolio
            results = await cl.make_async(backtest_portfolio)()
            if not results:
                await cl.Message(content="No trades in portfolio to backtest.").send()
                return

            report = "## Portfolio Backtest Results\n\n"
            report += "| Symbol | PrimoAgent | Buy & Hold | Alpha |\n"
            report += "|--------|-----------|-----------|-------|\n"
            for symbol, r in results.items():
                primo = r["primo"]["Cumulative Return [%]"]
                bh = r["buyhold"]["Cumulative Return [%]"]
                diff = primo - bh
                emoji = "+" if diff > 0 else ""
                report += f"| **{symbol}** | {primo:.2f}% | {bh:.2f}% | {emoji}{diff:.2f}% |\n"

            total = len(results)
            wins = sum(1 for r in results.values()
                       if r["primo"]["Cumulative Return [%]"] > r["buyhold"]["Cumulative Return [%]"])
            report += f"\n**Win Rate:** {wins}/{total} ({wins / total * 100:.1f}%)\n"

            await cl.Message(content=report).send()
        except Exception as e:
            logger.error("Backtest error: %s", e, exc_info=True)
            await cl.Message(content=f"Backtest error: {e}").send()
        return

    # 4. SOCIAL SCOUT
    elif user_input.startswith("@"):
        handle = user_input.replace("@", "")
        await cl.Message(content=f"Scouting **@{handle}**...").send()
        tickers = await cl.make_async(fetch_tickers_from_social)(handle)
        if not tickers:
            await cl.Message(content="No tickers found.").send()
            return

    # 5. CHAT (has a space -> conversational query)
    elif " " in user_input:
        await cl.Message(content="Consulting Senior Broker...").send()
        try:
            config = {"configurable": {"thread_id": "ui_session"}, "recursion_limit": 30}
            result = await app.ainvoke(
                {"ticker": user_input, "retry_count": 0, "manual_search": False},
                config=config,
            )
            report = result.get("final_report", "No response generated.")
            await cl.Message(content=f"**Agent:**\n\n{report}").send()
        except Exception as e:
            logger.error("Chat error: %s", e)
            await cl.Message(content=f"Chat error: {e}").send()
        return

    # 6. SINGLE OR COMMA-SEPARATED TICKERS
    else:
        raw_list = user_input.upper().replace(",", " ").split()
        tickers = [t for t in raw_list if len(t) <= 5 and t.isalpha()]
        if not tickers and " " not in user_input:
            tickers = [user_input.upper()]

    for ticker in tickers:
        await cl.Message(content=f"--- **Processing:** {ticker} ---").send()
        try:
            config = {"configurable": {"thread_id": "ui_session"}, "recursion_limit": 30}
            result = await app.ainvoke(
                {"ticker": ticker, "retry_count": 0, "manual_search": True},
                config=config,
            )

            status = result.get("status")
            report = result.get("final_report", "No report generated.")
            chart_bytes = result.get("chart_data")

            elements = []
            if chart_bytes:
                elements.append(
                    cl.Image(content=chart_bytes, name=f"{ticker}_chart", display="inline")
                )

            if status == "FAIL":
                response = f"{report}\n\n*Chart provided for visual reference.*"
            else:
                response = f"**PASSED FIREWALL**\n\n{report}"

            await cl.Message(content=response, elements=elements).send()

        except Exception as e:
            logger.error("Error processing %s: %s", ticker, e)
            await cl.Message(content=f"Error on {ticker}: {e}").send()
        finally:
            plt.close("all")
            gc.collect()