QuantConnect Logo


InĀ [1]:
from datetime import datetime
import pandas as pd
import numpy as np

# =====================================================
# 1. INITIALIZATION & SETUP
# =====================================================
qb = QuantBook()

# Define your list of tickers here
tickers = ["NVDA", "AMZN", "JPM"]

# Data Window
start = datetime(1998, 1, 1) 
end   = datetime(2026, 3, 28)
folder_name = "Data_Financials"

# Define Field Map (Strictly raw data for Go backend processing)
field_map = {
    "CompanyProfile.SharesOutstanding": "SharesOutstanding",
    "CompanyReference.FiscalYearEnd": "LatestFiscalYearEndMonth",
    "CompanyReference.ExpectedFiscalYearEnd": "ExpectedFiscalYearEndMonth",
    "FinancialStatements.IncomeStatement.TotalRevenue.ThreeMonths": "Revenue",
    "FinancialStatements.IncomeStatement.GrossProfit.ThreeMonths": "GrossProfit",
    "FinancialStatements.IncomeStatement.OperatingIncome.ThreeMonths": "OperatingIncome",
    "FinancialStatements.IncomeStatement.NetIncome.ThreeMonths": "NetIncome",
    "FinancialStatements.IncomeStatement.PretaxIncome.ThreeMonths": "PretaxIncome",
    "FinancialStatements.IncomeStatement.TaxProvision.ThreeMonths": "TaxProvision",
    "FinancialStatements.IncomeStatement.OtherNonOperatingIncomeExpenses.ThreeMonths": "OtherNonOp",
    "FinancialStatements.IncomeStatement.EBIT.ThreeMonths": "EBIT",
    "FinancialStatements.IncomeStatement.EBITDA.ThreeMonths": "EBITDA",
    "FinancialStatements.IncomeStatement.InterestExpense.ThreeMonths": "InterestExpense",
    "FinancialStatements.IncomeStatement.ResearchAndDevelopment.ThreeMonths": "ResearchAndDevelopment",
    "FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.ThreeMonths": "SGA_Expense",
    "FinancialStatements.IncomeStatement.NetInterestIncome.ThreeMonths": "NetInterestIncome",
    "FinancialStatements.IncomeStatement.CreditLossesProvision.ThreeMonths": "CreditLossesProvision",
    "FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths": "CurrentAssets",
    "FinancialStatements.BalanceSheet.CurrentLiabilities.ThreeMonths": "CurrentLiabilities",
    "FinancialStatements.BalanceSheet.StockholdersEquity.ThreeMonths": "StockholdersEquity",
    "FinancialStatements.BalanceSheet.TotalEquity.ThreeMonths": "TotalEquity",
    "FinancialStatements.BalanceSheet.TotalAssets.ThreeMonths": "TotalAssets",
    "FinancialStatements.BalanceSheet.TotalDebt.ThreeMonths": "TotalDebt",
    "FinancialStatements.BalanceSheet.CurrentDebt.ThreeMonths": "ShortTermDebt",
    "FinancialStatements.BalanceSheet.LongTermDebt.ThreeMonths": "LongTermDebt",
    "FinancialStatements.BalanceSheet.CashAndCashEquivalents.ThreeMonths": "CashAndCashEquivalents",
    "FinancialStatements.BalanceSheet.Cash.ThreeMonths": "Cash",
    "FinancialStatements.BalanceSheet.CashEquivalents.ThreeMonths": "CashEquivalents",
    "FinancialStatements.BalanceSheet.OtherShortTermInvestments.ThreeMonths": "OtherShortTermInvestments",
    "FinancialStatements.BalanceSheet.GoodwillAndOtherIntangibleAssets.ThreeMonths": "IntangibleAssets",
    "FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths": "PreferredStock",
    "FinancialStatements.BalanceSheet.AccountsReceivable.ThreeMonths": "AccountsReceivable",
    "FinancialStatements.BalanceSheet.CapitalLeaseObligations.ThreeMonths": "CapitalLeaseObligations",
    "FinancialStatements.BalanceSheet.Inventory.ThreeMonths": "Inventory",
    "FinancialStatements.BalanceSheet.AccountsPayable.ThreeMonths": "AccountsPayable",
    "FinancialStatements.BalanceSheet.RetainedEarnings.ThreeMonths": "RetainedEarnings",
    "FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths": "FreeCashFlow",
    "FinancialStatements.CashFlowStatement.OperatingCashFlow.ThreeMonths": "OperatingCashFlow",
    "FinancialStatements.CashFlowStatement.InvestingCashFlow.ThreeMonths": "InvestingCashFlow",
    "FinancialStatements.CashFlowStatement.FinancingCashFlow.ThreeMonths": "FinancingCashFlow",
    "FinancialStatements.CashFlowStatement.StockBasedCompensation.ThreeMonths": "StockBasedCompensation",
    "FinancialStatements.CashFlowStatement.CapitalExpenditure.ThreeMonths": "CapitalExpenditure",
    "FinancialStatements.CashFlowStatement.DepreciationAndAmortization.ThreeMonths": "DepreciationAndAmortization",
    "FinancialStatements.CashFlowStatement.Depreciation.ThreeMonths": "Depreciation",
    "FinancialStatements.CashFlowStatement.RepurchaseOfCapitalStock.ThreeMonths": "Buybacks_CommonAndPreferredStock",
    "FinancialStatements.CashFlowStatement.CommonStockPayments.ThreeMonths": "Buybacks_CommonStock",
    "EarningReports.PeriodEndingDate.ThreeMonths": "FiscalPeriodEnd",
    "EarningReports.TotalDividendPerShare.ThreeMonths": "TotalDividend_PerShare",
    "EarningReports.DilutedEPS.ThreeMonths": "DilutedEPS",
    "EarningReports.DilutedAverageShares.ThreeMonths": "DilutedSharesOutstanding",
    "OperationRatios.InventoryTurnover.ThreeMonths": "InventoryTurnover",
    "OperationRatios.ReceivableTurnover.ThreeMonths": "ReceivableTurnover",
    "OperationRatios.PaymentTurnover.ThreeMonths": "PaymentTurnover",
    "OperationRatios.DaysInInventory.ThreeMonths": "DaysInInventory",
    "OperationRatios.DaysInPayment.ThreeMonths": "DaysInPayables",
    "OperationRatios.DaysInSales.ThreeMonths": "DaysInReceivables",
    "OperationRatios.TaxRate.ThreeMonths": "TaxRate",
    "ValuationRatios.ForwardPERatio": "ForwardPE_Ratio",
    "ValuationRatios.TwoYearsForwardPERatio": "TwoYearsForwardPERatio",
    "ValuationRatios.FirstYearEstimatedEPSGrowth": "FirstYearEstimatedEPSGrowth",
    "ValuationRatios.SecondYearEstimatedEPSGrowth": "SecondYearEstimatedEPSGrowth",
    "ValuationRatios.BuyBackYield": "BuyBackYield",
    "ValuationRatios.TotalYield": "TotalYield",
    "ValuationRatios.PEGRatio": "PEGRatio",
}

