Esempio n. 1
0
 def test_client_passed_to_objects(self, mock_user_agent):
     client = NetworkClient(user_agent=mock_user_agent)
     combo = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2020, 12, 31),
                          client=client)
     assert combo.quarterly.client == client
     assert combo.daily.client == client
Esempio n. 2
0
 def test_combo_daily_only_multiple_days(self, mock_user_agent):
     combo = ComboFilings(start_date=date(2020, 12, 10),
                          end_date=date(2020, 12, 12),
                          user_agent=mock_user_agent)
     expected = ["2020-12-10", "2020-12-11", "2020-12-12"]
     assert [str(s) for s in combo.daily_date_list] == expected
     assert len(combo.quarterly_date_list) == 0
Esempio n. 3
0
 def test_combo_quarterly_only_one_year(self):
     combo = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2020, 12, 31))
     expected = [(2020, 1, lambda _: True), (2020, 2, lambda _: True),
                 (2020, 3, lambda _: True), (2020, 4, lambda _: True)]
     assert quarterly_list_matches(combo.quarterly_date_list, expected)
     assert len(combo.daily_date_list) == 0
Esempio n. 4
0
 def test_combo_daily_quarterly_mixed(self, start_date, end_date,
                                      quarterly_expected, daily_expected,
                                      mock_user_agent):
     combo = ComboFilings(start_date=start_date,
                          end_date=end_date,
                          user_agent=mock_user_agent)
     assert [str(s) for s in combo.daily_date_list] == daily_expected
     assert quarterly_list_matches(combo.quarterly_date_list,
                                   quarterly_expected)
Esempio n. 5
0
    def test_properties_on_init(self, mock_user_agent):
        start = date(2020, 1, 1)
        end = date(2020, 5, 30)
        bp = 25
        combo = ComboFilings(start_date=start,
                             end_date=end,
                             user_agent=mock_user_agent,
                             balancing_point=bp)

        assert combo.start_date == start
        assert combo.end_date == end
        assert combo.balancing_point == bp
        assert combo.client.user_agent == mock_user_agent
Esempio n. 6
0
 def test_combo_quarterly_only_multiple_years(self):
     combo = ComboFilings(start_date=date(2018, 10, 1),
                          end_date=date(2020, 6, 30))
     expected = [
         (2018, 4, lambda _: True),
         (2019, 1, lambda _: True),
         (2019, 2, lambda _: True),
         (2019, 3, lambda _: True),
         (2019, 4, lambda _: True),
         (2020, 1, lambda _: True),
         (2020, 2, lambda _: True),
     ]
     assert quarterly_list_matches(combo.quarterly_date_list, expected)
     assert len(combo.daily_date_list) == 0
Esempio n. 7
0
def filings(
    cik_lookup=None,
    filing_type=None,
    start_date=None,
    end_date=date.today(),
    count=None,
    client=None,
    entry_filter=lambda _: True,
):
    """Utility method to get best filing object.

    Args:
        cik_lookup (str): Central Index Key (CIK) for company of interest.
        start_date (datetime.date, optional): Date of daily filing to fetch.
        end_date (datetime.date, optional): Date of daily filing to fetch.
        filing_type (secedgar.core.filing_types.FilingType, optional): Valid filing type
            enum. Defaults to None. If None, then all filing types for CIKs will be returned.
        count (int, optional): Number of filings to fetch. Will fetch up to `count` if that
        many filings are available. Defaults to all filings available.
        client (secedgar.client._base, optional): Client to use. Defaults to
                    ``secedgar.client.NetworkClient`` if None given.
        entry_filter (function, optional): A boolean function to determine
            if the FilingEntry should be kept. Defaults to ``lambda _: True``.
            See :class:`secedgar.core.DailyFilings` for more detail.
    .. code-block:: python

        from datetime import date
        from secedgar.core import filings, FilingType

        engine = filings(start_date=date(2020, 12, 10), end_date=date(2020, 12, 10),
            filing_type=FilingType.FILING_4, count=50)
    """
    if filing_type is not None and not isinstance(filing_type, FilingType):
        raise FilingTypeError

    if cik_lookup:
        return CompanyFilings(
            cik_lookup,
            filing_type=filing_type,
            start_date=start_date,
            end_date=end_date,
            count=count,
            client=client,
        )

    if filing_type is not None:
        original_entry_filter = entry_filter

        def entry_filter(x):
            return x.form_type == filing_type and original_entry_filter(x)

        original_entry_filter = entry_filter

    if count is not None:
        raise NotImplementedError(
            "Count has not yet been implemented for Daily, quarterly & Combo Filings."
        )

    if (end_date is None or end_date == start_date) and isinstance(
            start_date, date):
        return DailyFilings(date=start_date,
                            client=client,
                            entry_filter=entry_filter)

    if isinstance(start_date, date) and isinstance(end_date, date):
        current_quarter = get_quarter(start_date)
        current_year = start_date.year
        start_quarter_date = date(current_year, get_month(current_quarter), 1)
        next_year, next_quarter = add_quarter(current_year, current_quarter)
        end_quarter_date = date(next_year, get_month(next_quarter),
                                1) - timedelta(days=1)
        if start_quarter_date == start_date and end_date == end_quarter_date:
            return QuarterlyFilings(current_year,
                                    current_quarter,
                                    client=client,
                                    entry_filter=entry_filter)
        return ComboFilings(start_date,
                            end_date,
                            client=client,
                            entry_filter=entry_filter)

    raise ValueError(
        """Invalid arguments. You must provide 'cik_lookup' OR 'start_date' \
OR ('start_date' and 'end_date').""")
Esempio n. 8
0
 def test_combo_daily_only_single_day(self, mock_user_agent):
     combo = ComboFilings(start_date=date(2020, 12, 10),
                          end_date=date(2020, 12, 10),
                          user_agent=mock_user_agent)
     assert [str(s) for s in combo.daily_date_list] == ["2020-12-10"]
     assert len(combo.quarterly_date_list) == 0
