229 lines
10 KiB
Python
229 lines
10 KiB
Python
|
|
"""
|
|||
|
|
Mapping verification: yfinance vs defeatbeta-api for AAPL.
|
|||
|
|
Prints type + 2 representative rows/values for each mapped pair.
|
|||
|
|
Methods that fail are reported inline (not crashed).
|
|||
|
|
"""
|
|||
|
|
import warnings
|
|||
|
|
warnings.filterwarnings("ignore")
|
|||
|
|
|
|||
|
|
import yfinance as yf
|
|||
|
|
from defeatbeta_api.data.ticker import Ticker
|
|||
|
|
import pandas as pd
|
|||
|
|
|
|||
|
|
SYMBOL = "AAPL"
|
|||
|
|
|
|||
|
|
yf_t = yf.Ticker(SYMBOL)
|
|||
|
|
db_t = Ticker(SYMBOL)
|
|||
|
|
|
|||
|
|
PASS = "✓"
|
|||
|
|
FAIL = "✗"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def try_db(fn, *args, **kwargs):
|
|||
|
|
"""Call a defeatbeta method, return (result, None) or (None, error_str)."""
|
|||
|
|
try:
|
|||
|
|
return fn(*args, **kwargs), None
|
|||
|
|
except Exception as e:
|
|||
|
|
return None, str(e)[:80]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def show_df(label, df, n=2):
|
|||
|
|
if df is None:
|
|||
|
|
return
|
|||
|
|
if isinstance(df, pd.DataFrame):
|
|||
|
|
print(f" {label} ({df.shape[0]}r×{df.shape[1]}c), last {n} rows:")
|
|||
|
|
cols = list(df.columns)[:6]
|
|||
|
|
print(df[cols].tail(n).to_string(index=False))
|
|||
|
|
elif isinstance(df, pd.Series):
|
|||
|
|
print(f" {label} (Series len={len(df)}), last {n}:")
|
|||
|
|
print(df.tail(n).to_string())
|
|||
|
|
|
|||
|
|
|
|||
|
|
def row(label, yf_val, yf_type_label, db_result, db_err):
|
|||
|
|
ok = PASS if db_result is not None else FAIL
|
|||
|
|
print(f"\n{ok} {label}")
|
|||
|
|
if db_err:
|
|||
|
|
print(f" yfinance → {yf_type_label}: {yf_val!r}")
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" yfinance type={yf_type_label} value={yf_val!r}")
|
|||
|
|
print(f" defeatbeta type={type(db_result).__name__}", end="")
|
|||
|
|
if isinstance(db_result, pd.DataFrame):
|
|||
|
|
print(f" cols={list(db_result.columns)}")
|
|||
|
|
show_df("defeatbeta", db_result)
|
|||
|
|
else:
|
|||
|
|
print(f" value={db_result!r}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def section(title):
|
|||
|
|
print(f"\n{'='*68}\n {title}\n{'='*68}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── 1. PRICE DATA ─────────────────────────────────────────────────────────────
|
|||
|
|
section("1. PRICE DATA — ticker.history() vs ticker.price()")
|
|||
|
|
|
|||
|
|
yf_price = yf_t.history(period="5d")[["Open", "Close", "High", "Low", "Volume"]]
|
|||
|
|
db_price, db_price_err = try_db(db_t.price)
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_price_err else FAIL} ticker.history() vs ticker.price()")
|
|||
|
|
print(f" yfinance type=DataFrame cols={list(yf_price.columns)}")
|
|||
|
|
print(yf_price.tail(2).to_string())
|
|||
|
|
if db_price_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_price_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta type=DataFrame cols={list(db_price.columns)}")
|
|||
|
|
print(db_price.tail(2).to_string(index=False))
|
|||
|
|
|
|||
|
|
# ── 2. FINANCIAL STATEMENTS ───────────────────────────────────────────────────
|
|||
|
|
section("2. FINANCIAL STATEMENTS")
|
|||
|
|
|
|||
|
|
# yfinance uses .quarterly_income_stmt (v1.3) — old .quarterly_financials is deprecated
|
|||
|
|
yf_inc_q = yf_t.quarterly_income_stmt
|
|||
|
|
db_inc_q, db_inc_q_err = try_db(db_t.quarterly_income_statement)
|
|||
|
|
db_inc_q_df = db_inc_q.df() if db_inc_q else None
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_inc_q_err else FAIL} quarterly_income_stmt vs quarterly_income_statement()")
|
|||
|
|
print(f" yfinance type={type(yf_inc_q).__name__} shape={yf_inc_q.shape} (rows=line items, cols=quarters)")
|
|||
|
|
print(yf_inc_q.iloc[:2, :2].to_string())
|
|||
|
|
if db_inc_q_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_inc_q_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta type={type(db_inc_q).__name__} → .df() shape={db_inc_q_df.shape}")
|
|||
|
|
print(db_inc_q_df.iloc[:2, :5].to_string(index=False))
|
|||
|
|
|
|||
|
|
yf_bs = yf_t.balance_sheet
|
|||
|
|
db_bs, db_bs_err = try_db(db_t.annual_balance_sheet)
|
|||
|
|
db_bs_df = db_bs.df() if db_bs else None
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_bs_err else FAIL} balance_sheet vs annual_balance_sheet()")
|
|||
|
|
print(f" yfinance type={type(yf_bs).__name__} shape={yf_bs.shape}")
|
|||
|
|
print(yf_bs.iloc[:2, :2].to_string())
|
|||
|
|
if db_bs_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_bs_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta type={type(db_bs).__name__} → .df() shape={db_bs_df.shape}")
|
|||
|
|
print(db_bs_df.iloc[:2, :5].to_string(index=False))
|
|||
|
|
|
|||
|
|
yf_cf = yf_t.cashflow
|
|||
|
|
db_cf, db_cf_err = try_db(db_t.annual_cash_flow)
|
|||
|
|
db_cf_df = db_cf.df() if db_cf else None
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_cf_err else FAIL} cashflow vs annual_cash_flow()")
|
|||
|
|
print(f" yfinance type={type(yf_cf).__name__} shape={yf_cf.shape}")
|
|||
|
|
print(yf_cf.iloc[:2, :2].to_string())
|
|||
|
|
if db_cf_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_cf_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta → .df() shape={db_cf_df.shape}")
|
|||
|
|
print(db_cf_df.iloc[:2, :5].to_string(index=False))
|
|||
|
|
|
|||
|
|
# ── 3. VALUATION METRICS ──────────────────────────────────────────────────────
|
|||
|
|
section("3. VALUATION METRICS")
|
|||
|
|
|
|||
|
|
db_pe, db_pe_err = try_db(db_t.ttm_pe)
|
|||
|
|
db_eps, db_eps_err = try_db(db_t.ttm_eps)
|
|||
|
|
db_mc, db_mc_err = try_db(db_t.market_capitalization)
|
|||
|
|
db_pb, db_pb_err = try_db(db_t.pb_ratio)
|
|||
|
|
db_ps, db_ps_err = try_db(db_t.ps_ratio)
|
|||
|
|
|
|||
|
|
row("trailingPE → ttm_pe()", yf_t.info.get("trailingPE"), "float", db_pe, db_pe_err)
|
|||
|
|
row("trailingEps → ttm_eps()", yf_t.info.get("trailingEps"), "float", db_eps, db_eps_err)
|
|||
|
|
row("marketCap → market_cap()", yf_t.info.get("marketCap"), "int", db_mc, db_mc_err)
|
|||
|
|
row("priceToBook → pb_ratio()", yf_t.info.get("priceToBook"), "float", db_pb, db_pb_err)
|
|||
|
|
row("priceToSales → ps_ratio()", yf_t.info.get("priceToSalesTrailing12Months"), "float", db_ps, db_ps_err)
|
|||
|
|
|
|||
|
|
# ── 4. FINANCIAL RATIOS ───────────────────────────────────────────────────────
|
|||
|
|
section("4. FINANCIAL RATIOS")
|
|||
|
|
|
|||
|
|
db_roe, db_roe_err = try_db(db_t.roe)
|
|||
|
|
db_roa, db_roa_err = try_db(db_t.roa)
|
|||
|
|
db_beta, db_beta_err = try_db(db_t.beta)
|
|||
|
|
db_wacc, db_wacc_err = try_db(db_t.wacc)
|
|||
|
|
|
|||
|
|
row("returnOnEquity → roe()", yf_t.info.get("returnOnEquity"), "float", db_roe, db_roe_err)
|
|||
|
|
row("returnOnAssets → roa()", yf_t.info.get("returnOnAssets"), "float", db_roa, db_roa_err)
|
|||
|
|
row("beta → beta()", yf_t.info.get("beta"), "float", db_beta, db_beta_err)
|
|||
|
|
row("N/A → wacc()", None, "N/A", db_wacc, db_wacc_err)
|
|||
|
|
|
|||
|
|
# ── 5. GROWTH METRICS ─────────────────────────────────────────────────────────
|
|||
|
|
section("5. GROWTH METRICS")
|
|||
|
|
|
|||
|
|
db_rg, db_rg_err = try_db(db_t.quarterly_revenue_yoy_growth)
|
|||
|
|
db_eg, db_eg_err = try_db(db_t.quarterly_eps_yoy_growth)
|
|||
|
|
db_nig, db_nig_err = try_db(db_t.quarterly_net_income_yoy_growth)
|
|||
|
|
|
|||
|
|
row("revenueGrowth → quarterly_revenue_yoy_growth()", yf_t.info.get("revenueGrowth"), "float", db_rg, db_rg_err)
|
|||
|
|
row("earningsGrowth → quarterly_eps_yoy_growth()", yf_t.info.get("earningsGrowth"), "float", db_eg, db_eg_err)
|
|||
|
|
row("N/A → quarterly_net_income_yoy_growth()", None, "N/A", db_nig, db_nig_err)
|
|||
|
|
|
|||
|
|
# ── 6. MARGIN METRICS ─────────────────────────────────────────────────────────
|
|||
|
|
section("6. MARGIN METRICS")
|
|||
|
|
|
|||
|
|
db_nm, db_nm_err = try_db(db_t.quarterly_net_margin)
|
|||
|
|
db_gm, db_gm_err = try_db(db_t.quarterly_gross_margin)
|
|||
|
|
db_om, db_om_err = try_db(db_t.quarterly_operating_margin)
|
|||
|
|
|
|||
|
|
row("profitMargins → quarterly_net_margin()", yf_t.info.get("profitMargins"), "float", db_nm, db_nm_err)
|
|||
|
|
row("grossMargins → quarterly_gross_margin()", yf_t.info.get("grossMargins"), "float", db_gm, db_gm_err)
|
|||
|
|
row("operatingMargins → quarterly_op_margin()", yf_t.info.get("operatingMargins"), "float", db_om, db_om_err)
|
|||
|
|
|
|||
|
|
# ── 7. DIVIDENDS & SPLITS ─────────────────────────────────────────────────────
|
|||
|
|
section("7. DIVIDENDS & SPLITS")
|
|||
|
|
|
|||
|
|
yf_div = yf_t.dividends
|
|||
|
|
db_div, db_div_err = try_db(db_t.dividends)
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_div_err else FAIL} .dividends vs .dividends()")
|
|||
|
|
print(f" yfinance type={type(yf_div).__name__} len={len(yf_div)}")
|
|||
|
|
print(yf_div.tail(2).to_string())
|
|||
|
|
if db_div_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_div_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta type={type(db_div).__name__} shape={db_div.shape}")
|
|||
|
|
print(db_div.tail(2).to_string(index=False))
|
|||
|
|
|
|||
|
|
yf_sp = yf_t.splits
|
|||
|
|
db_sp, db_sp_err = try_db(db_t.splits)
|
|||
|
|
|
|||
|
|
print(f"\n{PASS if not db_sp_err else FAIL} .splits vs .splits()")
|
|||
|
|
print(f" yfinance type={type(yf_sp).__name__} len={len(yf_sp)}")
|
|||
|
|
print(yf_sp.tail(2).to_string())
|
|||
|
|
if db_sp_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_sp_err}")
|
|||
|
|
else:
|
|||
|
|
print(f" defeatbeta type={type(db_sp).__name__} shape={db_sp.shape}")
|
|||
|
|
print(db_sp.tail(2).to_string(index=False))
|
|||
|
|
|
|||
|
|
# ── 8. COMPANY INFO ───────────────────────────────────────────────────────────
|
|||
|
|
section("8. COMPANY INFO — .info vs .info()")
|
|||
|
|
|
|||
|
|
yf_info = yf_t.info
|
|||
|
|
db_info, db_info_err = try_db(db_t.info)
|
|||
|
|
|
|||
|
|
if db_info_err:
|
|||
|
|
print(f" defeatbeta→ ERROR: {db_info_err}")
|
|||
|
|
else:
|
|||
|
|
fields = [
|
|||
|
|
("sector", "sector", yf_info.get("sector")),
|
|||
|
|
("industry", "industry", yf_info.get("industry")),
|
|||
|
|
("employees", "full_time_employees", yf_info.get("fullTimeEmployees")),
|
|||
|
|
("website", "web_site", yf_info.get("website")),
|
|||
|
|
# Note: yf longName = company name; longBusinessSummary = description
|
|||
|
|
("longName", None, yf_info.get("longName")),
|
|||
|
|
("longBusinessSummary", "long_business_summary", yf_info.get("longBusinessSummary", "")[:60]),
|
|||
|
|
]
|
|||
|
|
print(f"\n {'Field':<30} {'yfinance':<35} {'defeatbeta'}")
|
|||
|
|
print(f" {'-'*30} {'-'*35} {'-'*30}")
|
|||
|
|
for label, db_col, yf_val in fields:
|
|||
|
|
db_val = db_info[db_col].iloc[0] if db_col and db_col in db_info.columns else "—"
|
|||
|
|
if isinstance(db_val, str) and len(db_val) > 40:
|
|||
|
|
db_val = db_val[:40] + "…"
|
|||
|
|
if isinstance(yf_val, str) and len(yf_val) > 34:
|
|||
|
|
yf_val = yf_val[:34] + "…"
|
|||
|
|
print(f" {label:<30} {str(yf_val):<35} {str(db_val)}")
|
|||
|
|
|
|||
|
|
# ── SUMMARY ───────────────────────────────────────────────────────────────────
|
|||
|
|
print(f"\n{'='*68}")
|
|||
|
|
print(" ALL CHECKS COMPLETE")
|
|||
|
|
print(f"{'='*68}\n")
|