| import requests |
| from bs4 import BeautifulSoup |
| import pandas as pd |
| from io import StringIO |
| from datetime import datetime, timedelta |
| import gradio as gr |
| from langchain.tools import tool |
| from langchain.agents import initialize_agent, Tool, AgentExecutor |
| from langchain_openai import ChatOpenAI |
| from langchain.callbacks.base import BaseCallbackHandler |
|
|
| |
| OPENROUTER_API_KEY = "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" |
| HEADERS = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| 'Referer': 'https://www.google.com/', |
| 'Connection': 'keep-alive', |
| 'Cache-Control': 'max-age=0', |
| 'Upgrade-Insecure-Requests': '1' |
| } |
|
|
| |
| @tool("GetCurrentPrice") |
| def get_current_price(symbol: str) -> str: |
| """here is the doc string""" |
| url = f'https://www.marketwatch.com/investing/stock/{symbol}' |
| response = requests.get(url, headers=HEADERS) |
| if response.status_code == 200: |
| soup = BeautifulSoup(response.text, 'html.parser') |
| price_tag = soup.find('bg-quote', class_='value') |
| if price_tag: |
| price = price_tag.get_text(strip=True) |
| return f"{symbol.upper()} current price: ${price}" |
| else: |
| return "Stock price not found." |
| else: |
| return f"Failed to retrieve stock page. Status code: {response.status_code}" |
|
|
| @tool("GetHistoricalData") |
| def get_historical_data(symbol: str, days: int = 5000) -> str: |
| """here is it""" |
| end_date = datetime.now() |
| start_date = end_date - timedelta(days=days) |
| start_date_str = start_date.strftime('%m/%d/%Y 00:00:00') |
| end_date_str = end_date.strftime('%m/%d/%Y 23:59:59') |
| csv_url = ( |
| f'https://www.marketwatch.com/investing/stock/{symbol}/downloaddatapartial' |
| f'?csvdownload=true&downloadpartial=false' |
| f'&startdate={start_date_str}&enddate={end_date_str}' |
| f'&frequency=p1d&newdates=false' |
| ) |
| response = requests.get(csv_url, headers=HEADERS) |
| if response.status_code == 200: |
| try: |
| df = pd.read_csv(StringIO(response.text)) |
| return df.head(10).to_csv(index=False) |
| except Exception as e: |
| return f"Failed to parse CSV data: {e}" |
| else: |
| return f"Failed to download historical data. Status code: {response.status_code}" |
|
|
| @tool("GetTechnicalAnalysisDocs") |
| def get_technical_analysis_docs(_: str = "") -> str: |
| """Get documentation for technical analysis indicators.""" |
| return """ |
| Technical Analysis: Core Concepts & Formulas |
| ------------------------------------------- |
| - Market Action Discounts Everything: All known information is reflected in price. |
| - Prices Move in Trends: Uptrend, downtrend, or sideways movement. |
| - History Repeats Itself: Psychological patterns repeat. |
| Key Technical Indicators and Their Formulas: |
| ------------------------------------------- |
| 1. Simple Moving Average (SMA): SMA(time_period) = Sum(Price_t ... Price_{t-n}) / n |
| 2. Exponential Moving Average (EMA): EMA_t = (Price_t * α) + EMA_{t-1} * (1 - α), where α = 2/(n+1) |
| 3. Relative Strength Index (RSI): RSI = 100 - [100 / (1 + Avg Gain / Avg Loss)] |
| 4. MACD (Moving Average Convergence Divergence): MACD Line = 12-period EMA - 26-period EMA; Signal Line = 9-period EMA of MACD Line; Histogram = MACD Line - Signal Line |
| 5. Stochastic Oscillator (STOCH): Fast K = (Current Close - Lowest Low) / (Highest High - Lowest Low) * 100 over N periods; Slow K = 3-day SMA of Fast K; Slow D = 3-day SMA of Slow K |
| 6. Momentum (MOM): MOM = Current Close - Close_N_days_ago |
| 7. Rate of Change (ROC): ROC = [(Current Close - Prior Close) / Prior Close] * 100 |
| 8. Volume Weighted Average Price (VWAP): VWAP = Sum(Price * Volume) / Sum(Volume) over intraday period |
| 9. Bollinger Bands: Middle Band = 20-day SMA; Upper Band = 20-day SMA + 2 * 20-day Standard Deviation; Lower Band = 20-day SMA - 2 * 20-day Standard Deviation |
| 10. Ichimoku Cloud: Tenkan-sen = (9-period high + low)/2; Kijun-sen = (26-period high + low)/2; Senkou Span A = (Tenkan-sen + Kijun-sen)/2 shifted forward by 26; Senkou Span B = (52-period high + low)/2 shifted forward by 26; Chikou Span = Current close shifted back by 26 days |
| 11. **Williams %R**: |
| %R = (Highest High - Close) / (Highest High - Lowest Low) * -100 over N periods |
| 12. **Commodity Channel Index (CCI)**: |
| CCI = (Typical Price - 20-day SMA of TP) / (0.015 * Mean Deviation) |
| where Typical Price = (High + Low + Close) / 3 |
| 13. **Average Directional Index (ADX)**: |
| ADX = Smoothed average of DX values, which measure directional strength |
| 14. **On-Balance Volume (OBV)**: |
| OBV = previous OBV + volume if close > previous close, else -volume |
| 15. **Moving Average Convergence Divergence (MACD)**: |
| MACD Line = 12-day EMA - 26-day EMA |
| Signal Line = 9-day EMA of MACD Line |
| MACD Histogram = MACD Line - Signal Line |
| 16. **Absolute Price Oscillator (APO)**: |
| APO = Fast EMA - Slow EMA |
| 17. **Balance of Power (BOP)**: |
| BOP = (Close - Open) / (High - Low) |
| 18. **Triple Exponential Moving Average (TEMA)**: |
| TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 |
| where EMA1 = fast EMA, EMA2 = slower EMA, etc. |
| 19. **Double Exponential Moving Average (DEMA)**: |
| DEMA = 2*EMA1 - EMA2 |
| 20. **Kaufman Adaptive Moving Average (KAMA)**: |
| KAMA = prior KAMA + SC * (price - prior KAMA) |
| where SC = smoothing constant based on efficiency ratio |
| 21. **Chaikin Money Flow (CMF)**: |
| MF Multiplier = [(Close - Low) - (High - Close)] / (High - Low) |
| MF Volume = MF Multiplier * Volume |
| CMF = Sum(MF Volume) / Sum(Volume) over N days |
| 22. **Aroon Indicator**: |
| Aroon Up = ((N - Periods Since Highest Close) / N) * 100 |
| Aroon Down = ((N - Periods Since Lowest Close) / N) * 100 |
| 23. **Parabolic SAR**: |
| SAR_t = SAR_{t-1} + AF * (EP - SAR_{t-1}) |
| 24. **Standard Deviation (Volatility)**: |
| σ = sqrt[1/N * Σ(Close_i - μ)^2] |
| 25. **Candlestick Patterns**: |
| - Hammer, Shooting Star, Engulfing, Doji, Morning/Evening Star, etc. |
| """ |
|
|
| |
| tools = [ |
| Tool.from_function(get_current_price, name="GetCurrentPrice", description="Get current stock price."), |
| Tool.from_function(get_historical_data, name="GetHistoricalData", description="Get stock historical data as CSV."), |
| Tool.from_function(get_technical_analysis_docs, name="GetTechnicalAnalysisDocs", description="Technical indicator docs.") |
| ] |
|
|
| |
| class ReasoningCallbackHandler(BaseCallbackHandler): |
| def __init__(self, chat_callback): |
| self.chat_callback = chat_callback |
|
|
| def on_tool_start(self, serialized, input_str, **kwargs): |
| self.chat_callback(f"🛠️ Using Tool: {serialized['name']} with input: {input_str}") |
|
|
| def on_tool_end(self, output, **kwargs): |
| self.chat_callback(f"✅ Tool output: {output}") |
|
|
| def on_llm_new_token(self, token, **kwargs): |
| if token.strip(): |
| self.chat_callback(token) |
|
|
| |
| llm = ChatOpenAI( |
| openai_api_key=OPENROUTER_API_KEY, |
| openai_api_base="https://openrouter.ai/api/v1", |
| model_name="google/gemma-3n-e4b-it:free" |
| ) |
|
|
| |
| def run_agent(question, chat_callback): |
| callback_handler = ReasoningCallbackHandler(chat_callback) |
| agent_executor = initialize_agent( |
| tools, llm, |
| agent="zero-shot-react-description", |
| verbose=True, |
| callbacks=[callback_handler] |
| ) |
| try: |
| result = agent_executor.run(question+' give an approximation') |
| except Exception as e: |
| result = f"❌ Error: {str(e)}" |
|
|
| chat_callback(f"✅ Final Answer: {result}") |
|
|
| |
| with gr.Blocks(theme=gr.themes.Monochrome()) as demo: |
| gr.Markdown("## 📈 Stock Market Analysis Agent") |
| chatbot = gr.Chatbot(label="Stock Agent Chat") |
| with gr.Row(): |
| user_input = gr.Textbox(label="Type your question here...", scale=4) |
| submit_btn = gr.Button("Submit", scale=1) |
|
|
| def respond(msg, chat_history): |
| chat_history.append((msg, "")) |
| def chat_callback(new_msg): |
| chat_history.append(("", new_msg)) |
| chatbot.value = chat_history |
| run_agent(msg, chat_callback) |
| return chat_history |
|
|
|
|
|
|
| submit_btn.click(respond, [user_input, chatbot], [chatbot]) |
|
|
| demo.launch() |
|
|