class PolygonData(DataAPI): def __init__(self): self.polygon_rest_client = RESTClient(config.polygon_api_key) if not self.polygon_rest_client: raise AssertionError( "Failed to authenticate Polygon restful client" ) def get_symbols(self) -> List[Dict]: if not self.polygon_rest_client: raise AssertionError("Must call w/ authenticated polygon client") data = self.polygon_rest_client.stocks_equities_snapshot_all_tickers() return data.tickers def get_symbol_data( self, symbol: str, start: date, end: date = date.today(), scale: TimeScale = TimeScale.minute, ) -> pd.DataFrame: if not self.polygon_rest_client: raise AssertionError("Must call w/ authenticated polygon client") data = self.polygon_rest_client.stocks_equities_aggregates( symbol, 1, scale.name, start, end, unadjusted=False ) if not data or not hasattr(data, "results"): raise ValueError( f"[ERROR] {symbol} has no data for {start} to {end} w {scale.name}" ) d = { pd.Timestamp(result["t"], unit="ms", tz="America/New_York"): [ result.get("o"), result.get("h"), result.get("l"), result.get("c"), result.get("v"), result.get("vw"), result.get("n"), ] for result in data.results } _df = pd.DataFrame.from_dict( d, orient="index", columns=[ "open", "high", "low", "close", "volume", "average", "count", ], ) _df["vwap"] = 0.0 return _df
def trade(self): """ This function when called begins the cycle of trading. Note that the data the system has access to will be the beginDate - lookback, but the first trade will be initiated as if it was done during beginDate The Eventual goal for this will be to implement some controls over it in tkinter :return: """ client = RESTClient(self.api_key) tickers = self.portfolio.tickers self.__correct_begin_date() for stock in tickers: response = client.stocks_equities_aggregates(stock, 1, self.bar_distance, self.begin_date - datetime.timedelta(days=1), self.end_date + datetime.timedelta(days=1)) if response.results is None: #Make sure that data is actually gotten raise Exception("Unable to retrieve market data") self.asset[stock] = response.results while self.date_offset + self.look_back.days < len(self.asset[tickers[0]]) - 1: #TODO Is every stock going to have the exact same amount of days? truncated_data = dict() for stock in tickers: truncated_data[stock] = self.asset[stock][self.date_offset:self.look_back.days + self.date_offset] #Creates the set of data that only includes the current lookback period self.trading_func(truncated_data) self.date_offset += 1 self.__calc_pl()
def get_day_quotes(self, ticker, timestamp): """Collects all quotes from the day of the market timestamp.""" polygon_client = PolygonClient(POLYGON_API_KEY) quotes = [] # The timestamp is expected in market time. day_str = timestamp.strftime("%Y-%m-%d") response = polygon_client.stocks_equities_aggregates( ticker, 1, "minute", day_str, day_str) if not response or response.status != "OK" or not response.results: self.logs.error( "Failed to request historical data for %s on %s: %s" % (ticker, timestamp, response)) return None for result in response.results: try: # Parse and convert the current minute's timestamp. minute_timestamp = result["t"] / 1000 minute_market_time = self.utc_to_market_time( datetime.fromtimestamp(minute_timestamp)) # Use the price at the beginning of the minute. price = result["o"] if not price or price < 0: self.logs.warn("Invalid price: %s" % price) continue quote = {"time": minute_market_time, "price": price} quotes.append(quote) except (KeyError, TypeError, ValueError) as e: self.logs.warn("Failed to parse result: %s" % e) return quotes
def get_day_quotes(self, ticker, timestamp): """Collects all quotes from the day of the market timestamp.""" polygon_client = PolygonClient(POLYGON_API_KEY) quotes = [] # The timestamp is expected in market time. day_str = timestamp.strftime('%Y-%m-%d') try: response = polygon_client.stocks_equities_aggregates( ticker, 1, 'minute', day_str, day_str) results = response.results except AttributeError as e: self.logs.error( 'Failed to request historical data for %s on %s: %s' % (ticker, timestamp, e)) return None for result in results: try: # Parse and convert the current minute's timestamp. minute_timestamp = result['t'] / 1000 minute_market_time = self.utc_to_market_time( datetime.fromtimestamp(minute_timestamp)) # Use the price at the beginning of the minute. price = result['o'] if not price or price < 0: self.logs.warn('Invalid price: %s' % price) continue quote = {'time': minute_market_time, 'price': price} quotes.append(quote) except (KeyError, TypeError, ValueError) as e: self.logs.warn('Failed to parse result: %s' % e) return quotes
class Polygon(MarketData): def __init__(self, token=os.environ.get('POLYGON'), free=True): super().__init__() self.client = RESTClient(token) self.provider = 'polygon' self.free = free def obey_free_limit(self): if self.free and hasattr(self, 'last_api_call_time'): time_since_last_call = time() - self.last_api_call_time delay = C.POLY_FREE_DELAY - time_since_last_call if delay > 0: sleep(delay) def log_api_call_time(self): self.last_api_call_time = time() def get_dividends(self, **kwargs): def _get_dividends(symbol, timeframe='max'): self.obey_free_limit() try: response = self.client.reference_stock_dividends(symbol) except Exception as e: raise e finally: self.log_api_call_time() raw = pd.DataFrame(response.results) df = self.standardize_dividends(symbol, raw) return self.reader.data_in_timeframe(df, C.EX, timeframe) return self.try_again(func=_get_dividends, **kwargs) def get_splits(self, **kwargs): def _get_splits(symbol, timeframe='max'): self.obey_free_limit() try: response = self.client.reference_stock_splits(symbol) except Exception as e: raise e finally: self.log_api_call_time() raw = pd.DataFrame(response.results) df = self.standardize_splits(symbol, raw) return self.reader.data_in_timeframe(df, C.EX, timeframe) return self.try_again(func=_get_splits, **kwargs) def get_ohlc(self, **kwargs): def _get_ohlc(symbol, timeframe='max'): is_crypto = symbol.find('X%3A') == 0 formatted_start, formatted_end = self.traveller.convert_dates( timeframe) self.obey_free_limit() try: response = self.client.stocks_equities_aggregates( symbol, 1, 'day', from_=formatted_start, to=formatted_end, unadjusted=False) except Exception as e: raise e finally: self.log_api_call_time() raw = response.results columns = { 't': 'date', 'o': 'open', 'h': 'high', 'l': 'low', 'c': 'close', 'v': 'volume', 'vw': 'average', 'n': 'trades' } df = pd.DataFrame(raw).rename(columns=columns) if is_crypto: df['date'] = pd.to_datetime(df['date'], unit='ms') else: df['date'] = pd.to_datetime( df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert( C.TZ).dt.tz_localize(None) df = self.standardize_ohlc(symbol, df) return self.reader.data_in_timeframe(df, C.TIME, timeframe) return self.try_again(func=_get_ohlc, **kwargs) def get_intraday(self, **kwargs): def _get_intraday(symbol, min=1, timeframe='max', extra_hrs=True): # pass min directly into stock_aggs function as multiplier is_crypto = symbol.find('X%3A') == 0 dates = self.traveller.dates_in_range(timeframe) if dates == []: raise Exception(f'No dates in timeframe: {timeframe}.') for idx, date in enumerate(dates): self.obey_free_limit() try: response = self.client.stocks_equities_aggregates( symbol, min, 'minute', from_=date, to=date, unadjusted=False) except Exception as e: raise e finally: self.log_api_call_time() if hasattr(response, 'results'): response = response.results else: continue columns = { 't': 'date', 'o': 'open', 'h': 'high', 'l': 'low', 'c': 'close', 'v': 'volume', 'vw': 'average', 'n': 'trades' } df = pd.DataFrame(response).rename(columns=columns) if is_crypto: df['date'] = pd.to_datetime(df['date'], unit='ms') else: df['date'] = pd.to_datetime( df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert( C.TZ).dt.tz_localize(None) filename = self.finder.get_intraday_path( symbol, date, self.provider) df = self.standardize_ohlc(symbol, df, filename) df = df[df[C.TIME].dt.strftime(C.DATE_FMT) == date] yield df return self.try_again(func=_get_intraday, **kwargs)
'low': 'Low', 'close': 'Close', }) elif False: pu = "https://api.polygon.io/v2/aggs/ticker/AAPL/range/1/week/2019-01-01/2019-02-01?sort=asc&apiKey=rmr_K5BO36nfgC_N6qpPNt95kMY_Z3F_iX_0aC" if freq == "H" or freq.startswith("H"): timespan = "hour" multiplier = int(freq.replace("H", "")) elif freq == "D" or freq.startswith("D"): timespan = "day" multiplier = int(freq.replace("D", "")) elif freq == "M" or freq.startswith("M"): timespan = "minute" multiplier = int(freq.replace("M", "")) res = pgo.stocks_equities_aggregates(ticker, multiplier, timespan, startDate, endDate) df = pd.DataFrame(res.results) df = df.rename(columns={ 't': 'Date_Time', 'o': 'Open', 'h': 'High', 'l': 'Low', 'c': 'Close', }) df['Date_Time'] = df['Date_Time'].map(lambda n: int(n / 1000)) df['Date_Time'] = pd.to_datetime(df['Date_Time'], unit="s") else: if freq == "H" or freq.startswith("H"): p = freq.replace("H", "") + "h" elif freq == "D" or freq.startswith("D"):