Skip to content

Commit

Permalink
Edge headers (#343)
Browse files Browse the repository at this point in the history
* removed formatting from docs and unwated comment dupe

* Add headers from options

* Added options parameter to rest api functions.

* handle headers from options

* edge-headers push

* Attempting to use with options

* created response options class

* handle headers from options

* merge headers

* parameter type cleanup and None value handling in request.

* attempting to revert conf.py changes

* added concat_method to baseclient and test for requestoptions

* refactored and introduced more optionals for stricter use

* added type hinting to builder method returns. removed optional from edge_headers method

* removed one example from ./example/launchpad and renamed function

* lint

* Update polygon/rest/base.py

remove redundancy.

* Update examples/launchpad/launchpad.py

Co-authored-by: jbonzo <8647805+jbonzo@users.noreply.github.com>
  • Loading branch information
antdjohns and jbonzo committed Dec 12, 2022
1 parent cce5e84 commit 97ac520
Show file tree
Hide file tree
Showing 15 changed files with 414 additions and 7 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Requires Python >= 3.8.
See the [Getting Started](https://polygon-api-client.readthedocs.io/en/latest/Getting-Started.html)
section in our docs or view the [examples](./examples) directory.

#### Launchpad Usage

Users of the Launchpad product will need to pass in certain headers in order to make API requests.
Example can be found [here](./examples/launchpad).

## Contributing

If you found a bug or have an idea for a new feature, please first discuss it with us by
Expand Down
49 changes: 49 additions & 0 deletions examples/launchpad/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Launchpad

Users of the Launchpad product will need to pass in certain headers in order to make API requests.

```python

# import RESTClient
from polygon import RESTClient
from polygon.rest.models.request import RequestOptionBuilder

# create client
c = RESTClient(api_key="API_KEY")

# create request options
options = RequestOptionBuilder().edge_headers(
edge_id="YOUR_EDGE_ID", # required
edge_ip_address="IP_ADDRESS", # required
)
# get response
res = c.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04", options=options)

# do something with response

```
Launchpad users can also provide the optional User Agent value describing their Edge User's origination request.

```python

# import RESTClient
from polygon import RESTClient
from polygon.rest.models.request import RequestOptionBuilder

# create client
c = RESTClient(api_key="API_KEY")

# create request options
options = RequestOptionBuilder().edge_headers(
edge_id="YOUR_EDGE_ID", # required
edge_ip_address="IP_ADDRESS" # required
).update_edge_header(
edge_user="EDGE_USER" # optional
)

# get response
res = c.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04", options=options)

# do something with response

```
31 changes: 31 additions & 0 deletions examples/launchpad/launchpad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from polygon import RESTClient
from polygon.rest.models.request import RequestOptionBuilder


def get_list_trades_launchpad():
client = RESTClient()

"""
set headers example:
options = RequestOptionBuilder()
.edge_headers(edge_id="EDGE_ID", edge_ip_address="EDGE_ID_ADDRESS", edge_user="EDGE_USER")
update headers example:
options = options.update_edge_header(edge_ip_address="NEW_IP")
"""
options = RequestOptionBuilder().edge_headers(
edge_id="EDGE_ID", edge_ip_address="EDGE_ID_ADDRESS", edge_user="EDGE_USER"
)

trades = []
for t in client.list_trades("AAA", "2022-04-04", limit=5, options=options):
trades.append(t)
print(trades)


def main():
get_list_trades_launchpad()


if __name__ == "__main__":
main()
9 changes: 8 additions & 1 deletion examples/rest/simple-get.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from polygon import RESTClient
from polygon.rest import models

client = RESTClient()

aggs = client.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04")
aggs = client.get_aggs(
"AAPL",
1,
"day",
"2022-04-04",
"2022-04-04",
)
print(aggs)
10 changes: 10 additions & 0 deletions polygon/rest/aggs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from urllib3 import HTTPResponse
from datetime import datetime, date

from .models.request import RequestOptionBuilder


class AggsClient(BaseClient):
def get_aggs(
Expand All @@ -19,6 +21,7 @@ def get_aggs(
limit: Optional[int] = None,
params: Optional[Dict[str, Any]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[List[Agg], HTTPResponse]:
"""
Get aggregate bars for a ticker over a given date range in custom time window sizes.
Expand Down Expand Up @@ -48,6 +51,7 @@ def get_aggs(
result_key="results",
deserializer=Agg.from_dict,
raw=raw,
options=options,
)

# TODO: next breaking change release move "market_type" to be 2nd mandatory
Expand All @@ -60,6 +64,7 @@ def get_grouped_daily_aggs(
raw: bool = False,
market_type: str = "stocks",
include_otc: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[List[GroupedDailyAgg], HTTPResponse]:
"""
Get the daily open, high, low, and close (OHLC) for the entire market.
Expand All @@ -78,6 +83,7 @@ def get_grouped_daily_aggs(
result_key="results",
deserializer=GroupedDailyAgg.from_dict,
raw=raw,
options=options,
)

def get_daily_open_close_agg(
Expand All @@ -87,6 +93,7 @@ def get_daily_open_close_agg(
adjusted: Optional[bool] = None,
params: Optional[Dict[str, Any]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[DailyOpenCloseAgg, HTTPResponse]:
"""
Get the open, close and afterhours prices of a stock symbol on a certain date.
Expand All @@ -105,6 +112,7 @@ def get_daily_open_close_agg(
params=self._get_params(self.get_daily_open_close_agg, locals()),
deserializer=DailyOpenCloseAgg.from_dict,
raw=raw,
options=options,
)

def get_previous_close_agg(
Expand All @@ -113,6 +121,7 @@ def get_previous_close_agg(
adjusted: Optional[bool] = None,
params: Optional[Dict[str, Any]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[PreviousCloseAgg, HTTPResponse]:
"""
Get the previous day's open, high, low, and close (OHLC) for the specified stock ticker.
Expand All @@ -131,4 +140,5 @@ def get_previous_close_agg(
result_key="results",
deserializer=PreviousCloseAgg.from_dict,
raw=raw,
options=options,
)
32 changes: 27 additions & 5 deletions polygon/rest/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Optional, Any, Dict
from datetime import datetime
import pkg_resources # part of setuptools
from .models.request import RequestOptionBuilder
from ..logging import get_logger
import logging
from ..exceptions import AuthError, BadResponse, NoResultsError
Expand Down Expand Up @@ -34,20 +35,24 @@ def __init__(
raise AuthError(
f"Must specify env var POLYGON_API_KEY or pass api_key in constructor"
)

self.API_KEY = api_key
self.BASE = base

self.headers = {
"Authorization": "Bearer " + self.API_KEY,
"User-Agent": f"Polygon.io PythonClient/{version}",
}

# https://urllib3.readthedocs.io/en/stable/reference/urllib3.poolmanager.html
# https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool
self.client = urllib3.PoolManager(
num_pools=num_pools,
headers={
"Authorization": "Bearer " + self.API_KEY,
"User-Agent": f"Polygon.io PythonClient/{version}",
},
headers=self.headers, # default headers sent with each request.
ca_certs=certifi.where(),
cert_reqs="CERT_REQUIRED",
)

self.timeout = urllib3.Timeout(connect=connect_timeout, read=read_timeout)
self.retries = retries
if verbose:
Expand All @@ -67,12 +72,16 @@ def _get(
result_key: Optional[str] = None,
deserializer=None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Any:
option = options if options is not None else RequestOptionBuilder()

resp = self.client.request(
"GET",
self.BASE + path,
fields=params,
retries=self.retries,
headers=self._concat_headers(option.headers),
)

if resp.status != 200:
Expand Down Expand Up @@ -143,12 +152,18 @@ def _get_params(

return params

def _concat_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
if headers is None:
return self.headers
return {**headers, **self.headers}

def _paginate_iter(
self,
path: str,
params: dict,
deserializer,
result_key: str = "results",
options: Optional[RequestOptionBuilder] = None,
):
while True:
resp = self._get(
Expand All @@ -157,6 +172,7 @@ def _paginate_iter(
deserializer=deserializer,
result_key=result_key,
raw=True,
options=options,
)
decoded = self._decode(resp)
for t in decoded[result_key]:
Expand All @@ -174,15 +190,21 @@ def _paginate(
raw: bool,
deserializer,
result_key: str = "results",
options: Optional[RequestOptionBuilder] = None,
):
if raw:
return self._get(
path=path, params=params, deserializer=deserializer, raw=True
path=path,
params=params,
deserializer=deserializer,
raw=True,
options=options,
)

return self._paginate_iter(
path=path,
params=params,
deserializer=deserializer,
result_key=result_key,
options=options,
)
10 changes: 10 additions & 0 deletions polygon/rest/indicators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from urllib3 import HTTPResponse
from datetime import datetime, date

from .models.request import RequestOptionBuilder


class IndicatorsClient(BaseClient):
def get_sma(
Expand All @@ -29,6 +31,7 @@ def get_sma(
params: Optional[Dict[str, Any]] = None,
series_type: Optional[Union[str, SeriesType]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[SMAIndicatorResults, HTTPResponse]:
"""
Get SMA values for a given ticker over a given range with the specified parameters
Expand Down Expand Up @@ -62,6 +65,7 @@ def get_sma(
result_key="results",
deserializer=SMAIndicatorResults.from_dict,
raw=raw,
options=options,
)

def get_ema(
Expand All @@ -80,6 +84,7 @@ def get_ema(
params: Optional[Dict[str, Any]] = None,
series_type: Optional[Union[str, SeriesType]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[EMAIndicatorResults, HTTPResponse]:
"""
Get EMA values for a given ticker over a given range with the specified parameters
Expand Down Expand Up @@ -113,6 +118,7 @@ def get_ema(
result_key="results",
deserializer=EMAIndicatorResults.from_dict,
raw=raw,
options=options,
)

def get_rsi(
Expand All @@ -131,6 +137,7 @@ def get_rsi(
params: Optional[Dict[str, Any]] = None,
series_type: Optional[Union[str, SeriesType]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[RSIIndicatorResults, HTTPResponse]:
"""
Get RSI values for a given ticker over a given range with the specified parameters
Expand Down Expand Up @@ -164,6 +171,7 @@ def get_rsi(
result_key="results",
deserializer=RSIIndicatorResults.from_dict,
raw=raw,
options=options,
)

def get_macd(
Expand All @@ -184,6 +192,7 @@ def get_macd(
params: Optional[Dict[str, Any]] = None,
series_type: Optional[Union[str, SeriesType]] = None,
raw: bool = False,
options: Optional[RequestOptionBuilder] = None,
) -> Union[MACDIndicatorResults, HTTPResponse]:
"""
Get MACD values for a given ticker over a given range with the specified parameters
Expand Down Expand Up @@ -218,4 +227,5 @@ def get_macd(
result_key="results",
deserializer=MACDIndicatorResults.from_dict,
raw=raw,
options=options,
)
6 changes: 6 additions & 0 deletions polygon/rest/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,9 @@ class SeriesType(Enum):
CLOSE = "close"
HIGH = "high"
LOW = "low"


class LaunchPadOptions(Enum):
X_POLYGON_EDGE_ID = "X-Polygon-Edge-ID"
X_POLYGON_IP_ADDRESS = "X-Polygon-Edge-IP-Address"
X_POLYGON_EDGE_USER_AGENT = "X-Polygon-Edge-User-Agent"
Loading

0 comments on commit 97ac520

Please sign in to comment.