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
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() |