Skip to content

Commit 3cee0bc

Browse files
antdjohnsjbonzo
andauthored
Edge headers (#343)
* 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 <[email protected]>
1 parent 82e651c commit 3cee0bc

File tree

15 files changed

+415
-7
lines changed

15 files changed

+415
-7
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Requires Python >= 3.8.
1515
See the [Getting Started](https://polygon-api-client.readthedocs.io/en/latest/Getting-Started.html)
1616
section in our docs or view the [examples](./examples) directory.
1717

18+
#### Launchpad Usage
19+
20+
Users of the Launchpad product will need to pass in certain headers in order to make API requests.
21+
Example can be found [here](./examples/launchpad).
22+
1823
## Contributing
1924

2025
If you found a bug or have an idea for a new feature, please first discuss it with us by

examples/launchpad/README.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Launchpad
2+
3+
Users of the Launchpad product will need to pass in certain headers in order to make API requests.
4+
5+
```python
6+
7+
# import RESTClient
8+
from polygon import RESTClient
9+
from polygon.rest.models.request import RequestOptionBuilder
10+
11+
# create client
12+
c = RESTClient(api_key="API_KEY")
13+
14+
# create request options
15+
options = RequestOptionBuilder().edge_headers(
16+
edge_id="YOUR_EDGE_ID", # required
17+
edge_ip_address="IP_ADDRESS", # required
18+
)
19+
# get response
20+
res = c.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04", options=options)
21+
22+
# do something with response
23+
24+
```
25+
Launchpad users can also provide the optional User Agent value describing their Edge User's origination request.
26+
27+
```python
28+
29+
# import RESTClient
30+
from polygon import RESTClient
31+
from polygon.rest.models.request import RequestOptionBuilder
32+
33+
# create client
34+
c = RESTClient(api_key="API_KEY")
35+
36+
# create request options
37+
options = RequestOptionBuilder().edge_headers(
38+
edge_id="YOUR_EDGE_ID", # required
39+
edge_ip_address="IP_ADDRESS" # required
40+
).update_edge_header(
41+
edge_user="EDGE_USER" # optional
42+
)
43+
44+
# get response
45+
res = c.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04", options=options)
46+
47+
# do something with response
48+
49+
```

examples/launchpad/launchpad.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from polygon import RESTClient
2+
from polygon.rest.models.request import RequestOptionBuilder
3+
4+
5+
def get_list_trades_launchpad():
6+
client = RESTClient()
7+
8+
"""
9+
set headers example:
10+
options = RequestOptionBuilder()
11+
.edge_headers(edge_id="EDGE_ID", edge_ip_address="EDGE_ID_ADDRESS", edge_user="EDGE_USER")
12+
13+
update headers example:
14+
options = options.update_edge_header(edge_ip_address="NEW_IP")
15+
"""
16+
options = RequestOptionBuilder().edge_headers(
17+
edge_id="EDGE_ID", edge_ip_address="EDGE_ID_ADDRESS", edge_user="EDGE_USER"
18+
)
19+
20+
trades = []
21+
for t in client.list_trades("AAA", "2022-04-04", limit=5, options=options):
22+
trades.append(t)
23+
print(trades)
24+
25+
26+
def main():
27+
get_list_trades_launchpad()
28+
29+
30+
if __name__ == "__main__":
31+
main()

examples/rest/simple-get.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
from polygon import RESTClient
2+
from polygon.rest import models
23

34
client = RESTClient()
45

5-
aggs = client.get_aggs("AAPL", 1, "day", "2022-04-04", "2022-04-04")
6+
aggs = client.get_aggs(
7+
"AAPL",
8+
1,
9+
"day",
10+
"2022-04-04",
11+
"2022-04-04",
12+
)
613
print(aggs)

polygon/rest/aggs.py

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from urllib3 import HTTPResponse
55
from datetime import datetime, date
66

7+
from .models.request import RequestOptionBuilder
8+
79

810
class AggsClient(BaseClient):
911
def get_aggs(
@@ -19,6 +21,7 @@ def get_aggs(
1921
limit: Optional[int] = None,
2022
params: Optional[Dict[str, Any]] = None,
2123
raw: bool = False,
24+
options: Optional[RequestOptionBuilder] = None,
2225
) -> Union[List[Agg], HTTPResponse]:
2326
"""
2427
Get aggregate bars for a ticker over a given date range in custom time window sizes.
@@ -48,6 +51,7 @@ def get_aggs(
4851
result_key="results",
4952
deserializer=Agg.from_dict,
5053
raw=raw,
54+
options=options,
5155
)
5256

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

8389
def get_daily_open_close_agg(
@@ -87,6 +93,7 @@ def get_daily_open_close_agg(
8793
adjusted: Optional[bool] = None,
8894
params: Optional[Dict[str, Any]] = None,
8995
raw: bool = False,
96+
options: Optional[RequestOptionBuilder] = None,
9097
) -> Union[DailyOpenCloseAgg, HTTPResponse]:
9198
"""
9299
Get the open, close and afterhours prices of a stock symbol on a certain date.
@@ -105,6 +112,7 @@ def get_daily_open_close_agg(
105112
params=self._get_params(self.get_daily_open_close_agg, locals()),
106113
deserializer=DailyOpenCloseAgg.from_dict,
107114
raw=raw,
115+
options=options,
108116
)
109117

110118
def get_previous_close_agg(
@@ -113,6 +121,7 @@ def get_previous_close_agg(
113121
adjusted: Optional[bool] = None,
114122
params: Optional[Dict[str, Any]] = None,
115123
raw: bool = False,
124+
options: Optional[RequestOptionBuilder] = None,
116125
) -> Union[PreviousCloseAgg, HTTPResponse]:
117126
"""
118127
Get the previous day's open, high, low, and close (OHLC) for the specified stock ticker.
@@ -131,4 +140,5 @@ def get_previous_close_agg(
131140
result_key="results",
132141
deserializer=PreviousCloseAgg.from_dict,
133142
raw=raw,
143+
options=options,
134144
)

polygon/rest/base.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Optional, Any, Dict
77
from datetime import datetime
88
import pkg_resources # part of setuptools
9+
from .models.request import RequestOptionBuilder
910
from ..logging import get_logger
1011
import logging
1112
from ..exceptions import AuthError, BadResponse, NoResultsError
@@ -34,20 +35,24 @@ def __init__(
3435
raise AuthError(
3536
f"Must specify env var POLYGON_API_KEY or pass api_key in constructor"
3637
)
38+
3739
self.API_KEY = api_key
3840
self.BASE = base
3941

42+
self.headers = {
43+
"Authorization": "Bearer " + self.API_KEY,
44+
"User-Agent": f"Polygon.io PythonClient/{version}",
45+
}
46+
4047
# https://urllib3.readthedocs.io/en/stable/reference/urllib3.poolmanager.html
4148
# https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool
4249
self.client = urllib3.PoolManager(
4350
num_pools=num_pools,
44-
headers={
45-
"Authorization": "Bearer " + self.API_KEY,
46-
"User-Agent": f"Polygon.io PythonClient/{version}",
47-
},
51+
headers=self.headers, # default headers sent with each request.
4852
ca_certs=certifi.where(),
4953
cert_reqs="CERT_REQUIRED",
5054
)
55+
5156
self.timeout = urllib3.Timeout(connect=connect_timeout, read=read_timeout)
5257
self.retries = retries
5358
if verbose:
@@ -67,16 +72,21 @@ def _get(
6772
result_key: Optional[str] = None,
6873
deserializer=None,
6974
raw: bool = False,
75+
options: Optional[RequestOptionBuilder] = None,
7076
) -> Any:
7177
if params is None:
7278
params = {}
7379
params = {str(k): str(v) for k, v in params.items() if v is not None}
7480
logger.debug("_get %s params %s", path, params)
81+
82+
option = options if options is not None else RequestOptionBuilder()
83+
7584
resp = self.client.request(
7685
"GET",
7786
self.BASE + path,
7887
fields=params,
7988
retries=self.retries,
89+
headers=self._concat_headers(option.headers),
8090
)
8191

8292
if resp.status != 200:
@@ -147,12 +157,18 @@ def _get_params(
147157

148158
return params
149159

160+
def _concat_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
161+
if headers is None:
162+
return self.headers
163+
return {**headers, **self.headers}
164+
150165
def _paginate_iter(
151166
self,
152167
path: str,
153168
params: dict,
154169
deserializer,
155170
result_key: str = "results",
171+
options: Optional[RequestOptionBuilder] = None,
156172
):
157173
while True:
158174
resp = self._get(
@@ -161,6 +177,7 @@ def _paginate_iter(
161177
deserializer=deserializer,
162178
result_key=result_key,
163179
raw=True,
180+
options=options,
164181
)
165182
decoded = self._decode(resp)
166183
for t in decoded[result_key]:
@@ -178,15 +195,21 @@ def _paginate(
178195
raw: bool,
179196
deserializer,
180197
result_key: str = "results",
198+
options: Optional[RequestOptionBuilder] = None,
181199
):
182200
if raw:
183201
return self._get(
184-
path=path, params=params, deserializer=deserializer, raw=True
202+
path=path,
203+
params=params,
204+
deserializer=deserializer,
205+
raw=True,
206+
options=options,
185207
)
186208

187209
return self._paginate_iter(
188210
path=path,
189211
params=params,
190212
deserializer=deserializer,
191213
result_key=result_key,
214+
options=options,
192215
)

polygon/rest/indicators.py

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from urllib3 import HTTPResponse
1212
from datetime import datetime, date
1313

14+
from .models.request import RequestOptionBuilder
15+
1416

1517
class IndicatorsClient(BaseClient):
1618
def get_sma(
@@ -29,6 +31,7 @@ def get_sma(
2931
params: Optional[Dict[str, Any]] = None,
3032
series_type: Optional[Union[str, SeriesType]] = None,
3133
raw: bool = False,
34+
options: Optional[RequestOptionBuilder] = None,
3235
) -> Union[SMAIndicatorResults, HTTPResponse]:
3336
"""
3437
Get SMA values for a given ticker over a given range with the specified parameters
@@ -62,6 +65,7 @@ def get_sma(
6265
result_key="results",
6366
deserializer=SMAIndicatorResults.from_dict,
6467
raw=raw,
68+
options=options,
6569
)
6670

6771
def get_ema(
@@ -80,6 +84,7 @@ def get_ema(
8084
params: Optional[Dict[str, Any]] = None,
8185
series_type: Optional[Union[str, SeriesType]] = None,
8286
raw: bool = False,
87+
options: Optional[RequestOptionBuilder] = None,
8388
) -> Union[EMAIndicatorResults, HTTPResponse]:
8489
"""
8590
Get EMA values for a given ticker over a given range with the specified parameters
@@ -113,6 +118,7 @@ def get_ema(
113118
result_key="results",
114119
deserializer=EMAIndicatorResults.from_dict,
115120
raw=raw,
121+
options=options,
116122
)
117123

118124
def get_rsi(
@@ -131,6 +137,7 @@ def get_rsi(
131137
params: Optional[Dict[str, Any]] = None,
132138
series_type: Optional[Union[str, SeriesType]] = None,
133139
raw: bool = False,
140+
options: Optional[RequestOptionBuilder] = None,
134141
) -> Union[RSIIndicatorResults, HTTPResponse]:
135142
"""
136143
Get RSI values for a given ticker over a given range with the specified parameters
@@ -164,6 +171,7 @@ def get_rsi(
164171
result_key="results",
165172
deserializer=RSIIndicatorResults.from_dict,
166173
raw=raw,
174+
options=options,
167175
)
168176

169177
def get_macd(
@@ -184,6 +192,7 @@ def get_macd(
184192
params: Optional[Dict[str, Any]] = None,
185193
series_type: Optional[Union[str, SeriesType]] = None,
186194
raw: bool = False,
195+
options: Optional[RequestOptionBuilder] = None,
187196
) -> Union[MACDIndicatorResults, HTTPResponse]:
188197
"""
189198
Get MACD values for a given ticker over a given range with the specified parameters
@@ -218,4 +227,5 @@ def get_macd(
218227
result_key="results",
219228
deserializer=MACDIndicatorResults.from_dict,
220229
raw=raw,
230+
options=options,
221231
)

polygon/rest/models/common.py

+6
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,9 @@ class SeriesType(Enum):
9292
CLOSE = "close"
9393
HIGH = "high"
9494
LOW = "low"
95+
96+
97+
class LaunchPadOptions(Enum):
98+
X_POLYGON_EDGE_ID = "X-Polygon-Edge-ID"
99+
X_POLYGON_IP_ADDRESS = "X-Polygon-Edge-IP-Address"
100+
X_POLYGON_EDGE_USER_AGENT = "X-Polygon-Edge-User-Agent"

0 commit comments

Comments
 (0)