Skip to content

Commit

Permalink
v0.0.2: Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Elijas committed Oct 10, 2023
1 parent 298b984 commit 26b5219
Show file tree
Hide file tree
Showing 12 changed files with 652 additions and 156 deletions.
1 change: 1 addition & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SECAPIO_API_KEY=your-api-key-here
61 changes: 0 additions & 61 deletions nbs/00_core.ipynb

This file was deleted.

150 changes: 131 additions & 19 deletions nbs/index.ipynb
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"from sec_api_io.core import *"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
Expand All @@ -20,20 +11,23 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"This file will become your README and also the index of your documentation."
"## Install and Setup"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Install"
"Run in terminal:"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
Expand All @@ -43,52 +37,170 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## How to use"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Set API key"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"1. Make a copy of the `.env.template` file in the root directory of the project.\n",
"2. Rename the copied file to `.env`.\n",
"3. Open the `.env` file and locate the `SECAPIO_API_KEY` variable.\n",
"4. Fill in the value for the `SECAPIO_API_KEY` variable. \n",
" - You can obtain a free key from [sec-api.io](https://sec-api.io/). \n",
" - Note: The first 100 requests are free. \n",
"5. Save the `.env` file next to your notebook or script.\n",
"\n",
"> **Important Note:** Depending on your geographical location, you might need to use a VPN set to a United States location to access [sec-api.io](https://sec-api.io/) API."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's load the API key from .env file into the environment variable SECAPIO_API_KEY"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import os \n",
"from dotenv import load_dotenv\n",
"\n",
"if 'SECAPIO_API_KEY' not in os.environ:\n",
" assert load_dotenv()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Fill me in please! Don't forget code examples:"
"### Get latest 10-Q report by ticker"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
"'https://www.sec.gov/Archives/edgar/data/320193/000032019323000077/aapl-20230701.htm'"
]
},
"execution_count": null,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1+1"
"from sec_api_io.secapio_data_retriever import SecapioDataRetriever\n",
"\n",
"retriever = SecapioDataRetriever()\n",
"# retriever = SecapioDataRetriever(api_key=...) # If you don't want to use .env file\n",
"\n",
"metadata = retriever.retrieve_report_metadata('10-Q', latest_from_ticker='AAPL')\n",
"url = metadata[\"linkToFilingDetails\"]\n",
"\n",
"assert url.startswith('https://www.sec.gov/Archives/edgar/data/')\n",
"url"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Download 10-Q HTML split into sections"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": []
"source": [
"html = retriever.get_report_html('10-Q', url)\n",
"assert html"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n",
"<document-root-section comment=\"This tag was added...\n",
"<span style=\"color:#000000;font-family:'Helvetica'...\n"
]
}
],
"source": [
"for line in html.splitlines():\n",
" print(line[:50] + '...')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "python3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion sec_api_io/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.0.1"
__version__ = "0.0.2"
5 changes: 4 additions & 1 deletion sec_api_io/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
'doc_host': 'https://Elijas.github.io',
'git_url': 'https://github.com/Elijas/sec-api-io',
'lib_path': 'sec_api_io'},
'syms': {'sec_api_io.core': {'sec_api_io.core.foo': ('core.html#foo', 'sec_api_io/core.py')}}}
'syms': { 'sec_api_io.abstract_sec_data_retriever': {},
'sec_api_io.sec_edgar_enums': {},
'sec_api_io.sec_edgar_utils': {},
'sec_api_io.secapio_data_retriever': {}}}
76 changes: 76 additions & 0 deletions sec_api_io/abstract_sec_data_retriever.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from sec_api_io.sec_edgar_enums import DocumentType, SectionType
from sec_api_io.sec_edgar_utils import validate_sections

if TYPE_CHECKING:
from collections.abc import Iterable


class DocumentTypeNotSupportedError(ValueError):
pass


class AbstractSECDataRetriever(ABC):
SUPPORTED_DOCUMENT_TYPES: frozenset[DocumentType] = frozenset()

def __init__(self) -> None:
if self.SUPPORTED_DOCUMENT_TYPES is None:
msg = "SUPPORTED_DOCUMENT_TYPES must be set in subclass"
raise NotImplementedError( # pragma: no cover
msg,
)

def get_report_html(
self: AbstractSECDataRetriever,
doc_type: DocumentType | str,
url: str,
*,
sections: Iterable[SectionType | str] | None = None,
) -> str:
doc_type, sections = self._validate_and_convert(doc_type, sections)

# Using the Template Method Pattern here to ensure all necessary
# validations are performed before calling the actual implementation.
# Subclasses are expected to implement _get_html_from_url for the
# core functionality.
return self._get_report_html(
doc_type,
url=url,
sections=sections,
)

@abstractmethod
def _get_report_html(
self: AbstractSECDataRetriever,
doc_type: DocumentType,
url: str,
*,
sections: Iterable[SectionType] | None = None,
) -> str:
raise NotImplementedError # pragma: no cover

def _validate_and_convert(
self,
doc_type: DocumentType | str,
sections: Iterable[SectionType | str] | None = None,
) -> tuple[DocumentType, Iterable[SectionType] | None]:
new_doc_type = (
DocumentType.from_str(doc_type) if isinstance(doc_type, str) else doc_type
)
if new_doc_type not in self.SUPPORTED_DOCUMENT_TYPES:
msg = f"Document type {doc_type} not supported."
raise DocumentTypeNotSupportedError(msg)
new_sections = (
[
SectionType.from_str(section) if isinstance(section, str) else section
for section in sections
]
if sections
else None
)
validate_sections(new_doc_type, new_sections)
return new_doc_type, new_sections
7 changes: 0 additions & 7 deletions sec_api_io/core.py

This file was deleted.

Loading

0 comments on commit 26b5219

Please sign in to comment.