You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
11 KiB
Python

"""
5-Year Quarter-by-Quarter Comparison: Stock Oracle vs YFinance_plus
"""
import requests
import yfinance_plus as yf
from datetime import datetime, timezone
import pandas as pd
import json
from typing import Dict, Any, List
# API configuration
API_URL = "http://localhost:18001/api/v1"
def get_yfinance_plus_5year_data(ticker: str) -> Dict[str, Any]:
"""Get 5 years of quarterly financial data from yfinance_plus"""
print(f"\n=== Getting 5-year yfinance_plus data for {ticker} ===")
stock = yf.Ticker(ticker)
# Get quarterly financial statements
quarterly_income = stock.quarterly_income_stmt
quarterly_balance = stock.quarterly_balance_sheet
quarterly_cashflow = stock.quarterly_cashflow
# Get company info
info = stock.info
print(f"Company: {info.get('longName', 'N/A')}")
print(f"Shares Outstanding: {info.get('sharesOutstanding', 0):,.0f}")
print(f"Available quarters in income statement: {len(quarterly_income.columns)}")
print(f"Date range: {quarterly_income.columns[-1]} to {quarterly_income.columns[0]}")
# Process quarterly data
quarterly_data = []
for quarter_date in quarterly_income.columns:
try:
# Income statement
revenue = quarterly_income.loc['Total Revenue', quarter_date] if 'Total Revenue' in quarterly_income.index else None
net_income = quarterly_income.loc['Net Income', quarter_date] if 'Net Income' in quarterly_income.index else None
gross_profit = quarterly_income.loc['Gross Profit', quarter_date] if 'Gross Profit' in quarterly_income.index else None
operating_income = quarterly_income.loc['Operating Income', quarter_date] if 'Operating Income' in quarterly_income.index else None
# Balance sheet (if available for this quarter)
total_assets = None
total_equity = None
total_debt = None
cash = None
if not quarterly_balance.empty and quarter_date in quarterly_balance.columns:
total_assets = quarterly_balance.loc['Total Assets', quarter_date] if 'Total Assets' in quarterly_balance.index else None
total_equity = quarterly_balance.loc['Total Equity Gross Minority Interest', quarter_date] if 'Total Equity Gross Minority Interest' in quarterly_balance.index else None
total_debt = quarterly_balance.loc['Total Debt', quarter_date] if 'Total Debt' in quarterly_balance.index else None
cash = quarterly_balance.loc['Cash And Cash Equivalents', quarter_date] if 'Cash And Cash Equivalents' in quarterly_balance.index else None
# Cash flow (if available for this quarter)
operating_cash_flow = None
capex = None
if not quarterly_cashflow.empty and quarter_date in quarterly_cashflow.columns:
operating_cash_flow = quarterly_cashflow.loc['Operating Cash Flow', quarter_date] if 'Operating Cash Flow' in quarterly_cashflow.index else None
capex = quarterly_cashflow.loc['Capital Expenditure', quarter_date] if 'Capital Expenditure' in quarterly_cashflow.index else None
# Calculate EPS
shares_outstanding = info.get('sharesOutstanding', info.get('impliedSharesOutstanding', 0))
eps = net_income / shares_outstanding if net_income and shares_outstanding > 0 else None
quarterly_data.append({
'quarter_date': quarter_date,
'revenue': revenue,
'gross_profit': gross_profit,
'operating_income': operating_income,
'net_income': net_income,
'eps': eps,
'total_assets': total_assets,
'total_equity': total_equity,
'total_debt': total_debt,
'cash': cash,
'operating_cash_flow': operating_cash_flow,
'capex': capex,
'shares_outstanding': shares_outstanding
})
except Exception as e:
print(f"Error processing quarter {quarter_date}: {e}")
continue
return {
'info': info,
'quarterly_data': quarterly_data
}
def get_stock_oracle_5year_data(ticker: str) -> Dict[str, Any]:
"""Get 5 years of quarterly financial data from Stock Oracle"""
print(f"\n=== Getting 5-year Stock Oracle data for {ticker} ===")
# Request 5 years of data
start_date = "2019-01-01"
end_date = "2024-12-31"
response = requests.post(
f"{API_URL}/financial/data",
json={
"ticker": ticker,
"start_date": start_date,
"end_date": end_date,
"period_type": "all",
"include_metrics": True,
"force_refresh": True # Force refresh to get latest data
}
)
if response.status_code == 200:
data = response.json()
print(f"Company: {data['company']['name']}")
print(f"Number of periods: {len(data['financial_data'])}")
# Check real vs estimated data
real_data_count = sum(1 for fd in data['financial_data'] if not fd.get('is_estimated', True))
print(f"Real data periods: {real_data_count}")
print(f"Estimated data periods: {len(data['financial_data']) - real_data_count}")
return data
else:
print(f"Error: {response.status_code} - {response.text}")
return None
def find_matching_quarters(yfinance_data: List[Dict], oracle_data: List[Dict]) -> List[tuple]:
"""Find quarters that exist in both datasets"""
# Convert YFinance quarter dates to comparable format
yf_quarters = set()
for qd in yfinance_data:
quarter_date = qd['quarter_date']
if hasattr(quarter_date, 'strftime'):
yf_quarters.add(quarter_date.strftime('%Y-%m-%d'))
else:
yf_quarters.add(str(quarter_date)[:10])
# Convert Oracle quarter dates to comparable format
oracle_quarters = set()
for fd in oracle_data:
period_date = fd['period_date']
oracle_quarters.add(period_date[:10]) # Take YYYY-MM-DD part
# Find intersection
common_quarters = yf_quarters.intersection(oracle_quarters)
print(f"\nCommon quarters found: {len(common_quarters)}")
print(f"YFinance quarters: {len(yf_quarters)}")
print(f"Oracle quarters: {len(oracle_quarters)}")
return sorted(list(common_quarters))
def compare_quarter_by_quarter(yfinance_data: Dict, oracle_data: Dict, ticker: str):
"""Compare quarter by quarter for exact matches"""
print(f"\n=== QUARTER-BY-QUARTER COMPARISON FOR {ticker} ===")
if not oracle_data or 'financial_data' not in oracle_data:
print("No Stock Oracle data to compare")
return
yf_quarterly = yfinance_data['quarterly_data']
oracle_quarterly = oracle_data['financial_data']
# Find matching quarters
common_quarters = find_matching_quarters(yf_quarterly, oracle_quarterly)
if not common_quarters:
print("No matching quarters found between datasets")
return
print(f"\nComparing {len(common_quarters)} matching quarters:")
print("=" * 100)
total_revenue_diff = 0
total_net_income_diff = 0
total_eps_diff = 0
compared_quarters = 0
for quarter_str in common_quarters[:20]: # Limit to 20 quarters for readability
# Find YFinance data for this quarter
yf_quarter = None
for qd in yf_quarterly:
quarter_date = qd['quarter_date']
if hasattr(quarter_date, 'strftime'):
qd_str = quarter_date.strftime('%Y-%m-%d')
else:
qd_str = str(quarter_date)[:10]
if qd_str == quarter_str:
yf_quarter = qd
break
# Find Oracle data for this quarter
oracle_quarter = None
for fd in oracle_quarterly:
if fd['period_date'][:10] == quarter_str:
oracle_quarter = fd
break
if not yf_quarter or not oracle_quarter:
continue
print(f"\n📅 Quarter: {quarter_str}")
print(f"Oracle Data Source: {oracle_quarter.get('data_source', 'Unknown')}")
print(f"Oracle Is Estimated: {oracle_quarter.get('is_estimated', 'Unknown')}")
# Compare Revenue
oracle_revenue = oracle_quarter.get('revenue', 0) or 0
yf_revenue = yf_quarter.get('revenue', 0) or 0
if oracle_revenue > 0 and yf_revenue > 0:
revenue_diff = ((oracle_revenue - yf_revenue) / yf_revenue) * 100
total_revenue_diff += abs(revenue_diff)
print(f"💰 Revenue:")
print(f" Oracle: ${oracle_revenue:,.0f}")
print(f" YFinance: ${yf_revenue:,.0f}")
print(f" Difference: {revenue_diff:+.2f}%")
# Compare Net Income
oracle_net = oracle_quarter.get('net_income', 0) or 0
yf_net = yf_quarter.get('net_income', 0) or 0
if oracle_net != 0 and yf_net != 0:
net_diff = ((oracle_net - yf_net) / yf_net) * 100
total_net_income_diff += abs(net_diff)
print(f"📈 Net Income:")
print(f" Oracle: ${oracle_net:,.0f}")
print(f" YFinance: ${yf_net:,.0f}")
print(f" Difference: {net_diff:+.2f}%")
# Compare EPS
oracle_eps = oracle_quarter.get('eps', 0) or 0
yf_eps = yf_quarter.get('eps', 0) or 0
if oracle_eps > 0 and yf_eps > 0:
eps_diff = ((oracle_eps - yf_eps) / yf_eps) * 100
total_eps_diff += abs(eps_diff)
print(f"📊 EPS:")
print(f" Oracle: ${oracle_eps:.2f}")
print(f" YFinance: ${yf_eps:.2f}")
print(f" Difference: {eps_diff:+.2f}%")
# Compare Shares Outstanding
oracle_shares = oracle_quarter.get('shares_outstanding', 0) or 0
yf_shares = yf_quarter.get('shares_outstanding', 0) or 0
if oracle_shares > 0 and yf_shares > 0:
shares_diff = ((oracle_shares - yf_shares) / yf_shares) * 100
print(f"🏢 Shares Outstanding:")
print(f" Oracle: {oracle_shares:,.0f}")
print(f" YFinance: {yf_shares:,.0f}")
print(f" Difference: {shares_diff:+.2f}%")
compared_quarters += 1
print("-" * 80)
# Summary statistics
if compared_quarters > 0:
print(f"\n📊 SUMMARY STATISTICS ({compared_quarters} quarters)")
print("=" * 50)
print(f"Average Revenue Difference: {total_revenue_diff/compared_quarters:.2f}%")
print(f"Average Net Income Difference: {total_net_income_diff/compared_quarters:.2f}%")
print(f"Average EPS Difference: {total_eps_diff/compared_quarters:.2f}%")
def main():
"""Main comparison function"""
tickers = ["AAPL", "MSFT"]
for ticker in tickers:
print(f"\n{'='*100}")
print(f"5-YEAR QUARTER-BY-QUARTER COMPARISON: {ticker}")
print(f"{'='*100}")
# Get data from both sources
yfinance_data = get_yfinance_plus_5year_data(ticker)
oracle_data = get_stock_oracle_5year_data(ticker)
# Compare quarter by quarter
compare_quarter_by_quarter(yfinance_data, oracle_data, ticker)
print(f"\n{'='*100}")
if __name__ == "__main__":
main()