cols = [
    "ReportDate", "FiscalPeriodEnd", "SharesOutstanding", "LatestFiscalYearEndMonth", "ExpectedFiscalYearEndMonth",
    "Revenue", "GrossProfit", "OperatingIncome", "NetIncome", "PretaxIncome", "TaxProvision",
    "OtherNonOp", "EBIT", "EBITDA", "InterestExpense", "ResearchAndDevelopment", "SGA_Expense", "NetInterestIncome",
    "CreditLossesProvision", "CurrentAssets", "CurrentLiabilities", "StockholdersEquity", "TotalEquity", "TotalAssets",
    "TotalDebt", "ShortTermDebt", "LongTermDebt", "CashAndCashEquivalents", "Cash", "CashEquivalents", "OtherShortTermInvestments",
    "IntangibleAssets", "PreferredStock", "AccountsReceivable", "CapitalLeaseObligations", "Inventory", "AccountsPayable",
    "RetainedEarnings",  "FreeCashFlow", "OperatingCashFlow", "InvestingCashFlow", "FinancingCashFlow", "StockBasedCompensation",
    "CapitalExpenditure", "DepreciationAndAmortization", "Depreciation", "Buybacks_CommonAndPreferredStock", "Buybacks_CommonStock",
    "TotalDividend_PerShare", "DilutedEPS", "DilutedSharesOutstanding", "InventoryTurnover", "ReceivableTurnover", "PaymentTurnover",
    "DaysInInventory", "DaysInPayables", "DaysInReceivables", "TaxRate", "ForwardPE_Ratio", "TwoYearsForwardPERatio",
    "FirstYearEstimatedEPSGrowth", "SecondYearEstimatedEPSGrowth", "BuyBackYield", "TotalYield", "PEGRatio",
]

