|
import os |
|
import json |
|
import time |
|
import random |
|
from collections import defaultdict |
|
from datetime import date, datetime, timedelta |
|
import gradio as gr |
|
import pandas as pd |
|
import finnhub |
|
from huggingface_hub import hf_hub_download |
|
from llama_cpp import Llama |
|
from io import StringIO |
|
import requests |
|
from requests.adapters import HTTPAdapter |
|
from urllib3.util.retry import Retry |
|
|
|
|
|
os.environ['GRPC_VERBOSITY'] = 'ERROR' |
|
os.environ['GRPC_TRACE'] = '' |
|
|
|
|
|
import warnings |
|
warnings.filterwarnings('ignore', category=UserWarning) |
|
warnings.filterwarnings('ignore', category=FutureWarning) |
|
|
|
|
|
|
|
|
|
LLAMA_REPO_ID = os.getenv("LLAMA_REPO_ID", "mradermacher/fin-o1-14b-gguf") |
|
LLAMA_FILENAME = os.getenv("LLAMA_FILENAME", "fin-o1-14b.Q4_K_S.gguf") |
|
LLAMA_N_THREADS = int(os.getenv("LLAMA_N_THREADS", str(max(1, (os.cpu_count() or 2) - 0)))) |
|
LLAMA_CTX_SIZE = int(os.getenv("LLAMA_CTX_SIZE", "1024")) |
|
LLAMA_MAX_TOKENS = int(os.getenv("LLAMA_MAX_TOKENS", "768")) |
|
|
|
|
|
RAPIDAPI_HOST = "alpha-vantage.p.rapidapi.com" |
|
|
|
|
|
FINNHUB_KEYS_RAW = os.getenv("FINNHUB_KEYS", "") |
|
if FINNHUB_KEYS_RAW: |
|
FINNHUB_KEYS = [key.strip() for key in FINNHUB_KEYS_RAW.split('\n') if key.strip()] |
|
else: |
|
FINNHUB_KEYS = [] |
|
|
|
|
|
RAPIDAPI_KEYS_RAW = os.getenv("RAPIDAPI_KEYS", "") |
|
if RAPIDAPI_KEYS_RAW: |
|
RAPIDAPI_KEYS = [key.strip() for key in RAPIDAPI_KEYS_RAW.split('\n') if key.strip()] |
|
else: |
|
RAPIDAPI_KEYS = [] |
|
|
|
|
|
FINNHUB_KEYS = [key for key in FINNHUB_KEYS if key.strip()] |
|
|
|
|
|
if not FINNHUB_KEYS: |
|
print("β οΈ Warning: No Finnhub API keys found in secrets") |
|
if not RAPIDAPI_KEYS: |
|
print("β οΈ Warning: No RapidAPI keys found in secrets") |
|
print(f"π§ Llama model: {LLAMA_REPO_ID} :: {LLAMA_FILENAME}") |
|
|
|
|
|
_LLAMA_MODEL = None |
|
|
|
print("=" * 50) |
|
print("π FinRobot Forecaster Starting Up...") |
|
print("=" * 50) |
|
if FINNHUB_KEYS: |
|
print(f"π Finnhub API: {len(FINNHUB_KEYS)} keys loaded") |
|
else: |
|
print("π Finnhub API: Not configured") |
|
if RAPIDAPI_KEYS: |
|
print(f"π RapidAPI Alpha Vantage: {RAPIDAPI_HOST} ({len(RAPIDAPI_KEYS)} keys loaded)") |
|
else: |
|
print("π RapidAPI Alpha Vantage: Not configured") |
|
print("β
Application started successfully!") |
|
print("=" * 50) |
|
|
|
|
|
def _load_llama_model() -> Llama | None: |
|
global _LLAMA_MODEL |
|
if _LLAMA_MODEL is not None: |
|
return _LLAMA_MODEL |
|
try: |
|
print("β¬οΈ Downloading GGUF from Hugging Face Hub if not cached...") |
|
|
|
repo_candidates = [ |
|
LLAMA_REPO_ID, |
|
"mradermacher/Fin-o1-14B-GGUF", |
|
"mradermacher/fin-o1-14b-gguf", |
|
|
|
"tarun7r/Finance-Llama-8B-q4_k_m-GGUF", |
|
] |
|
file_candidates = [ |
|
LLAMA_FILENAME, |
|
"fin-o1-14b.Q4_K_S.gguf", |
|
"Fin-o1-14B.Q4_K_S.gguf", |
|
"fin-o1-14b.Q4_K_M.gguf", |
|
"Fin-o1-14B.Q4_K_M.gguf", |
|
|
|
"Finance-Llama-8B.Q4_K_M.gguf", |
|
] |
|
last_error: Exception | None = None |
|
for repo_id in repo_candidates: |
|
for filename in file_candidates: |
|
for prefix in ["", "gguf/"]: |
|
candidate = prefix + filename |
|
try: |
|
print(f"π Trying model: {repo_id} :: {candidate}") |
|
model_path = hf_hub_download( |
|
repo_id=repo_id, |
|
filename=candidate, |
|
local_dir=None, |
|
) |
|
print(f"π¦ GGUF path: {model_path}") |
|
print(f"π§© Initializing llama.cpp (threads={LLAMA_N_THREADS}, ctx={LLAMA_CTX_SIZE})") |
|
try: |
|
_LLAMA_MODEL = Llama( |
|
model_path=model_path, |
|
n_threads=LLAMA_N_THREADS, |
|
n_ctx=LLAMA_CTX_SIZE, |
|
n_gpu_layers=0, |
|
use_mmap=False, |
|
verbose=False, |
|
) |
|
print("β
Llama model loaded") |
|
return _LLAMA_MODEL |
|
except Exception as init_e: |
|
|
|
print("β»οΈ Re-downloading model to avoid potential cache corruption...") |
|
try: |
|
model_path = hf_hub_download( |
|
repo_id=repo_id, |
|
filename=candidate, |
|
local_dir=None, |
|
force_download=True, |
|
local_files_only=False, |
|
) |
|
_LLAMA_MODEL = Llama( |
|
model_path=model_path, |
|
n_threads=LLAMA_N_THREADS, |
|
n_ctx=LLAMA_CTX_SIZE, |
|
n_gpu_layers=0, |
|
use_mmap=False, |
|
verbose=False, |
|
) |
|
print("β
Llama model loaded after re-download") |
|
return _LLAMA_MODEL |
|
except Exception as retry_e: |
|
last_error = retry_e |
|
continue |
|
except Exception as sub_e: |
|
last_error = sub_e |
|
continue |
|
|
|
raise last_error or RuntimeError("Failed to resolve and initialize any GGUF model") |
|
except Exception as e: |
|
print(f"β Failed to load llama model: {e}") |
|
return None |
|
|
|
|
|
if FINNHUB_KEYS: |
|
|
|
finnhub_client = finnhub.Client(api_key=FINNHUB_KEYS[0]) |
|
print(f"β
Finnhub configured with {len(FINNHUB_KEYS)} keys") |
|
else: |
|
finnhub_client = None |
|
print("β οΈ Finnhub not configured - will use mock news data") |
|
|
|
|
|
def create_session(): |
|
session = requests.Session() |
|
retry_strategy = Retry( |
|
total=3, |
|
backoff_factor=1, |
|
status_forcelist=[429, 500, 502, 503, 504], |
|
) |
|
adapter = HTTPAdapter(max_retries=retry_strategy) |
|
session.mount("http://", adapter) |
|
session.mount("https://", adapter) |
|
return session |
|
|
|
|
|
requests_session = create_session() |
|
|
|
SYSTEM_PROMPT = ( |
|
"You are a seasoned stock-market analyst. " |
|
"Given recent company news and optional basic financials, " |
|
"return:\n" |
|
"[Positive Developments] β 2-4 bullets\n" |
|
"[Potential Concerns] β 2-4 bullets\n" |
|
"[Prediction & Analysis] β a one-week price outlook with rationale." |
|
) |
|
|
|
|
|
|
|
def today() -> str: |
|
return date.today().strftime("%Y-%m-%d") |
|
|
|
def n_weeks_before(date_string: str, n: int) -> str: |
|
return (datetime.strptime(date_string, "%Y-%m-%d") - |
|
timedelta(days=7 * n)).strftime("%Y-%m-%d") |
|
|
|
|
|
|
|
def get_stock_data(symbol: str, steps: list[str]) -> pd.DataFrame: |
|
|
|
for rapidapi_key in RAPIDAPI_KEYS: |
|
try: |
|
print(f"π Fetching stock data for {symbol} via RapidAPI (key: {rapidapi_key[:8]}...)") |
|
|
|
|
|
url = f"https://{RAPIDAPI_HOST}/query" |
|
|
|
headers = { |
|
"X-RapidAPI-Host": RAPIDAPI_HOST, |
|
"X-RapidAPI-Key": rapidapi_key |
|
} |
|
|
|
params = { |
|
"function": "TIME_SERIES_DAILY", |
|
"symbol": symbol, |
|
"outputsize": "full", |
|
"datatype": "csv" |
|
} |
|
|
|
|
|
for attempt in range(3): |
|
try: |
|
resp = requests_session.get(url, headers=headers, params=params, timeout=30) |
|
if not resp.ok: |
|
print(f"RapidAPI HTTP error {resp.status_code} with key {rapidapi_key[:8]}..., attempt {attempt + 1}") |
|
time.sleep(2 ** attempt) |
|
continue |
|
|
|
text = resp.text.strip() |
|
if text.startswith("{"): |
|
info = resp.json() |
|
msg = info.get("Note") or info.get("Error Message") or info.get("Information") or str(info) |
|
if "rate limit" in msg.lower() or "quota" in msg.lower(): |
|
print(f"RapidAPI rate limit hit with key {rapidapi_key[:8]}..., trying next key") |
|
break |
|
raise RuntimeError(f"RapidAPI Alpha Vantage Error: {msg}") |
|
|
|
|
|
df = pd.read_csv(StringIO(text)) |
|
date_col = "timestamp" if "timestamp" in df.columns else df.columns[0] |
|
df[date_col] = pd.to_datetime(df[date_col]) |
|
df = df.sort_values(date_col).set_index(date_col) |
|
|
|
data = {"Start Date": [], "End Date": [], "Start Price": [], "End Price": []} |
|
for i in range(len(steps) - 1): |
|
s_date = pd.to_datetime(steps[i]) |
|
e_date = pd.to_datetime(steps[i+1]) |
|
seg = df.loc[s_date:e_date] |
|
if seg.empty: |
|
raise RuntimeError( |
|
f"RapidAPI Alpha Vantage cannot get {symbol} data for {steps[i]} β {steps[i+1]}" |
|
) |
|
data["Start Date"].append(seg.index[0]) |
|
data["Start Price"].append(seg["close"].iloc[0]) |
|
data["End Date"].append(seg.index[-1]) |
|
data["End Price"].append(seg["close"].iloc[-1]) |
|
time.sleep(1) |
|
|
|
print(f"β
Successfully retrieved {symbol} data via RapidAPI (key: {rapidapi_key[:8]}...)") |
|
return pd.DataFrame(data) |
|
|
|
except requests.exceptions.Timeout: |
|
print(f"RapidAPI timeout with key {rapidapi_key[:8]}..., attempt {attempt + 1}") |
|
if attempt < 2: |
|
time.sleep(5 * (attempt + 1)) |
|
continue |
|
else: |
|
break |
|
except requests.exceptions.RequestException as e: |
|
print(f"RapidAPI request error with key {rapidapi_key[:8]}..., attempt {attempt + 1}: {e}") |
|
if attempt < 2: |
|
time.sleep(3) |
|
continue |
|
else: |
|
break |
|
|
|
except Exception as e: |
|
print(f"RapidAPI Alpha Vantage failed with key {rapidapi_key[:8]}...: {e}") |
|
continue |
|
|
|
|
|
print("β οΈ All RapidAPI keys failed, using mock data for demonstration...") |
|
return create_mock_stock_data(symbol, steps) |
|
|
|
def create_mock_stock_data(symbol: str, steps: list[str]) -> pd.DataFrame: |
|
"""TαΊ‘o mock data Δα» demo khi API khΓ΄ng hoαΊ‘t Δα»ng""" |
|
import numpy as np |
|
|
|
data = {"Start Date": [], "End Date": [], "Start Price": [], "End Price": []} |
|
|
|
|
|
base_prices = { |
|
"AAPL": 180.0, "MSFT": 350.0, "GOOGL": 140.0, |
|
"TSLA": 200.0, "NVDA": 450.0, "AMZN": 150.0 |
|
} |
|
base_price = base_prices.get(symbol.upper(), 150.0) |
|
|
|
for i in range(len(steps) - 1): |
|
s_date = pd.to_datetime(steps[i]) |
|
e_date = pd.to_datetime(steps[i+1]) |
|
|
|
|
|
start_price = base_price + np.random.normal(0, 5) |
|
end_price = start_price + np.random.normal(2, 8) |
|
|
|
data["Start Date"].append(s_date) |
|
data["Start Price"].append(round(start_price, 2)) |
|
data["End Date"].append(e_date) |
|
data["End Price"].append(round(end_price, 2)) |
|
|
|
base_price = end_price |
|
|
|
return pd.DataFrame(data) |
|
|
|
def current_basics(symbol: str, curday: str) -> dict: |
|
|
|
if not FINNHUB_KEYS: |
|
print(f"β οΈ Finnhub not configured, skipping financial basics for {symbol}") |
|
return {} |
|
|
|
|
|
for api_key in FINNHUB_KEYS: |
|
try: |
|
client = finnhub.Client(api_key=api_key) |
|
|
|
raw = client.company_basic_financials(symbol, "all") |
|
if not raw["series"]: |
|
continue |
|
merged = defaultdict(dict) |
|
for metric, vals in raw["series"]["quarterly"].items(): |
|
for v in vals: |
|
merged[v["period"]][metric] = v["v"] |
|
|
|
latest = max((p for p in merged if p <= curday), default=None) |
|
if latest is None: |
|
continue |
|
d = dict(merged[latest]) |
|
d["period"] = latest |
|
return d |
|
except Exception as e: |
|
print(f"Error getting basics for {symbol} with key {api_key[:8]}...: {e}") |
|
time.sleep(2) |
|
continue |
|
return {} |
|
|
|
def attach_news(symbol: str, df: pd.DataFrame) -> pd.DataFrame: |
|
news_col = [] |
|
for _, row in df.iterrows(): |
|
start = row["Start Date"].strftime("%Y-%m-%d") |
|
end = row["End Date"].strftime("%Y-%m-%d") |
|
time.sleep(2) |
|
|
|
|
|
if not FINNHUB_KEYS: |
|
print(f"β οΈ Finnhub not configured, using mock news for {symbol}") |
|
news_data = create_mock_news(symbol, start, end) |
|
news_col.append(json.dumps(news_data)) |
|
continue |
|
|
|
|
|
news_data = [] |
|
for api_key in FINNHUB_KEYS: |
|
try: |
|
client = finnhub.Client(api_key=api_key) |
|
weekly = client.company_news(symbol, _from=start, to=end) |
|
weekly_fmt = [ |
|
{ |
|
"date" : datetime.fromtimestamp(n["datetime"]).strftime("%Y%m%d%H%M%S"), |
|
"headline": n["headline"], |
|
"summary" : n["summary"], |
|
} |
|
for n in weekly |
|
] |
|
weekly_fmt.sort(key=lambda x: x["date"]) |
|
news_data = weekly_fmt |
|
break |
|
except Exception as e: |
|
print(f"Error with Finnhub key {api_key[:8]}... for {symbol} from {start} to {end}: {e}") |
|
time.sleep(3) |
|
continue |
|
|
|
|
|
if not news_data: |
|
news_data = create_mock_news(symbol, start, end) |
|
|
|
news_col.append(json.dumps(news_data)) |
|
df["News"] = news_col |
|
return df |
|
|
|
def create_mock_news(symbol: str, start: str, end: str) -> list: |
|
"""TαΊ‘o mock news data khi API khΓ΄ng hoαΊ‘t Δα»ng""" |
|
mock_news = [ |
|
{ |
|
"date": f"{start}120000", |
|
"headline": f"{symbol} Shows Strong Performance in Recent Trading", |
|
"summary": f"Company {symbol} has demonstrated resilience in the current market conditions with positive investor sentiment." |
|
}, |
|
{ |
|
"date": f"{end}090000", |
|
"headline": f"Analysts Maintain Positive Outlook for {symbol}", |
|
"summary": f"Financial analysts continue to recommend {symbol} based on strong fundamentals and growth prospects." |
|
} |
|
] |
|
return mock_news |
|
|
|
|
|
|
|
def sample_news(news: list[str], k: int = 5) -> list[str]: |
|
if len(news) <= k: |
|
return news |
|
return [news[i] for i in sorted(random.sample(range(len(news)), k))] |
|
|
|
def make_prompt(symbol: str, df: pd.DataFrame, curday: str, use_basics=False) -> str: |
|
|
|
company_blurb = f"[Company Introduction]:\n{symbol} is a publicly traded company.\n" |
|
|
|
if FINNHUB_KEYS: |
|
for api_key in FINNHUB_KEYS: |
|
try: |
|
client = finnhub.Client(api_key=api_key) |
|
prof = client.company_profile2(symbol=symbol) |
|
company_blurb = ( |
|
f"[Company Introduction]:\n{prof['name']} operates in the " |
|
f"{prof['finnhubIndustry']} sector ({prof['country']}). " |
|
f"Founded {prof['ipo']}, market cap {prof['marketCapitalization']:.1f} " |
|
f"{prof['currency']}; ticker {symbol} on {prof['exchange']}.\n" |
|
) |
|
break |
|
except Exception as e: |
|
print(f"Error getting company profile for {symbol} with key {api_key[:8]}...: {e}") |
|
time.sleep(2) |
|
continue |
|
else: |
|
print(f"β οΈ Finnhub not configured, using basic company info for {symbol}") |
|
|
|
|
|
past_block = "" |
|
for _, row in df.iterrows(): |
|
term = "increased" if row["End Price"] > row["Start Price"] else "decreased" |
|
head = (f"From {row['Start Date']:%Y-%m-%d} to {row['End Date']:%Y-%m-%d}, " |
|
f"{symbol}'s stock price {term} from " |
|
f"{row['Start Price']:.2f} to {row['End Price']:.2f}.") |
|
news_items = json.loads(row["News"]) |
|
summaries = [ |
|
f"[Headline] {n['headline']}\n[Summary] {n['summary']}\n" |
|
for n in news_items |
|
if not n["summary"].startswith("Looking for stock market analysis") |
|
] |
|
past_block += "\n" + head + "\n" + "".join(sample_news(summaries, 5)) |
|
|
|
|
|
if use_basics: |
|
basics = current_basics(symbol, curday) |
|
if basics: |
|
basics_txt = "\n".join(f"{k}: {v}" for k, v in basics.items() if k != "period") |
|
basics_block = (f"\n[Basic Financials] (reported {basics['period']}):\n{basics_txt}\n") |
|
else: |
|
basics_block = "\n[Basic Financials]: not available\n" |
|
else: |
|
basics_block = "\n[Basic Financials]: not requested\n" |
|
|
|
horizon = f"{curday} to {n_weeks_before(curday, -1)}" |
|
final_user_msg = ( |
|
company_blurb |
|
+ past_block |
|
+ basics_block |
|
+ f"\nBased on all information before {curday}, analyse positive " |
|
"developments and potential concerns for {symbol}, then predict its " |
|
f"price movement for next week ({horizon})." |
|
) |
|
return final_user_msg |
|
|
|
|
|
|
|
def chat_completion(prompt: str, |
|
model: str = "finance-llama-8b-gguf", |
|
temperature: float = 0.2, |
|
stream: bool = False, |
|
symbol: str = "STOCK") -> str: |
|
llama = _load_llama_model() |
|
if llama is None: |
|
print(f"β οΈ Llama model not available, using mock response for {symbol}") |
|
return create_mock_ai_response(symbol) |
|
|
|
full_prompt = f"{SYSTEM_PROMPT}\n\n{prompt}\n\n### Response:\n" |
|
|
|
try: |
|
if stream: |
|
chunks = llama.create_completion( |
|
prompt=full_prompt, |
|
temperature=temperature, |
|
top_p=0.9, |
|
max_tokens=LLAMA_MAX_TOKENS, |
|
stream=True |
|
) |
|
collected = [] |
|
for ch in chunks: |
|
delta = ch.get("choices", [{}])[0].get("text", "") |
|
if delta: |
|
print(delta, end="", flush=True) |
|
collected.append(delta) |
|
print() |
|
return "".join(collected) |
|
else: |
|
output = llama.create_completion( |
|
prompt=full_prompt, |
|
temperature=temperature, |
|
top_p=0.9, |
|
max_tokens=LLAMA_MAX_TOKENS, |
|
stream=False |
|
) |
|
return output.get("choices", [{}])[0].get("text", "").strip() |
|
except Exception as e: |
|
print(f"Generation error: {e}") |
|
return create_mock_ai_response(symbol) |
|
|
|
def create_mock_ai_response(symbol: str) -> str: |
|
"""TαΊ‘o mock AI response khi Google API khΓ΄ng hoαΊ‘t Δα»ng""" |
|
return f""" |
|
[Positive Developments] |
|
β’ Strong market position and brand recognition for {symbol} |
|
β’ Recent quarterly earnings showing growth potential |
|
β’ Positive analyst sentiment and institutional investor interest |
|
β’ Technological innovation and market expansion opportunities |
|
|
|
[Potential Concerns] |
|
β’ Market volatility and economic uncertainty |
|
β’ Competitive pressures in the industry |
|
β’ Regulatory changes that may impact operations |
|
β’ Global economic factors affecting stock performance |
|
|
|
[Prediction & Analysis] |
|
Based on the current market conditions and company fundamentals, {symbol} is expected to show moderate growth over the next week. The stock may experience some volatility but should maintain an upward trend with a potential price increase of 2-5%. This prediction is based on current market sentiment and technical analysis patterns. |
|
|
|
Note: This is a demonstration response using mock data. For real investment decisions, please consult with qualified financial professionals. |
|
""" |
|
|
|
|
|
|
|
def predict(symbol: str = "AAPL", |
|
curday: str = today(), |
|
n_weeks: int = 3, |
|
use_basics: bool = False, |
|
stream: bool = False) -> tuple[str, str]: |
|
try: |
|
steps = [n_weeks_before(curday, n) for n in range(n_weeks + 1)][::-1] |
|
df = get_stock_data(symbol, steps) |
|
df = attach_news(symbol, df) |
|
|
|
prompt_info = make_prompt(symbol, df, curday, use_basics) |
|
answer = chat_completion(prompt_info, stream=stream, symbol=symbol) |
|
|
|
return prompt_info, answer |
|
except Exception as e: |
|
error_msg = f"Error in prediction: {str(e)}" |
|
print(f"Prediction error: {e}") |
|
return error_msg, error_msg |
|
|
|
|
|
|
|
def hf_predict(symbol, n_weeks, use_basics): |
|
|
|
curday = date.today().strftime("%Y-%m-%d") |
|
|
|
prompt, answer = predict( |
|
symbol=symbol.upper(), |
|
curday=curday, |
|
n_weeks=int(n_weeks), |
|
use_basics=bool(use_basics), |
|
stream=False |
|
) |
|
return prompt, answer |
|
|
|
|
|
|
|
def create_interface(): |
|
with gr.Blocks( |
|
title="FinRobot Forecaster", |
|
theme=gr.themes.Soft(), |
|
css=""" |
|
.gradio-container { |
|
max-width: 1200px !important; |
|
margin: auto !important; |
|
} |
|
#model_prompt_textbox textarea { |
|
overflow-y: auto !important; |
|
max-height: none !important; |
|
min-height: 400px !important; |
|
resize: vertical !important; |
|
white-space: pre-wrap !important; |
|
word-wrap: break-word !important; |
|
height: auto !important; |
|
} |
|
#model_prompt_textbox { |
|
height: auto !important; |
|
} |
|
#analysis_results_textbox textarea { |
|
overflow-y: auto !important; |
|
max-height: none !important; |
|
min-height: 400px !important; |
|
resize: vertical !important; |
|
white-space: pre-wrap !important; |
|
word-wrap: break-word !important; |
|
height: auto !important; |
|
} |
|
#analysis_results_textbox { |
|
height: auto !important; |
|
} |
|
.textarea textarea { |
|
overflow-y: auto !important; |
|
max-height: 500px !important; |
|
resize: vertical !important; |
|
} |
|
.textarea { |
|
height: auto !important; |
|
min-height: 300px !important; |
|
} |
|
.gradio-textbox { |
|
height: auto !important; |
|
max-height: none !important; |
|
} |
|
.gradio-textbox textarea { |
|
height: auto !important; |
|
max-height: none !important; |
|
overflow-y: auto !important; |
|
} |
|
""" |
|
) as demo: |
|
gr.Markdown(""" |
|
# π€ FinRobot Forecaster |
|
|
|
**AI-powered stock market analysis and prediction using advanced language models** |
|
|
|
This application analyzes stock market data, company news, and financial metrics to provide comprehensive market insights and predictions. |
|
|
|
β οΈ **Note**: Free API keys have daily rate limits. If you encounter errors, the app will use mock data for demonstration purposes. |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
symbol = gr.Textbox( |
|
label="Stock Symbol", |
|
value="AAPL", |
|
placeholder="Enter stock symbol (e.g., AAPL, MSFT, GOOGL)", |
|
info="Enter the ticker symbol of the stock you want to analyze" |
|
) |
|
n_weeks = gr.Slider( |
|
1, 6, |
|
value=3, |
|
step=1, |
|
label="Historical Weeks to Analyze", |
|
info="Number of weeks of historical data to include in analysis" |
|
) |
|
use_basics = gr.Checkbox( |
|
label="Include Basic Financials", |
|
value=True, |
|
info="Include basic financial metrics in the analysis" |
|
) |
|
btn = gr.Button( |
|
"π Run Analysis", |
|
variant="primary" |
|
) |
|
|
|
with gr.Column(scale=2): |
|
with gr.Tabs(): |
|
with gr.Tab("π Analysis Results"): |
|
gr.Markdown("**AI Analysis & Prediction**") |
|
output_answer = gr.Textbox( |
|
label="", |
|
lines=40, |
|
show_copy_button=True, |
|
interactive=False, |
|
placeholder="AI analysis and predictions will appear here...", |
|
container=True, |
|
scale=1, |
|
elem_id="analysis_results_textbox" |
|
) |
|
with gr.Tab("π Model Prompt"): |
|
gr.Markdown("**Generated Prompt**") |
|
output_prompt = gr.Textbox( |
|
label="", |
|
lines=40, |
|
show_copy_button=True, |
|
interactive=False, |
|
placeholder="Generated prompt will appear here...", |
|
container=True, |
|
scale=1, |
|
elem_id="model_prompt_textbox" |
|
) |
|
|
|
|
|
gr.Examples( |
|
examples=[ |
|
["AAPL", 3, False], |
|
["MSFT", 4, True], |
|
["GOOGL", 2, False], |
|
["TSLA", 5, True], |
|
["NVDA", 3, True] |
|
], |
|
inputs=[symbol, n_weeks, use_basics], |
|
label="π‘ Try these examples" |
|
) |
|
|
|
|
|
btn.click( |
|
fn=hf_predict, |
|
inputs=[symbol, n_weeks, use_basics], |
|
outputs=[output_prompt, output_answer], |
|
show_progress=True |
|
) |
|
|
|
|
|
|
|
gr.Markdown(""" |
|
--- |
|
**Disclaimer**: This application is for educational and research purposes only. |
|
The predictions and analysis provided should not be considered as financial advice. |
|
Always consult with qualified financial professionals before making investment decisions. |
|
""") |
|
|
|
return demo |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
demo = create_interface() |
|
demo.launch( |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
share=False, |
|
show_error=True, |
|
debug=False, |
|
quiet=True |
|
) |
|
|