{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# When did the numbers become available?\n", "Match reported fiscal periods to SEC acceptance dates before using fundamentals in research.\n", "AAPL annual reports, FY2021\u20132025. SEC acceptance dates establish when the filing became available; fiscal year-end is not the publication date." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"Match reported fiscal periods to SEC acceptance dates before using fundamentals in research.\n", "\n", "Research example. Live observations can change.\n", "\"\"\"\n", "\n", "from datetime import UTC, datetime\n", "from textwrap import fill\n", "\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "AS_OF = datetime.now(UTC).date()\n", "NAVY, BLUE, PINK = \"#132244\", \"#8fb9ff\", \"#e7a2cc\"\n", "\n", "\n", "def fetch(qj):\n", " return {\n", " \"income\": qj.fmp.get_income_statement(symbol=\"AAPL\", period=\"annual\", limit=5),\n", " \"filings\": qj.sec.get_company_filings(symbol=\"AAPL\", form_type=\"10-K\", limit=12),\n", " }\n", "\n", "\n", "def analyze(raw):\n", " joined = verified_income(raw)\n", " if joined.empty:\n", " raise ValueError(\"No matching fiscal periods and SEC filings\")\n", " joined[\"days_to_filing\"] = (joined[\"accepted\"] - joined[\"date\"]).dt.total_seconds() / 86400\n", " joined[\"period\"] = joined[\"date\"].dt.strftime(\"%Y-%m-%d\")\n", " fig, (ax,) = canvas(\"AAPL \u00b7 the reporting clock\", \"Calendar days to SEC acceptance\")\n", " ax.bar(joined[\"period\"], joined[\"days_to_filing\"], color=BLUE, width=0.6)\n", " ax.set_xlabel(\"Fiscal period end \u00b7 FMP, matched to SEC report date\")\n", " table = joined[[\"period\", \"accepted\", \"days_to_filing\"]]\n", " return table, fig\n", "\n", "\n", "def payload(response):\n", " if hasattr(response, \"to_dict\"):\n", " response = response.to_dict()\n", " while isinstance(response, dict):\n", " if response.get(\"error\") or response.get(\"error_code\") or response.get(\"success\") is False:\n", " raise ValueError(\"Provider request failed\")\n", " if \"data\" in response:\n", " response = response[\"data\"]\n", " elif \"value\" in response:\n", " response = response[\"value\"]\n", " else:\n", " break\n", " return response\n", "\n", "\n", "def frame(response, date=\"date\"):\n", " rows = payload(response)\n", " if not isinstance(rows, list) or not rows:\n", " raise ValueError(\"No provider observations returned\")\n", " result = pd.DataFrame(rows)\n", " if date:\n", " result[date] = pd.to_datetime(result[date], utc=True)\n", " result = result.sort_values(date)\n", " return result\n", "\n", "\n", "def verified_income(raw):\n", " income = frame(raw[\"income\"])\n", " filings = frame(raw[\"filings\"], \"reportDate\")\n", " filings = filings[[\"reportDate\", \"acceptanceDateTime\", \"filingDate\"]].copy()\n", " filings[\"accepted\"] = pd.to_datetime(filings[\"acceptanceDateTime\"], utc=True)\n", " return income.merge(\n", " filings[[\"reportDate\", \"accepted\"]],\n", " left_on=\"date\",\n", " right_on=\"reportDate\",\n", " validate=\"one_to_one\",\n", " ).sort_values(\"date\")\n", "\n", "\n", "def canvas(title, ylabel, panels=1):\n", " plt.rcParams.update(\n", " {\n", " \"figure.facecolor\": NAVY,\n", " \"axes.facecolor\": NAVY,\n", " \"savefig.facecolor\": NAVY,\n", " \"text.color\": \"#e9eef8\",\n", " \"axes.labelcolor\": \"#afc1dc\",\n", " \"xtick.color\": \"#afc1dc\",\n", " \"ytick.color\": \"#afc1dc\",\n", " \"axes.edgecolor\": \"#34486b\",\n", " \"grid.color\": \"#34486b\",\n", " \"font.size\": 10,\n", " \"axes.prop_cycle\": plt.cycler(color=[BLUE, PINK, \"#ba9dea\"]),\n", " }\n", " )\n", " fig, axes = plt.subplots(panels, 1, figsize=(9, 6.1), squeeze=False, layout=\"constrained\")\n", " fig.suptitle(fill(title, width=58), fontsize=17, x=0.04, ha=\"left\", weight=\"medium\")\n", " for ax in axes.flat:\n", " ax.grid(axis=\"y\", alpha=0.45)\n", " ax.set_axisbelow(True)\n", " ax.spines[[\"top\", \"right\"]].set_visible(False)\n", " axes.flat[0].set_ylabel(ylabel)\n", " return fig, list(axes.flat)\n", "\n", "\n", "def run():\n", " from quantjourney.sdk import QuantJourneyAPI\n", "\n", " qj = QuantJourneyAPI.from_env()\n", " raw = fetch(qj)\n", " table, fig = analyze(raw)\n", " print(table.to_string(index=False))\n", " fig.savefig(OUTPUT, dpi=180)\n", " plt.show()\n", "\n", "\n", "OUTPUT = \"sdk_reporting_dates.png\"\n", "\n", "if __name__ == \"__main__\":\n", " run()\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }