Skip to content

Commit cadd01f

Browse files
aitzkovitzdanielatpolygoniomorningvera
authored
Add Technical Indicator Endpoints (#284)
* initial models * wip * restore websocket files * restore examples to master * touch up from_dict methods, add tests * individual result types * lint * remove multiplier per spec change * rename mock to account for spec change in test * fix typos in method descriptions * rename the base instead of re-export * update rest spec Co-authored-by: danielatpolygonio <[email protected]> Co-authored-by: Vera Harless <[email protected]>
1 parent a5b266b commit cadd01f

11 files changed

+6320
-37
lines changed

.polygon/rest.json

Lines changed: 5500 additions & 28 deletions
Large diffs are not rendered by default.

polygon/rest/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .trades import TradesClient
33
from .quotes import QuotesClient
44
from .snapshot import SnapshotClient
5+
from .indicators import IndicatorsClient
56
from .reference import (
67
MarketsClient,
78
TickersClient,
@@ -32,6 +33,7 @@ class RESTClient(
3233
ConditionsClient,
3334
ExchangesClient,
3435
ContractsClient,
36+
IndicatorsClient,
3537
):
3638
def __init__(
3739
self,

polygon/rest/indicators.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
from polygon.rest.models.common import SeriesType
2+
from polygon.rest.models.indicators import (
3+
SMAIndicatorResults,
4+
EMAIndicatorResults,
5+
RSIIndicatorResults,
6+
MACDIndicatorResults,
7+
)
8+
from .base import BaseClient
9+
from typing import Optional, Any, Dict, List, Union
10+
from .models import Order
11+
from urllib3 import HTTPResponse
12+
from datetime import datetime, date
13+
14+
15+
class IndicatorsClient(BaseClient):
16+
def get_sma(
17+
self,
18+
ticker: str,
19+
timestamp: Optional[Union[str, int, datetime, date]] = None,
20+
timestamp_lt: Optional[Union[str, int, datetime, date]] = None,
21+
timestamp_lte: Optional[Union[str, int, datetime, date]] = None,
22+
timestamp_gt: Optional[Union[str, int, datetime, date]] = None,
23+
timestamp_gte: Optional[Union[str, int, datetime, date]] = None,
24+
timespan: Optional[str] = None,
25+
window: Optional[int] = None,
26+
adjusted: Optional[bool] = None,
27+
expand_underlying: Optional[bool] = None,
28+
order: Optional[Union[str, Order]] = None,
29+
params: Optional[Dict[str, Any]] = None,
30+
series_type: Optional[Union[str, SeriesType]] = None,
31+
raw: bool = False,
32+
) -> Union[SMAIndicatorResults, HTTPResponse]:
33+
"""
34+
Get SMA values for a given ticker over a given range with the specified parameters
35+
36+
:param ticker: The ticker symbol
37+
:param timespan: The size of the underlying aggregate time window
38+
:param window: The window size used to calculate the simple moving average. i.e. a window size of 10 with daily
39+
aggregates would result in a 10-day moving average
40+
:param timestamp: Either a date with the format YYYY-MM-DD or a millisecond timestamp.
41+
:param timestamp_lt: Timestamp less than
42+
:param timestamp_lte: Timestamp less than or equal to
43+
:param timestamp_gt: Timestamp greater than
44+
:param timestamp_gte: Timestamp greater than or equal to
45+
:param adjusted: Whether the underlying aggregates are adjusted for splits. By default, the aggregates used to
46+
calculate this indicator are adjusted. Set this as false to get results that are NOT adjusted for splits
47+
:param expand_underlying: Whether to include the aggregates used to calculate this indicator in the response
48+
:param order: Sort the results by timestamp. asc will return results in ascending order (oldest at the top),
49+
desc will return results in descending order (newest at the top).The end of the aggregate time window
50+
:param params: Any additional query params
51+
:param series_type: The price in the aggregate which will be used to calculate the simple moving average
52+
i.e. 'close' will result in using close prices to calculate the simple moving average
53+
:param raw: Return raw object instead of results object
54+
:return: SingleIndicatorResults
55+
"""
56+
57+
url = f"/v1/indicators/sma/{ticker}"
58+
59+
return self._get(
60+
path=url,
61+
params=self._get_params(self.get_sma, locals()),
62+
result_key="results",
63+
deserializer=SMAIndicatorResults.from_dict,
64+
raw=raw,
65+
)
66+
67+
def get_ema(
68+
self,
69+
ticker: str,
70+
timestamp: Optional[Union[str, int, datetime, date]] = None,
71+
timestamp_lt: Optional[Union[str, int, datetime, date]] = None,
72+
timestamp_lte: Optional[Union[str, int, datetime, date]] = None,
73+
timestamp_gt: Optional[Union[str, int, datetime, date]] = None,
74+
timestamp_gte: Optional[Union[str, int, datetime, date]] = None,
75+
timespan: Optional[str] = None,
76+
window: Optional[int] = None,
77+
adjusted: Optional[bool] = None,
78+
expand_underlying: Optional[bool] = None,
79+
order: Optional[Union[str, Order]] = None,
80+
params: Optional[Dict[str, Any]] = None,
81+
series_type: Optional[Union[str, SeriesType]] = None,
82+
raw: bool = False,
83+
) -> Union[EMAIndicatorResults, HTTPResponse]:
84+
"""
85+
Get EMA values for a given ticker over a given range with the specified parameters
86+
87+
:param ticker: The ticker symbol
88+
:param timespan: The size of the underlying aggregate time window
89+
:param window: The window size used to calculate the exponential moving average. i.e. a window size of 10 with daily
90+
aggregates would result in a 10-day moving average
91+
:param timestamp: Either a date with the format YYYY-MM-DD or a millisecond timestamp.
92+
:param timestamp_lt: Timestamp less than
93+
:param timestamp_lte: Timestamp less than or equal to
94+
:param timestamp_gt: Timestamp greater than
95+
:param timestamp_gte: Timestamp greater than or equal to
96+
:param adjusted: Whether the underlying aggregates are adjusted for splits. By default, the aggregates used to
97+
calculate this indicator are adjusted. Set this as false to get results that are NOT adjusted for splits
98+
:param expand_underlying: Whether to include the aggregates used to calculate this indicator in the response
99+
:param order: Sort the results by timestamp. asc will return results in ascending order (oldest at the top),
100+
desc will return results in descending order (newest at the top).The end of the aggregate time window
101+
:param params: Any additional query params
102+
:param series_type: The price in the aggregate which will be used to calculate the simple moving average
103+
i.e. 'close' will result in using close prices to calculate the simple moving average
104+
:param raw: Return raw object instead of results object
105+
:return: SingleIndicatorResults
106+
"""
107+
108+
url = f"/v1/indicators/ema/{ticker}"
109+
110+
return self._get(
111+
path=url,
112+
params=self._get_params(self.get_ema, locals()),
113+
result_key="results",
114+
deserializer=EMAIndicatorResults.from_dict,
115+
raw=raw,
116+
)
117+
118+
def get_rsi(
119+
self,
120+
ticker: str,
121+
timestamp: Optional[Union[str, int, datetime, date]] = None,
122+
timestamp_lt: Optional[Union[str, int, datetime, date]] = None,
123+
timestamp_lte: Optional[Union[str, int, datetime, date]] = None,
124+
timestamp_gt: Optional[Union[str, int, datetime, date]] = None,
125+
timestamp_gte: Optional[Union[str, int, datetime, date]] = None,
126+
timespan: Optional[str] = None,
127+
window: Optional[int] = None,
128+
adjusted: Optional[bool] = None,
129+
expand_underlying: Optional[bool] = None,
130+
order: Optional[Union[str, Order]] = None,
131+
params: Optional[Dict[str, Any]] = None,
132+
series_type: Optional[Union[str, SeriesType]] = None,
133+
raw: bool = False,
134+
) -> Union[RSIIndicatorResults, HTTPResponse]:
135+
"""
136+
Get RSI values for a given ticker over a given range with the specified parameters
137+
138+
:param ticker: The ticker symbol
139+
:param timespan: The size of the underlying aggregate time window
140+
:param window: The window size used to calculate the simple moving average. i.e. a window size of 10 with daily
141+
aggregates would result in a 10-day moving average
142+
:param timestamp: Either a date with the format YYYY-MM-DD or a millisecond timestamp.
143+
:param timestamp_lt: Timestamp less than
144+
:param timestamp_lte: Timestamp less than or equal to
145+
:param timestamp_gt: Timestamp greater than
146+
:param timestamp_gte: Timestamp greater than or equal to
147+
:param adjusted: Whether the underlying aggregates are adjusted for splits. By default, the aggregates used to
148+
calculate this indicator are adjusted. Set this as false to get results that are NOT adjusted for splits
149+
:param expand_underlying: Whether to include the aggregates used to calculate this indicator in the response
150+
:param order: Sort the results by timestamp. asc will return results in ascending order (oldest at the top),
151+
desc will return results in descending order (newest at the top).The end of the aggregate time window
152+
:param params: Any additional query params
153+
:param series_type: The price in the aggregate which will be used to calculate the simple moving average
154+
i.e. 'close' will result in using close prices to calculate the simple moving average
155+
:param raw: Return raw object instead of results object
156+
:return: SingleIndicatorResults
157+
"""
158+
159+
url = f"/v1/indicators/rsi/{ticker}"
160+
161+
return self._get(
162+
path=url,
163+
params=self._get_params(self.get_rsi, locals()),
164+
result_key="results",
165+
deserializer=RSIIndicatorResults.from_dict,
166+
raw=raw,
167+
)
168+
169+
def get_macd(
170+
self,
171+
ticker: str,
172+
timestamp: Optional[Union[str, int, datetime, date]] = None,
173+
timestamp_lt: Optional[Union[str, int, datetime, date]] = None,
174+
timestamp_lte: Optional[Union[str, int, datetime, date]] = None,
175+
timestamp_gt: Optional[Union[str, int, datetime, date]] = None,
176+
timestamp_gte: Optional[Union[str, int, datetime, date]] = None,
177+
timespan: Optional[str] = None,
178+
short_window: Optional[int] = None,
179+
long_window: Optional[int] = None,
180+
signal_window: Optional[int] = None,
181+
adjusted: Optional[bool] = None,
182+
expand_underlying: Optional[bool] = None,
183+
order: Optional[Union[str, Order]] = None,
184+
params: Optional[Dict[str, Any]] = None,
185+
series_type: Optional[Union[str, SeriesType]] = None,
186+
raw: bool = False,
187+
) -> Union[MACDIndicatorResults, HTTPResponse]:
188+
"""
189+
Get MACD values for a given ticker over a given range with the specified parameters
190+
191+
:param ticker: The ticker symbol
192+
:param timespan: The size of the underlying aggregate time window
193+
:param short_window: The short window size used to calculate the MACD data
194+
:param long_window: The long window size used to calculate the MACD data
195+
:param signal_window: The window size used to calculate the MACD signal line
196+
:param timestamp: Either a date with the format YYYY-MM-DD or a millisecond timestamp.
197+
:param timestamp_lt: Timestamp less than
198+
:param timestamp_lte: Timestamp less than or equal to
199+
:param timestamp_gt: Timestamp greater than
200+
:param timestamp_gte: Timestamp greater than or equal to
201+
:param adjusted: Whether the underlying aggregates are adjusted for splits. By default, the aggregates used to
202+
calculate this indicator are adjusted. Set this as false to get results that are NOT adjusted for splits
203+
:param expand_underlying: Whether to include the aggregates used to calculate this indicator in the response
204+
:param order: Sort the results by timestamp. asc will return results in ascending order (oldest at the top),
205+
desc will return results in descending order (newest at the top).The end of the aggregate time window
206+
:param params: Any additional query params
207+
:param series_type: The price in the aggregate which will be used to calculate the simple moving average
208+
i.e. 'close' will result in using close prices to calculate the simple moving average
209+
:param raw: Return raw object instead of results object
210+
:return: MACDIndicatorResults
211+
"""
212+
213+
url = f"/v1/indicators/macd/{ticker}"
214+
215+
return self._get(
216+
path=url,
217+
params=self._get_params(self.get_macd, locals()),
218+
result_key="results",
219+
deserializer=MACDIndicatorResults.from_dict,
220+
raw=raw,
221+
)

polygon/rest/models/__init__.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from .common import *
21
from .aggs import *
3-
from .trades import *
4-
from .quotes import *
5-
from .markets import *
6-
from .tickers import *
7-
from .splits import *
8-
from .dividends import *
2+
from .common import *
93
from .conditions import *
4+
from .contracts import *
5+
from .dividends import *
106
from .exchanges import *
11-
from .snapshot import *
127
from .financials import *
13-
from .contracts import *
8+
from .indicators import *
9+
from .markets import *
10+
from .quotes import *
11+
from .snapshot import *
12+
from .splits import *
13+
from .tickers import *
14+
from .trades import *

polygon/rest/models/common.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,10 @@ class Precision(Enum):
8585
TWO = 2
8686
THREE = 3
8787
FOUR = 4
88+
89+
90+
class SeriesType(Enum):
91+
OPEN = "open"
92+
CLOSE = "close"
93+
HIGH = "high"
94+
LOW = "low"

polygon/rest/models/indicators.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from sqlite3 import Timestamp
2+
from typing import Optional, Any, Dict, List, Union
3+
from ...modelclass import modelclass
4+
from .aggs import Agg
5+
6+
7+
@modelclass
8+
class IndicatorValue:
9+
"Contains one datum for indicators with a single value."
10+
timestamp: Optional[int] = None
11+
value: Optional[float] = None
12+
13+
@staticmethod
14+
def from_dict(d):
15+
return IndicatorValue(
16+
timestamp=d.get("timestamp", None),
17+
value=d.get("value", None),
18+
)
19+
20+
21+
@modelclass
22+
class MACDIndicatorValue:
23+
"Contains one datum for all MACD values."
24+
timestamp: Optional[int] = None
25+
value: Optional[float] = None
26+
signal: Optional[float] = None
27+
histogram: Optional[float] = None
28+
29+
@staticmethod
30+
def from_dict(d):
31+
return MACDIndicatorValue(
32+
timestamp=d.get("timestamp", None),
33+
value=d.get("value", None),
34+
signal=d.get("histogram", None),
35+
histogram=d.get("signal", None),
36+
)
37+
38+
39+
@modelclass
40+
class IndicatorUnderlying:
41+
"Contains the URL to call to get the aggs used for building the indicator."
42+
url: Optional[str] = None
43+
aggregates: Optional[List[Agg]] = None
44+
45+
@staticmethod
46+
def from_dict(d):
47+
return IndicatorUnderlying(
48+
url=d.get("url", None),
49+
aggregates=[Agg.from_dict(a) for a in d.get("aggregates", [])],
50+
)
51+
52+
53+
@modelclass
54+
class SingleIndicatorResults:
55+
"Contains indicator values and Underlying."
56+
values: Optional[List[IndicatorValue]] = None
57+
underlying: Optional[IndicatorUnderlying] = None
58+
59+
@staticmethod
60+
def from_dict(d):
61+
return SingleIndicatorResults(
62+
values=[IndicatorValue.from_dict(v) for v in (d.get("values", []))],
63+
underlying=IndicatorUnderlying.from_dict(d.get("underlying", None)),
64+
)
65+
66+
67+
SMAIndicatorResults = SingleIndicatorResults
68+
EMAIndicatorResults = SingleIndicatorResults
69+
RSIIndicatorResults = SingleIndicatorResults
70+
71+
72+
@modelclass
73+
class MACDIndicatorResults:
74+
"Contains indicator values and Underlying."
75+
values: Optional[List[MACDIndicatorValue]] = None
76+
underlying: Optional[IndicatorUnderlying] = None
77+
78+
@staticmethod
79+
def from_dict(d):
80+
return MACDIndicatorResults(
81+
values=[MACDIndicatorValue.from_dict(v) for v in (d.get("values", []))],
82+
underlying=IndicatorUnderlying.from_dict(d.get("underlying", None)),
83+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"results": {
3+
"underlying": {
4+
"url": "http://localhost:8081/v2/aggs/ticker/AAPL/range/1/day/1477368000000/1478393873000?adjusted=false\u0026limit=50000\u0026sort=desc"
5+
},
6+
"values": [
7+
{
8+
"timestamp": 1478232000000,
9+
"value": 110.96883950617286
10+
},
11+
{
12+
"timestamp": 1478145600000,
13+
"value": 112.03325925925927
14+
},
15+
{
16+
"timestamp": 1478059200000,
17+
"value": 113.1348888888889
18+
},
19+
{
20+
"timestamp": 1477972800000,
21+
"value": 113.90733333333334
22+
}
23+
]
24+
},
25+
"status": "OK",
26+
"request_id": "aaa162ba-e0b6-4c4a-aa05-dcac472aea71"
27+
}

0 commit comments

Comments
 (0)