Skip to content

Commit

Permalink
feat: update get_option_contracts (#418)
Browse files Browse the repository at this point in the history
  • Loading branch information
hiohiohio committed Mar 12, 2024
1 parent 05a518a commit d408520
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 25 deletions.
7 changes: 5 additions & 2 deletions alpaca/trading/client.py
Expand Up @@ -707,11 +707,14 @@ def get_option_contracts(
"""
if request is None:
raise ValueError("request (GetOptionContractsRequest) is required")
if request.underlying_symbol == "":
raise ValueError("underlying_symbol is required")

params = request.to_request_fields()

if "underlying_symbols" in params and isinstance(
request.underlying_symbols, list
):
params["underlying_symbols"] = ",".join(request.underlying_symbols)

response = self.get("/options/contracts", params)

if self._use_raw_data:
Expand Down
6 changes: 2 additions & 4 deletions alpaca/trading/models.py
Expand Up @@ -667,10 +667,8 @@ class OptionContractsResponse(BaseModel):
Attributes:
option_contracts (Optional[List[OptionContract]]): The list of option contracts.
limit (int): The maximum number of option contracts in the response.
page (int): The page number of the response.
next_page_token (Optional[str]): Pagination token for next page.
"""

option_contracts: Optional[List[OptionContract]] = None
limit: int
page: int
next_page_token: Optional[str] = None
11 changes: 5 additions & 6 deletions alpaca/trading/requests.py
Expand Up @@ -466,7 +466,7 @@ class GetOptionContractsRequest(NonEmptyRequest):
Used to fetch option contracts for a given underlying symbol.
Attributes:
underlying_symbol (str): The underlying symbol for the option contracts to be returned.
underlying_symbols (Optional[List[str]]): The underlying symbols for the option contracts to be returned. (e.g. ["AAPL", "SPY"])
status (Optional[AssetStatus]): The status of the asset.
expiration_date (Optional[Union[date, str]]): The expiration date of the option contract. (YYYY-MM-DD)
expiration_date_gte (Optional[Union[date, str]]): The expiration date of the option contract greater than or equal to. (YYYY-MM-DD)
Expand All @@ -476,12 +476,11 @@ class GetOptionContractsRequest(NonEmptyRequest):
style (Optional[ExerciseStyle]): The option contract style.
strike_price_gte (Optional[str]): The option contract strike price greater than or equal to.
strike_price_lte (Optional[str]): The option contract strike price less than or equal to.
limit (Optional[int]): The maximum number of entries to return in the response.
page (Optional[int]): The page number for the results to return.
limit (Optional[int]): The number of contracts to limit per page (default=100, max=10000).
page_token (Optional[str]): Pagination token to continue from. The value to pass here is returned in specific requests when more data is available than the request limit allows.
"""

underlying_symbol: str

underlying_symbols: Optional[List[str]] = None
status: Optional[AssetStatus] = AssetStatus.ACTIVE
expiration_date: Optional[Union[date, str]] = None
expiration_date_gte: Optional[Union[date, str]] = None
Expand All @@ -493,4 +492,4 @@ class GetOptionContractsRequest(NonEmptyRequest):
strike_price_lte: Optional[str] = None

limit: Optional[int] = None
page: Optional[int] = None
page_token: Optional[str] = None
45 changes: 36 additions & 9 deletions examples/options-trading-basic.ipynb
Expand Up @@ -175,12 +175,13 @@
"metadata": {},
"outputs": [],
"source": [
"# get list of options contracts for the given underlying symbol (e.g. SPY)\n",
"# get list of options contracts for the given underlying symbol (e.g. SPY,AAPL)\n",
"# - get_option_contracts() is a new method to get list of options contracts\n",
"# - in this example, we get 2 options contracts for SPY\n",
"underlying_symbol = \"SPY\"\n",
"# - in this example, we get 2 options contracts for SPY,AAPL\n",
"# - you can continue to fetch options contracts by specifying page_token from next_page_token of response\n",
"underlying_symbols = [\"SPY\", \"AAPL\"]\n",
"req = GetOptionContractsRequest(\n",
" underlying_symbol=underlying_symbol, # specify underlying symbol\n",
" underlying_symbols=underlying_symbols, # specify underlying symbols\n",
" status=AssetStatus.ACTIVE, # specify asset status: active (default)\n",
" expiration_date=None, # specify expiration date (specified date + 1 day range)\n",
" expiration_date_gte=None, # we can pass date object\n",
Expand All @@ -191,10 +192,36 @@
" strike_price_gte=None, # specify strike price range\n",
" strike_price_lte=None, # specify strike price range\n",
" limit=2, # specify limit\n",
" page=None, # specify page\n",
" page_token=None, # specify page token\n",
")\n",
"res = trade_client.get_option_contracts(req)\n",
"res.option_contracts"
"res"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# continue to fetch option contracts if there is next_page_token in response\n",
"if res.next_page_token is not None:\n",
" req = GetOptionContractsRequest(\n",
" underlying_symbols=underlying_symbols, # specify underlying symbols\n",
" status=AssetStatus.ACTIVE, # specify asset status: active (default)\n",
" expiration_date=None, # specify expiration date (specified date + 1 day range)\n",
" expiration_date_gte=None, # we can pass date object\n",
" expiration_date_lte=None, # or string (YYYY-MM-DD)\n",
" root_symbol=None, # specify root symbol\n",
" type=None, # specify option type (ContractType.CALL or ContractType.PUT)\n",
" style=None, # specify option style (ContractStyle.AMERICAN or ContractStyle.EUROPEAN)\n",
" strike_price_gte=None, # specify strike price range\n",
" strike_price_lte=None, # specify strike price range\n",
" limit=2, # specify limit\n",
" page_token=res.next_page_token, # specify page token\n",
" )\n",
" res = trade_client.get_option_contracts(req)\n",
" display(res)"
]
},
{
Expand Down Expand Up @@ -229,15 +256,15 @@
"outputs": [],
"source": [
"# get put options contracts\n",
"underlying_symbol = \"SPY\"\n",
"underlying_symbols = [\"SPY\"]\n",
"\n",
"# specify expiration date range\n",
"now = datetime.now(tz=ZoneInfo(\"America/New_York\"))\n",
"day1 = now + timedelta(days=1)\n",
"day60 = now + timedelta(days=60)\n",
"\n",
"req = GetOptionContractsRequest(\n",
" underlying_symbol=underlying_symbol, # specify underlying symbol\n",
" underlying_symbols=underlying_symbols, # specify underlying symbols\n",
" status=AssetStatus.ACTIVE, # specify asset status: active (default)\n",
" expiration_date=None, # specify expiration date (specified date + 1 day range)\n",
" expiration_date_gte=day1.date(), # we can pass date object\n",
Expand All @@ -248,7 +275,7 @@
" strike_price_gte=None, # specify strike price range\n",
" strike_price_lte=None, # specify strike price range\n",
" limit=100, # specify limit\n",
" page=None, # specify page\n",
" page_token=None, # specify page\n",
")\n",
"res = trade_client.get_option_contracts(req)\n",
"res.option_contracts[:2]"
Expand Down
54 changes: 50 additions & 4 deletions tests/trading/trading_client/test_option_routes.py
Expand Up @@ -10,7 +10,7 @@

def test_get_option_contracts(reqmock, trading_client: TradingClient):
reqmock.get(
f"{BaseURL.TRADING_PAPER.value}/v2/options/contracts?underlying_symbol=AAPL",
f"{BaseURL.TRADING_PAPER.value}/v2/options/contracts?underlying_symbols=AAPL",
text="""
{
"option_contracts": [
Expand All @@ -34,18 +34,64 @@ def test_get_option_contracts(reqmock, trading_client: TradingClient):
"close_price_date": null
}
],
"page": 1,
"limit": 5
"next_page_token": null
}
""",
)

res = trading_client.get_option_contracts(
GetOptionContractsRequest(underlying_symbol="AAPL")
GetOptionContractsRequest(underlying_symbols=["AAPL"])
)

assert reqmock.called_once
assert isinstance(res, OptionContractsResponse)
assert res.next_page_token is None
assert isinstance(res.option_contracts, list)
assert len(res.option_contracts) == 1
assert isinstance(res.option_contracts[0], OptionContract)


def test_get_option_contracts_with_multiple_symbols(
reqmock, trading_client: TradingClient
):
reqmock.get(
f"{BaseURL.TRADING_PAPER.value}/v2/options/contracts?underlying_symbols=AAPL,SPY",
text="""
{
"option_contracts": [
{
"id": "00000000-0000-0000-0000-000000000000",
"symbol": "AAPL231103C00170000",
"name": "AAPL Nov 03 2023 170 Call",
"status": "active",
"tradable": true,
"expiration_date": "2023-11-03",
"root_symbol": "AAPL",
"underlying_symbol": "AAPL",
"underlying_asset_id": "00000000-0000-0000-0000-000000000000",
"type": "call",
"style": "american",
"strike_price": "170",
"size": "100",
"open_interest": "0",
"open_interest_date": "2023-11-03",
"close_price": null,
"close_price_date": null
}
],
"next_page_token": "MA=="
}
""",
)

res = trading_client.get_option_contracts(
GetOptionContractsRequest(underlying_symbols=["AAPL", "SPY"])
)

assert reqmock.called_once
assert isinstance(res, OptionContractsResponse)
assert res.next_page_token == "MA=="
assert isinstance(res.option_contracts, list)
assert len(res.option_contracts) == 1
assert isinstance(res.option_contracts[0], OptionContract)

Expand Down

0 comments on commit d408520

Please sign in to comment.