diff --git a/docs/source/Contracts.rst b/docs/source/Contracts.rst new file mode 100644 index 00000000..9278168f --- /dev/null +++ b/docs/source/Contracts.rst @@ -0,0 +1,24 @@ +.. _contracts_header: + +Contracts +================================= + +================================= +Get option contract +================================= + +- `Options Contract`_ + +.. automethod:: polygon.RESTClient.get_options_contract + +================================= +List Options Contracts +================================= + +- `Options Contracts`_ + +.. automethod:: polygon.RESTClient.list_options_contracts + + +.. _Options Contract: https://polygon.io/docs/options/get_v3_reference_options_contracts__options_ticker +.. _Options Contracts: https://polygon.io/docs/options/get_v3_reference_options_contracts \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index f3925eb1..b445b803 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -19,6 +19,7 @@ This documentation is for the Python client only. For details about the response Enums WebSocket-Enums Exceptions + Contracts Indices and tables ================== diff --git a/polygon/rest/__init__.py b/polygon/rest/__init__.py index 5607cc46..5a2e4a3c 100644 --- a/polygon/rest/__init__.py +++ b/polygon/rest/__init__.py @@ -9,6 +9,7 @@ DividendsClient, ConditionsClient, ExchangesClient, + ContractsClient, ) from .vX import VXClient from typing import Optional @@ -30,6 +31,7 @@ class RESTClient( DividendsClient, ConditionsClient, ExchangesClient, + ContractsClient, ): def __init__( self, diff --git a/polygon/rest/models/__init__.py b/polygon/rest/models/__init__.py index ce0103be..8fca108c 100644 --- a/polygon/rest/models/__init__.py +++ b/polygon/rest/models/__init__.py @@ -10,3 +10,4 @@ from .exchanges import * from .snapshot import * from .financials import * +from .contracts import * diff --git a/polygon/rest/models/contracts.py b/polygon/rest/models/contracts.py new file mode 100644 index 00000000..d618f5b1 --- /dev/null +++ b/polygon/rest/models/contracts.py @@ -0,0 +1,47 @@ +from typing import Optional, List +from ...modelclass import modelclass + + +@modelclass +class Underlying: + "Underlying contains data for an underlying or deliverable associated with an option contract." + amount: Optional[float] = None + type: Optional[str] = None + underlying: Optional[str] = None + + @staticmethod + def from_dict(d): + return Underlying(**d) + + +@modelclass +class OptionsContract: + "OptionsContract contains data for a specified ticker symbol." + additional_underlyings: Optional[List[Underlying]] = None + cfi: Optional[str] = None + correction: Optional[str] = None + exercise_style: Optional[str] = None + expiration_date: Optional[str] = None + primary_exchange: Optional[str] = None + shares_per_contract: Optional[float] = None + strike_price: Optional[float] = None + ticker: Optional[str] = None + underlying_ticker: Optional[str] = None + + @staticmethod + def from_dict(d): + return OptionsContract( + additional_underlyings=None + if "additional_underlyings" not in d + else Underlying.from_dict(d["additional_underlyings"]), + cfi=d.get("cfi", None), + correction=d.get("correction", None), + exercise_style=d.get("exercise_style", None), + expiration_date=d.get("expiration_date", None), + primary_exchange=d.get("primary_exchange", None), + shares_per_contract=d.get("shares_per_contract", None), + strike_price=d.get("strike_price", None), + size=d.get("size", None), + ticker=d.get("ticker", None), + underlying_ticker=d.get("underlying_ticker", None), + ) diff --git a/polygon/rest/reference.py b/polygon/rest/reference.py index 75f186a1..0b5bf0d5 100644 --- a/polygon/rest/reference.py +++ b/polygon/rest/reference.py @@ -19,6 +19,7 @@ DataType, SIP, Exchange, + OptionsContract, ) from urllib3 import HTTPResponse from datetime import date @@ -31,9 +32,9 @@ def get_market_holidays( """ Get upcoming market holidays and their open/close times. - :param params: Any additional query params - :param raw: Return HTTPResponse object instead of results object - :return: List of market holidays + :param params: Any additional query params. + :param raw: Return HTTPResponse object instead of results object. + :return: List of market holidays. """ url = "/v1/marketstatus/upcoming" @@ -51,9 +52,9 @@ def get_market_status( """ Get the current trading status of the exchanges and overall financial markets. - :param params: Any additional query params - :param raw: Return HTTPResponse object instead of results object - :return: Market status + :param params: Any additional query params. + :param raw: Return HTTPResponse object instead of results object. + :return: Market status. """ url = "/v1/marketstatus/now" @@ -88,10 +89,10 @@ def list_tickers( Query all ticker symbols which are supported by Polygon.io. This API currently includes Stocks/Equities, Crypto, and Forex. :param ticker: Specify a ticker symbol. Defaults to empty string which queries all tickers. - :param ticker_lt: Ticker less than - :param ticker_lte: Ticker less than or equal to - :param ticker_gt: Ticker greater than - :param ticker_gte: Ticker greater than or equal to + :param ticker_lt: Ticker less than. + :param ticker_lte: Ticker less than or equal to. + :param ticker_gt: Ticker greater than. + :param ticker_gte: Ticker greater than or equal to. :param type: Specify the type of the tickers. Find the types that we support via our Ticker Types API. Defaults to empty string which queries all types. :param market: Filter by market type. By default all markets are included. :param exchange: Specify the primary exchange of the asset in the ISO code format. Find more information about the ISO codes at the ISO org website. Defaults to empty string which queries all exchanges. @@ -103,9 +104,9 @@ def list_tickers( :param limit: Limit the size of the response, default is 100 and max is 1000. :param sort: The field to sort the results on. Default is ticker. If the search query parameter is present, sort is ignored and results are ordered by relevance. :param order: The order to sort the results on. Default is asc (ascending). - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: List of tickers + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: List of tickers. """ url = "/v3/reference/tickers" @@ -168,9 +169,9 @@ def list_ticker_news( :param limit: Limit the number of results returned, default is 10 and max is 1000. :param sort: Sort field used for ordering. :param order: Order results based on the sort field. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: Ticker News + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: Ticker News. """ url = "/v2/reference/news" @@ -193,9 +194,9 @@ def get_ticker_types( :param asset_class: Filter by asset class. :param locale: Filter by locale. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: Ticker Types + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: Ticker Types. """ url = "/v3/reference/tickers/types" @@ -232,22 +233,22 @@ def list_splits( Get a list of historical stock splits, including the ticker symbol, the execution date, and the factors of the split ratio. :param ticker: Return the stock splits that contain this ticker. - :param ticker_lt: Ticker less than - :param ticker_lte: Ticker less than or equal to - :param ticker_gt: Ticker greater than - :param ticker_gte: Ticker greater than or equal to + :param ticker_lt: Ticker less than. + :param ticker_lte: Ticker less than or equal to. + :param ticker_gt: Ticker greater than. + :param ticker_gte: Ticker greater than or equal to. :param execution_date: Query by execution date with the format YYYY-MM-DD. - :param execution_date_lt: Execution date less than - :param execution_date_lte: Execution date less than or equal to - :param execution_date_gt: Execution date greater than - :param execution_date_gte: Execution date greater than or equal to + :param execution_date_lt: Execution date less than. + :param execution_date_lte: Execution date less than or equal to. + :param execution_date_gt: Execution date greater than. + :param execution_date_gte: Execution date greater than or equal to. :param reverse_split: Query for reverse stock splits. A split ratio where split_from is greater than split_to represents a reverse split. By default this filter is not used. :param limit: Limit the number of results returned, default is 10 and max is 1000. :param sort: Sort field used for ordering. :param order: Order results based on the sort field. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: List of splits + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: List of splits. """ url = "/v3/reference/splits" @@ -304,39 +305,39 @@ def list_dividends( Get a list of historical cash dividends, including the ticker symbol, declaration date, ex-dividend date, record date, pay date, frequency, and amount. :param ticker: Return the dividends that contain this ticker. - :param ticker_lt: Ticker less than - :param ticker_lte: Ticker less than or equal to - :param ticker_gt: Ticker greater than - :param ticker_gte: Ticker greater than or equal to + :param ticker_lt: Ticker less than. + :param ticker_lte: Ticker less than or equal to. + :param ticker_gt: Ticker greater than. + :param ticker_gte: Ticker greater than or equal to. :param ex_dividend_date: Query by ex-dividend date with the format YYYY-MM-DD. - :param ex_dividend_date_lt: Ex-dividend date less than - :param ex_dividend_date_lte: Ex-dividend date less than or equal to - :param ex_dividend_date_gt: Ex-dividend date greater than - :param ex_dividend_date_gte: Ex-dividend date greater than or equal to + :param ex_dividend_date_lt: Ex-dividend date less than. + :param ex_dividend_date_lte: Ex-dividend date less than or equal to. + :param ex_dividend_date_gt: Ex-dividend date greater than. + :param ex_dividend_date_gte: Ex-dividend date greater than or equal to. :param record_date: Query by record date with the format YYYY-MM-DD. - :param record_date_lt: Record date less than - :param record_date_lte: Record date less than or equal to - :param record_date_gt: Record date greater than - :param record_date_gte: Record date greater than or equal to + :param record_date_lt: Record date less than. + :param record_date_lte: Record date less than or equal to. + :param record_date_gt: Record date greater than. + :param record_date_gte: Record date greater than or equal to. :param declaration_date: Query by declaration date with the format YYYY-MM-DD. - :param declaration_date_lt: Declaration date less than - :param declaration_date_lte: Declaration date less than or equal to - :param declaration_date_gt: Declaration date greater than - :param declaration_date_gte: Declaration date greater than or equal to + :param declaration_date_lt: Declaration date less than. + :param declaration_date_lte: Declaration date less than or equal to. + :param declaration_date_gt: Declaration date greater than. + :param declaration_date_gte: Declaration date greater than or equal to. :param pay_date: Query by pay date with the format YYYY-MM-DD. - :param pay_date_lt: Pay date less than - :param pay_date_lte: Pay date less than or equal to - :param pay_date_gt: Pay date greater than - :param pay_date_gte: Pay date greater than or equal to + :param pay_date_lt: Pay date less than. + :param pay_date_lte: Pay date less than or equal to. + :param pay_date_gt: Pay date greater than. + :param pay_date_gte: Pay date greater than or equal to. :param frequency: Query by the number of times per year the dividend is paid out. Possible values are 0 (one-time), 1 (annually), 2 (bi-annually), 4 (quarterly), and 12 (monthly). :param cash_amount: Query by the cash amount of the dividend. :param dividend_type: Query by the type of dividend. Dividends that have been paid and/or are expected to be paid on consistent schedules are denoted as CD. Special Cash dividends that have been paid that are infrequent or unusual, and/or can not be expected to occur in the future are denoted as SC. :param limit: Limit the number of results returned, default is 10 and max is 1000. :param sort: Sort field used for ordering. :param order: Order results based on the sort field. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: List of dividends + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: List of dividends. """ url = "/v3/reference/dividends" @@ -371,9 +372,9 @@ def list_conditions( :param limit: Limit the number of results returned, default is 10 and max is 1000. :param sort: Sort field used for ordering. :param order: Order results based on the sort field. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: List of conditions + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: List of conditions. """ url = "/v3/reference/conditions" @@ -398,9 +399,9 @@ def get_exchanges( :param asset_class: Filter by asset class. :param locale: Filter by locale. - :param params: Any additional query params - :param raw: Return HTTPResponse object instead of results object - :return: List of exchanges + :param params: Any additional query params. + :param raw: Return HTTPResponse object instead of results object. + :return: List of exchanges. """ url = "/v3/reference/exchanges" @@ -411,3 +412,82 @@ def get_exchanges( raw=raw, result_key="results", ) + + +class ContractsClient(BaseClient): + def get_options_contract( + self, + ticker: str, + as_of: Union[str, date] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + ) -> Union[OptionsContract, HTTPResponse]: + """ + Get the most recent trade for a ticker. + + :param ticker: The ticker symbol of the asset + :param as_of: Specify a point in time for the contract as of this date with format YYYY-MM-DD. + :param params: Any additional query params. + :param raw: Return raw object instead of results object. + :return: Last trade. + """ + url = f"/v3/reference/options/contracts/{ticker}" + + return self._get( + path=url, + params=self._get_params(self.get_options_contract, locals()), + result_key="results", + deserializer=OptionsContract.from_dict, + raw=raw, + ) + + def list_options_contracts( + self, + underlying_ticker: Optional[str] = None, + underlying_ticker_lt: Optional[str] = None, + underlying_ticker_lte: Optional[str] = None, + underlying_ticker_gt: Optional[str] = None, + underlying_ticker_gte: Optional[str] = None, + contract_type: Optional[str] = None, + expiration_date: Optional[Union[str, date]] = None, + expiration_date_lt: Optional[Union[str, date]] = None, + expiration_date_lte: Optional[Union[str, date]] = None, + expiration_date_gt: Optional[Union[str, date]] = None, + expiration_date_gte: Optional[Union[str, date]] = None, + as_of: Optional[Union[str, date]] = None, + strike_price: Optional[float] = None, + strike_price_lt: Optional[float] = None, + strike_price_lte: Optional[float] = None, + strike_price_gt: Optional[float] = None, + strike_price_gte: Optional[float] = None, + expired: Optional[bool] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + order: Optional[Union[str, Order]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + ) -> Union[Iterator[OptionsContract], HTTPResponse]: + """ + List historical options contracts. + + :param underlying_ticker: Query for contracts relating to an underlying stock ticker. + :param contract_type: Query by the type of contract. + :param expiration_date: Query by contract expiration with date format YYYY-MM-DD. + :param as_of: Specify a point in time for contracts as of this date with format YYYY-MM-DD. + :param strike_price: Query by strike price of a contract. + :param expired: Query for expired contracts. + :param limit: Limit the number of results returned, default is 10 and max is 1000. + :param sort: Sort field used for ordering. + :param order: Order results based on the sort field. + :param params: Any additional query params. + :param raw: Return raw object instead of results object + :return: List of options contracts. + """ + url = "/v3/reference/options/contracts" + + return self._paginate( + path=url, + params=self._get_params(self.list_options_contracts, locals()), + raw=raw, + deserializer=OptionsContract.from_dict, + ) diff --git a/test_rest/mocks/v3/reference/options/contracts&cursor=YXA9JTdCJTIySUQlMjIlM0ElMjIy.json b/test_rest/mocks/v3/reference/options/contracts&cursor=YXA9JTdCJTIySUQlMjIlM0ElMjIy.json new file mode 100644 index 00000000..4a041387 --- /dev/null +++ b/test_rest/mocks/v3/reference/options/contracts&cursor=YXA9JTdCJTIySUQlMjIlM0ElMjIy.json @@ -0,0 +1,116 @@ +{ + "results": [ + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 115, + "ticker": "O:A220520C00115000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 120, + "ticker": "O:A220520C00120000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 125, + "ticker": "O:A220520C00125000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 130, + "ticker": "O:A220520C00130000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 135, + "ticker": "O:A220520C00135000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 140, + "ticker": "O:A220520C00140000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 145, + "ticker": "O:A220520C00145000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 150, + "ticker": "O:A220520C00150000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 155, + "ticker": "O:A220520C00155000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 160, + "ticker": "O:A220520C00160000", + "underlying_ticker": "A" + } + ], + "status": "OK", + "request_id": "7ab36c77b9412ecc95275a94461be18f" +} \ No newline at end of file diff --git a/test_rest/mocks/v3/reference/options/contracts.json b/test_rest/mocks/v3/reference/options/contracts.json new file mode 100644 index 00000000..639c5cf5 --- /dev/null +++ b/test_rest/mocks/v3/reference/options/contracts.json @@ -0,0 +1,117 @@ +{ + "results": [ + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 65, + "ticker": "O:A220520C00065000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 70, + "ticker": "O:A220520C00070000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 75, + "ticker": "O:A220520C00075000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 80, + "ticker": "O:A220520C00080000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 85, + "ticker": "O:A220520C00085000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 90, + "ticker": "O:A220520C00090000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 95, + "ticker": "O:A220520C00095000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 100, + "ticker": "O:A220520C00100000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 105, + "ticker": "O:A220520C00105000", + "underlying_ticker": "A" + }, + { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2022-05-20", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 110, + "ticker": "O:A220520C00110000", + "underlying_ticker": "A" + } + ], + "status": "OK", + "request_id": "30ece14c74dcfc5ad4785b08fd21456d", + "next_url": "https://api.polygon.io/v3/reference/options/contracts?cursor=YXA9JTdCJTIySUQlMjIlM0ElMjIy" +} \ No newline at end of file diff --git a/test_rest/mocks/v3/reference/options/contracts/OEVRI240119C00002500.json b/test_rest/mocks/v3/reference/options/contracts/OEVRI240119C00002500.json new file mode 100644 index 00000000..5ceaa157 --- /dev/null +++ b/test_rest/mocks/v3/reference/options/contracts/OEVRI240119C00002500.json @@ -0,0 +1,15 @@ +{ + "results": { + "cfi": "OCASPS", + "contract_type": "call", + "exercise_style": "american", + "expiration_date": "2024-01-19", + "primary_exchange": "BATO", + "shares_per_contract": 100, + "strike_price": 2.5, + "ticker": "O:EVRI240119C00002500", + "underlying_ticker": "EVRI" + }, + "status": "OK", + "request_id": "c6a1dbcd2337b5887761d09bd4afdff8" +} \ No newline at end of file diff --git a/test_rest/test_contracts.py b/test_rest/test_contracts.py new file mode 100644 index 00000000..39ad7215 --- /dev/null +++ b/test_rest/test_contracts.py @@ -0,0 +1,287 @@ +from polygon.rest.models import OptionsContract +from base import BaseTest + + +class ContractsTest(BaseTest): + def test_get_options_contract(self): + contract = self.c.get_options_contract("OEVRI240119C00002500") + expected = OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2024-01-19", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=2.5, + size=None, + ticker="O:EVRI240119C00002500", + underlying_ticker="EVRI", + ) + self.assertEqual(contract, expected) + + def test_list_options_contracts(self): + contracts = [c for c in self.c.list_options_contracts()] + expected = [ + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=65, + size=None, + ticker="O:A220520C00065000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=70, + size=None, + ticker="O:A220520C00070000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=75, + size=None, + ticker="O:A220520C00075000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=80, + size=None, + ticker="O:A220520C00080000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=85, + size=None, + ticker="O:A220520C00085000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=90, + size=None, + ticker="O:A220520C00090000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=95, + size=None, + ticker="O:A220520C00095000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=100, + size=None, + ticker="O:A220520C00100000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=105, + size=None, + ticker="O:A220520C00105000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=110, + size=None, + ticker="O:A220520C00110000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=115, + size=None, + ticker="O:A220520C00115000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=120, + size=None, + ticker="O:A220520C00120000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=125, + size=None, + ticker="O:A220520C00125000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=130, + size=None, + ticker="O:A220520C00130000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=135, + size=None, + ticker="O:A220520C00135000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=140, + size=None, + ticker="O:A220520C00140000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=145, + size=None, + ticker="O:A220520C00145000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=150, + size=None, + ticker="O:A220520C00150000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=155, + size=None, + ticker="O:A220520C00155000", + underlying_ticker="A", + ), + OptionsContract( + additional_underlyings=None, + cfi="OCASPS", + correction=None, + exercise_style="american", + expiration_date="2022-05-20", + primary_exchange="BATO", + shares_per_contract=100, + strike_price=160, + size=None, + ticker="O:A220520C00160000", + underlying_ticker="A", + ), + ] + self.assertEqual(contracts, expected)