Esempio n. 9
0
 def test_user_agent_client_none(self):
     with pytest.raises(TypeError):
         _ = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2020, 12, 31),
                          user_agent=None,
                          client=None)
Esempio n. 10
0
 def test_balancing_point_read_only(self, mock_user_agent):
     combo = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2020, 1, 3),
                          user_agent=mock_user_agent)
     with pytest.raises(AttributeError):
         combo.balancing_point = 20
Esempio n. 11
0
 def test_good_entry_filter(self, good_entry_filter, mock_user_agent):
     combo = ComboFilings(date(2020, 1, 1),
                          date(2020, 5, 30),
                          entry_filter=good_entry_filter,
                          user_agent=mock_user_agent)
     assert combo.entry_filter == good_entry_filter
Esempio n. 12
0
 def test_bad_entry_filter(self, bad_entry_filter):
     with pytest.raises(ValueError):
         _ = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2020, 5, 30),
                          entry_filter=bad_entry_filter)
Esempio n. 13
0
 def test_user_agent_passed_to_client(self, mock_user_agent):
     combo = ComboFilings(start_date=date(2020, 1, 1),
                          end_date=date(2021, 1, 1),
                          user_agent=mock_user_agent)
     assert combo.quarterly.client.user_agent == mock_user_agent
     assert combo.daily.client.user_agent == mock_user_agent
