Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pandas as pd
- import numpy as np
- import warnings
- from finvizfinance.screener.technical import Technical # Technical view!
- from finvizfinance.screener.overview import Overview
- warnings.filterwarnings("ignore")
- # ── Pomocné mapovací tabulky pro Finviz filter_dict ──────────────────────────
- # (hodnoty jsou přesné řetězce, které Finviz akceptuje)
- RSI_BINS = [
- (10, "Oversold (< 20)"),
- (20, "Oversold (< 30)"),
- (30, "Under 30"),
- (40, "Under 40"),
- (50, "Under 50"),
- (60, "Under 60"),
- (70, "Under 70"),
- (80, "Overbought (> 70)"),
- (90, "Overbought (> 80)"),
- (100, "Overbought (> 90)"),
- ]
- BETA_BINS = [
- (0, "Under 0"),
- (0.5, "Under 0.5"),
- (1.0, "Under 1"),
- (1.5, "Under 1.5"),
- (2.0, "Under 2"),
- (3.0, "Under 3"),
- (99, "Over 3"),
- ]
- ATR_BINS = [
- (0.25, "Over 0.25"),
- (0.5, "Over 0.5"),
- (1.0, "Over 1"),
- (1.5, "Over 1.5"),
- (2.0, "Over 2"),
- (2.5, "Over 2.5"),
- (3.0, "Over 3"),
- (4.0, "Over 4"),
- (5.0, "Over 5"),
- ]
- AVG_VOL_BINS = [
- (100e3, "Over 100K"),
- (200e3, "Over 200K"),
- (500e3, "Over 500K"),
- (750e3, "Over 750K"),
- (1e6, "Over 1M"),
- (2e6, "Over 2M"),
- (5e6, "Over 5M"),
- (10e6, "Over 10M"),
- (20e6, "Over 20M"),
- (50e6, "Over 50M"),
- ]
- REL_VOL_BINS = [
- (0.5, "Over 0.5"),
- (1.0, "Over 1"),
- (1.5, "Over 1.5"),
- (2.0, "Over 2"),
- (3.0, "Over 3"),
- (5.0, "Over 5"),
- (10.0, "Over 10"),
- ]
- CHANGE_BINS_POS = [
- (1, "Up"),
- (2, "Up 2%"),
- (3, "Up 3%"),
- (4, "Up 4%"),
- (5, "Up 5%"),
- (10, "Up 10%"),
- (15, "Up 15%"),
- (20, "Up 20%"),
- ]
- CHANGE_BINS_NEG = [
- (-1, "Down"),
- (-2, "Down 2%"),
- (-3, "Down 3%"),
- (-4, "Down 4%"),
- (-5, "Down 5%"),
- (-10, "Down 10%"),
- (-15, "Down 15%"),
- (-20, "Down 20%"),
- ]
- PERF_BINS_POS = [
- (2, "Up 2%"),
- (5, "Up 5%"),
- (10, "Up 10%"),
- (15, "Up 15%"),
- (20, "Up 20%"),
- (25, "Up 25%"),
- (30, "Up 30%"),
- (50, "Up 50%"),
- (100,"Up 100%"),
- ]
- PERF_BINS_NEG = [
- (-2, "Down 2%"),
- (-5, "Down 5%"),
- (-10, "Down 10%"),
- (-15, "Down 15%"),
- (-20, "Down 20%"),
- (-25, "Down 25%"),
- (-30, "Down 30%"),
- (-50, "Down 50%"),
- ]
- VOLATILITY_BINS = [
- (0.5, "Over 0.5%"),
- (1.0, "Over 1%"),
- (1.5, "Over 1.5%"),
- (2.0, "Over 2%"),
- (2.5, "Over 2.5%"),
- (3.0, "Over 3%"),
- (4.0, "Over 4%"),
- (5.0, "Over 5%"),
- (6.0, "Over 6%"),
- (7.0, "Over 7%"),
- (8.0, "Over 8%"),
- (9.0, "Over 9%"),
- (10.0, "Over 10%"),
- (15.0, "Over 15%"),
- ]
- SMA20_BINS_POS = [
- (0, "Price above SMA20"),
- (5, "5% above SMA20"),
- (10, "10% above SMA20"),
- (15, "15% above SMA20"),
- (20, "20% above SMA20"),
- (30, "30% above SMA20"),
- (40, "40% above SMA20"),
- (50, "50% above SMA20"),
- ]
- SMA50_BINS_POS = [
- (0, "Price above SMA50"),
- (5, "5% above SMA50"),
- (10, "10% above SMA50"),
- (15, "15% above SMA50"),
- (20, "20% above SMA50"),
- (30, "30% above SMA50"),
- (40, "40% above SMA50"),
- (50, "50% above SMA50"),
- ]
- SMA200_BINS_POS = [
- (0, "Price above SMA200"),
- (5, "5% above SMA200"),
- (10, "10% above SMA200"),
- (20, "20% above SMA200"),
- (30, "30% above SMA200"),
- (40, "40% above SMA200"),
- (50, "50% above SMA200"),
- ]
- HIGH52W_BINS = [
- (0, "New High"),
- (3, "0-3% below High"),
- (5, "0-5% below High"),
- (10, "0-10% below High"),
- (15, "0-15% below High"),
- (20, "0-20% below High"),
- (30, "0-30% below High"),
- (50, "0-50% below High"),
- ]
- # ── Pomocné funkce ────────────────────────────────────────────────────────────
- def to_num(s):
- """Bezpečná konverze řetězce na float."""
- try:
- return float(str(s).replace("%","\").replace(",","\").strip())
- except:
- return np.nan
- def nearest_bin(value, bins, default=None):
- """Najde nejbližší bin k hodnotě (vrátí label)."""
- if pd.isna(value):
- return default
- best_label = default
- best_dist = float("inf")
- for threshold, label in bins:
- d = abs(value - threshold)
- if d < best_dist:
- best_dist = d
- best_label = label
- return best_label
- def above_bin(value, bins, default=None):
- """Vrátí největší bin, jehož threshold je ≤ value."""
- if pd.isna(value):
- return default
- chosen = default
- for threshold, label in bins:
- if value >= threshold:
- chosen = label
- return chosen
- def normalize_col(df, col):
- if col in df.columns:
- df[col] = df[col].apply(to_num)
- return df
- # ── 1. Stažení dat – Technical view ──────────────────────────────────────────
- TECH_COLS = [
- "Ticker", "Beta", "ATR", "SMA20", "SMA50", "SMA200",
- "52W High", "52W Low", "RSI", "Price", "Change",
- "from Open", "Gap", "Volume", "Avg Volume",
- "Rel Volume", "Volatility W", "Volatility M",
- "Perf Week", "Perf Month", "Perf Quarter",
- "Perf Half Y", "Perf Year", "Perf YTD",
- ]
- def fetch_technical(signal: str, max_rows: int = 50) -> pd.DataFrame:
- ft = Technical()
- ft.set_filter(signal=signal)
- try:
- df = ft.screener_view(verbose=0)
- except Exception as e:
- print(f" ⚠ Technical view selhal ({e}), zkouším Overview …")
- ft2 = Overview()
- ft2.set_filter(signal=signal)
- df = ft2.screener_view(verbose=0)
- if df is None or df.empty:
- return pd.DataFrame()
- df = df.head(max_rows).copy()
- for col in df.columns:
- df[col] = df[col].apply(to_num) if col != "Ticker" else df[col]
- return df
- print("📡 Stahuji Top Gainers (Technical view) …")
- gainers = fetch_technical("Top Gainers")
- print(f" → {len(gainers)} tickerů | sloupce: {list(gainers.columns)}")
- print("📡 Stahuji Top Losers (Technical view) …")
- losers = fetch_technical("Top Losers")
- print(f" → {len(losers)} tickerů | sloupce: {list(losers.columns)}")
- # ── 2. Statistický profil ─────────────────────────────────────────────────────
- NUM_COLS = [
- "Beta", "ATR", "SMA20", "SMA50", "SMA200",
- "52W High", "52W Low", "RSI", "Price", "Change",
- "from Open", "Gap", "Volume", "Avg Volume",
- "Rel Volume", "Volatility W", "Volatility M",
- "Perf Week", "Perf Month",
- ]
- def compute_profile(df: pd.DataFrame) -> dict:
- p = {}
- for col in NUM_COLS:
- if col in df.columns:
- s = df[col].dropna()
- if len(s):
- p[col] = {"mean": s.mean(), "med": s.median(), "std": s.std(),
- "min": s.min(), "max": s.max()}
- return p
- def print_profile(prof: dict, label: str):
- print(f"\n{'='*65}")
- print(f" Profil: {label}")
- print(f"{'='*65}")
- for col, s in prof.items():
- print(f" {col:<14} min={s['min']:>8.2f} max={s['max']:>8.2f} "
- f"mean={s['mean']:>8.2f} med={s['med']:>8.2f}")
- prof_g = compute_profile(gainers)
- prof_l = compute_profile(losers)
- print_profile(prof_g, "Top Gainers (dnešní)")
- print_profile(prof_l, "Top Losers (dnešní)")
- # ── 3. Převod profilu na Finviz filter_dict ───────────────────────────────────
- def build_filter_dict(prof: dict, direction: str = "up") -> dict:
- """
- direction = 'up' → hledáme kandidáty na gainers
- direction = 'down' → hledáme kandidáty na losers
- """
- fd = {}
- # RSI
- # if "RSI" in prof:
- # rsi = prof["RSI"]["med"]
- # lbl = nearest_bin(rsi, RSI_BINS)
- # if lbl:
- # fd["RSI (14)"] = lbl
- # Beta
- # if "Beta" in prof:
- # beta = prof["Beta"]["med"]
- # lbl = nearest_bin(beta, BETA_BINS)
- # if lbl:
- # fd["Beta"] = lbl
- # ATR (používáme dolní kvartil jako minimum)
- # if "ATR" in prof:
- # atr = max(0.1, prof["ATR"]["med"] * 0.5)
- # lbl = above_bin(atr, ATR_BINS)
- # if lbl:
- # fd["Average True Range"] = lbl
- # Average Volume (less strict)
- if "Avg Volume" in prof:
- # Set a very low minimum to be extremely permissive
- avg_v = max(100e3, prof["Avg Volume"]["med"] * 0.05)
- lbl = above_bin(avg_v, AVG_VOL_BINS)
- if lbl:
- fd["Average Volume"] = lbl
- # Relative Volume (less strict)
- if "Rel Volume" in prof:
- # Set a very low minimum to be extremely permissive
- rv = max(0.1, prof["Rel Volume"]["med"] * 0.05)
- lbl = above_bin(rv, REL_VOL_BINS)
- if lbl:
- fd["Relative Volume"] = lbl
- # Add 'Change' filter based on user's request: "did not rise/fall so much yesterday"
- if direction == "up":
- fd["Change"] = "Upto 10%" # For gainers, ensure today's change was not already too high
- fd["Performance (Week)"] = "Upto 5%" # Not risen much last week
- fd["52-Week High/Low"] = "0-3% above Low" # Near 52-week low
- elif direction == "down":
- fd["Change"] = "Flat (0%)" # For losers, ensure today's change was not already too negative
- fd["Performance (Week)"] = "Down 5%" # Not fallen much last week
- fd["52-Week High/Low"] = "0-3% below High" # Near 52-week high
- # Change (dnešní %) - original commented out block, now superseded by explicit filter above
- # if "Change" in prof:
- # chg = prof["Change"]["med"]
- # bins = CHANGE_BINS_POS if direction == "up" else CHANGE_BINS_NEG
- # lbl = above_bin(abs(chg) * (1 if direction == "up" else -1),
- # CHANGE_BINS_POS) if direction == "up" else \
- # above_bin(chg, CHANGE_BINS_NEG)
- # if lbl:
- # fd["Change"] = lbl
- # Performance – Week
- # if "Perf Week" in prof:
- # pw = prof["Perf Week"]["med"]
- # bins = PERF_BINS_POS if direction == "up" else PERF_BINS_NEG
- # lbl = above_bin(abs(pw) if direction == "up" else pw, bins)
- # if lbl:
- # fd["Performance (Week)"] = lbl
- # Volatility (Week) – chceme min
- # if "Volatility W" in prof:
- # vw = prof["Volatility W"]["med"] * 0.5
- # lbl = above_bin(vw, VOLATILITY_BINS)
- # if lbl:
- # fd["Volatility"] = lbl
- # SMA20 – relativní vzdálenost ceny od SMA20
- # if "SMA20" in prof and direction == "up":
- # s = prof["SMA20"]["med"]
- # lbl = above_bin(s * 0.3, SMA20_BINS_POS) if s > 0 else None
- # if lbl:
- # fd["20-Day Simple Moving Average"] = lbl
- # if "SMA50" in prof and direction == "up":
- # s = prof["SMA50"]["med"]
- # lbl = above_bin(s * 0.3, SMA50_BINS_POS) if s > 0 else None
- # if lbl:
- # fd["50-Day Simple Moving Average"] = lbl
- # 52W High/Low relativní vzdálenost
- # if "52W High" in prof:
- # h = prof["52W High"]["med"] # záporné číslo = % pod 52W high
- # lbl = above_bin(abs(h) if h < 0 else 0, [
- # (0, "0-3% below High"),
- # (5, "0-5% below High"),
- # (10, "0-10% below High"),
- # (20, "0-20% below High"),
- # ])
- # if lbl:
- # fd["52-Week High/Low"] = lbl
- return fd
- fd_gainers = build_filter_dict(prof_g, direction="up")
- fd_losers = build_filter_dict(prof_l, direction="down")
- print("\n\n🔧 Filter dict pro Gainers kandidáty:")
- for k, v in fd_gainers.items():
- print(f" {k:<35} = {v}")
- print("\n🔧 Filter dict pro Losers kandidáty:")
- for k, v in fd_losers.items():
- print(f" {k:<35} = {v}")
- # ── 4. Screenovací dotaz ──────────────────────────────────────────────────────
- def screen_candidates(
- filters: dict,
- signal: str,
- label: str,
- prof: dict,
- top_n: int = 10,
- ) -> pd.DataFrame:
- ft = Technical()
- # Odstraníme filtry, které by mohly způsobit prázdný výsledek (fallback)
- applied = {}
- for k, v in filters.items():
- test_ft = Technical()
- try:
- test_ft.set_filter(signal=signal, filters_dict={**applied, k: v})
- df_test = test_ft.screener_view(verbose=0)
- if df_test is not None and not df_test.empty:
- applied[k] = v
- print(f" ✓ filtr přidán: {k} = {v} ({len(df_test)} výsl.)")
- else:
- print(f" ✗ filtr přeskočen (0 výsl.): {k} = {v}")
- except Exception as e:
- print(f" ✗ filtr chyba ({k}): {e}")
- ft.set_filter(signal=signal, filters_dict=applied)
- try:
- df = ft.screener_view(verbose=0)
- except Exception as e:
- print(f" ⚠ Finální dotaz selhal: {e}")
- return pd.DataFrame()
- if df is None or df.empty:
- return pd.DataFrame()
- for col in df.columns:
- if col != "Ticker":
- df[col] = df[col].apply(to_num)
- # Seřadit: highest |Change|
- if "Change" in df.columns:
- df = df.reindex(df["Change"].abs().sort_values(ascending=False).index)
- df["Tomorrow_Prediction"] = label
- return df.head(top_n)
- print("\n\n🔍 Screening zítřejších Gainers kandidátů …")
- cand_gainers = screen_candidates(
- fd_gainers, signal="Top Gainers",
- label="🚀 Gainer candidate", prof=prof_g, top_n=20
- )
- print("\n🔍 Screening zítřejších Losers kandidátů …")
- cand_losers = screen_candidates(
- fd_losers, signal="Top Losers",
- label="📉 Loser candidate", prof=prof_l, top_n=20
- )
- # ── 5. Výstup ─────────────────────────────────────────────────────────────────
- SHOW_COLS = [
- "Ticker", "Price", "Change", "RSI", "Beta", "ATR",
- "Rel Volume", "Avg Volume", "Volatility W",
- "SMA20", "SMA50", "52W High", "Perf Week",
- "Tomorrow_Prediction",
- ]
- def show(df: pd.DataFrame, title: str):
- cols = [c for c in SHOW_COLS if c in df.columns]
- print(f"\n{'='*75}")
- print(f" {title}")
- print(f"{'='*75}")
- if df.empty:
- print(" (žádné výsledky – zkuste uvolnit filtry)")
- return
- print(df[cols].to_string(index=False))
- show(cand_gainers, "🚀 Predikce: zítřejší TOP GAINERS")
- show(cand_losers, "📉 Predikce: zítřejší TOP LOSERS")
- # ── 6. Export ─────────────────────────────────────────────────────────────────
- if not cand_gainers.empty or not cand_losers.empty:
- out = pd.concat([cand_gainers, cand_losers], ignore_index=True)
- out.to_csv("tomorrow_candidates.csv", index=False)
- print("\n✓ Uloženo do: tomorrow_candidates.csv")
- # ── 7. Dnešní data pro referenci ──────────────────────────────────────────────
- ref_cols = [c for c in ["Ticker","Price","Change","RSI","Beta","ATR",
- "Rel Volume","Volatility W","Perf Week"] if c in gainers.columns]
- print("\n\n── Dnešní Top Gainers – Technical (vstup) ──")
- print(gainers[ref_cols].head(10).to_string(index=False) if not gainers.empty else "(prázdné)")
- ref_cols2 = [c for c in ref_cols if c in losers.columns]
- print("\n── Dnešní Top Losers – Technical (vstup) ──")
- print(losers[ref_cols2].head(10).to_string(index=False) if not losers.empty else "(prázdné)")
Add Comment
Please, Sign In to add comment