print(f"Beginning fundamental data extraction for {len(tickers)} tickers...\n")

# =====================================================
# 2. MASTER LOOP FOR ALL TICKERS
# =====================================================
for ticker in tickers:
    print(f"Processing {ticker}...")
    try:
        # Add Equity for the specific ticker
        symbol = qb.AddEquity(ticker, Resolution.Daily).Symbol

        # --- DATA EXTRACTION & ALIGNMENT ---
        all_data = []
        for selector, clean_name in field_map.items():
            df = qb.GetFundamental([symbol], selector, start, end)
            if not df.empty:
                df.index = df.index.normalize() 
                df.columns = [clean_name]
                df = df[~df.index.duplicated(keep='last')]
                all_data.append(df)

        if not all_data:
            print(f"No fundamental data found for {ticker}. Skipping.")
            continue

        fund_df = pd.concat(all_data, axis=1).sort_index()
        fund_df = fund_df[~fund_df.index.duplicated(keep='last')]
        fund_df = fund_df.ffill() 

        # SAFETY CHECK: Ensure Revenue exists before masking
        if "Revenue" not in fund_df.columns:
            print(f"{ticker} is missing 'Revenue' data. Cannot isolate quarters. Skipping.")
            continue

        # --- QUARTERLY ISOLATION MASK ---
        report_mask = fund_df["Revenue"].ne(fund_df["Revenue"].shift()) & fund_df["Revenue"].notna()
        q_data = fund_df.loc[report_mask].copy()
        
        if q_data.empty:
            print(f"No quarterly changes detected for {ticker}. Skipping.")
            continue

        # --- DATE FORMATTING ---
        q_data = q_data.reset_index().rename(columns={"index": "Date"})
        q_data["Date"] = pd.to_datetime(q_data["Date"])

        if q_data["Date"].dt.tz is None:
            q_data["Date"] = q_data["Date"].dt.tz_localize('UTC')
        q_data["Date"] = q_data["Date"].dt.tz_convert('US/Eastern')

        q_data["ReportDate"] = q_data["Date"].dt.strftime('%Y-%m-%d')
        
        # SAFETY CHECKS: Only format these columns if they actually exist for this stock
        if "FiscalPeriodEnd" in q_data.columns:
            q_data["FiscalPeriodEnd"] = pd.to_datetime(q_data["FiscalPeriodEnd"]).dt.strftime('%Y-%m-%d')
        if 'ExpectedFiscalYearEndMonth' in q_data.columns:
            q_data['ExpectedFiscalYearEndMonth'] = q_data['ExpectedFiscalYearEndMonth'].astype(str).str.split('.').str[0]
        if 'LatestFiscalYearEndMonth' in q_data.columns:
            q_data['LatestFiscalYearEndMonth'] = q_data['LatestFiscalYearEndMonth'].astype(str).str.split('.').str[0]

        # --- FINAL TABLE SELECTION & EXPORT ---
        # Only select columns that actually exist in our q_data to prevent KeyErrors
        final_cols = [c for c in cols if c in q_data.columns]
        
        # Ensure ReportDate is always first
        if "ReportDate" not in final_cols:
            final_cols.insert(0, "ReportDate")
            
        final_table = q_data[final_cols].copy()
        final_table.insert(0, "Symbol", ticker)

        csv_output = final_table.to_csv(index=False, na_rep="", lineterminator='\n').strip()

        file_name = f"financials_quarterly_{ticker}.csv"
        object_store_key = f"{folder_name}/{file_name}"

        qb.ObjectStore.Save(object_store_key, csv_output)

        print(f"Extracted {len(final_table)} quarterly reports for {ticker}.")

    except Exception as e:
        print(f"Error processing {ticker}: {str(e)}")

print("\nAll fundamental data processing complete!")