Esempio n. 14
0
def filings(cik_lookup=None,
            filing_type=None,
            user_agent=None,
            start_date=None,
            end_date=date.today(),
            count=None,
            client=None,
            entry_filter=lambda _: True,
            **kwargs):
    """Utility method to get best filing object.

    Args:
        cik_lookup (str): Central Index Key (CIK) for company of interest.
        start_date (datetime.date, optional): Date of daily filing to fetch.
        end_date (datetime.date, optional): Date of daily filing to fetch.
        filing_type (secedgar.core.filing_types.FilingType, optional): Valid filing type
            enum. Defaults to None. If None, then all filing types for CIKs will be returned.
        count (int, optional): Number of filings to fetch. Will fetch up to `count` if that
        many filings are available. Defaults to all filings available.
        client (secedgar.client.NetworkClient, optional): Client to use. Defaults to
                    ``secedgar.client.NetworkClient`` if None given.
        entry_filter (function, optional): A boolean function to determine
            if the FilingEntry should be kept. Defaults to ``lambda _: True``.
            See :class:`secedgar.core.DailyFilings` for more detail.
        kwargs: Any keyword arguments to pass to ``NetworkClient`` if no client is specified.

    Examples:
        Using the ``filings`` function from secedgar is the easiest way to retrieve filings.

        Depending on the arguments given, secedgar will return an object that will get you
        the information you want from EDGAR.

        There are 4 main classes which can be returned.

            - :class:`secedgar.ComboFilings` for fetching filings over multiple days
              that does not fall exactly into a quarter
            - :class:`secedgar.CompanyFilings` for fetching a particular
              filing type for one or more companies
            - :class:`secedgar.DailyFilings` for fetching all filings
              from a specific date
            - :class:`secedgar.QuarterlyFilings` for fetching all filings
              from a specific quarter

        To get all filings over a time span, you could use something like below.

        .. code-block:: python

            from datetime import date
            from secedgar import filings, FilingType

            # secedgar creates correct filing object for given arguments
            # this will fetch the first 50 filings found over the time span
            my_filings = filings(start_date=date(2020, 12, 10),
                                 end_date=date(2020, 12, 15),
                                 filing_type=FilingType.FILING_4,
                                 user_agent="Name (email)",
                                 count=50)

            # easy access to methods shared across all 4 different filing classes
            my_filings_urls = my_filings.get_urls()
            my_filings.save("/path/to/directory")

        To get a single filing type for one or more companies, you could use this:

        .. code-block:: python

            from secedgar import filings, FilingType

            # similar to above, but fetches filings for specific tickers
            company_filings = filings(cik_lookup=["aapl", "fb"],
                                      filing_type=sec.FilingType.FILING_10Q,
                                      user_agent="Name (email)")
            company_filings_urls = company_filings.get_urls()
            company_filings.save("/path/to/directory")

        To get filings for a single day, you could use something like this:

        .. code-block:: python

            from datetime import date
            from secedgar import filings

            # all filings for
            daily_filings = filings(start_date=date(2020, 1 ,3),
                                    end_date=date(2020, 1, 3),
                                    user_agent="Name (email)")
            daily_filings.save("/path/to/directory")

            # limit which quarterly filings to use - saves only form 4 filings
            limit_to_form4 = lambda f: f.form_type.lower() == "4"
            daily_filings_limited = filings(start_date=date(2020, 1 ,3),
                                            end_date=date(2020, 1, 3),
                                            user_agent="Name (email)",
                                            entry_filter=limit_to_form4)
            daily_filings_limited.save("/path/to/other/directory")


        For getting filings from a specific quarter, the function call would look like this:


        .. code-block:: python

            from datetime import date
            from secedgar import filings

            # all quarterly filings
            quarterly_filings = filings(start_date=date(2020, 1 ,1),
                                        end_date=date(2020, 3, 31),
                                        user_agent="Name (email)")
            quarterly_filings.save("/path/to/directory")

            # limit which quarterly filings to use
            # saves only 10-K and 10-Q filings from quarter
            limit_to_10k_10q = lambda f: f.form_type.lower() in ("10-k", "10-q")
            quarterly_filings_limited = filings(start_date=date(2020, 1 ,1),
                                                end_date=date(2020, 3, 31),
                                                user_agent="Name (email)",
                                                entry_filter=limit_to_10k_10q)
            quarterly_filings_limited.save("/path/to/other/directory")

    """
    if filing_type is not None and not isinstance(filing_type, FilingType):
        raise FilingTypeError

    if cik_lookup:
        return CompanyFilings(cik_lookup,
                              filing_type=filing_type,
                              user_agent=user_agent,
                              start_date=start_date,
                              end_date=end_date,
                              count=count,
                              client=client,
                              **kwargs)
    # Define entry filter as original
    _entry_filter = entry_filter

    if filing_type is not None:
        # If filing type also given, add filing types to existing entry filter
        def _entry_filter(x):
            return x.form_type == filing_type and entry_filter(x)

    if count is not None:
        raise NotImplementedError(
            "Count has not yet been implemented for Daily, quarterly & Combo Filings."
        )

    if (end_date is None or end_date == start_date) and isinstance(
            start_date, date):
        return DailyFilings(date=start_date,
                            user_agent=user_agent,
                            client=client,
                            entry_filter=_entry_filter,
                            **kwargs)

    if isinstance(start_date, date) and isinstance(end_date, date):
        current_quarter = get_quarter(start_date)
        current_year = start_date.year
        start_quarter_date = date(current_year, get_month(current_quarter), 1)
        next_year, next_quarter = add_quarter(current_year, current_quarter)
        end_quarter_date = date(next_year, get_month(next_quarter),
                                1) - timedelta(days=1)
        if start_quarter_date == start_date and end_date == end_quarter_date:
            return QuarterlyFilings(year=current_year,
                                    quarter=current_quarter,
                                    client=client,
                                    user_agent=user_agent,
                                    entry_filter=_entry_filter,
                                    **kwargs)
        return ComboFilings(start_date=start_date,
                            end_date=end_date,
                            user_agent=user_agent,
                            client=client,
                            entry_filter=_entry_filter,
                            **kwargs)

    raise ValueError(
        """Invalid arguments. You must provide 'cik_lookup' OR 'start_date' \
OR ('start_date' and 'end_date').""")