def get_latest_price(self, ticker: str) -> Optional[source.SourcePrice]: """See contract in beanprice.source.Source.""" url = "https://query1.finance.yahoo.com/v7/finance/quote" fields = ['symbol', 'regularMarketPrice', 'regularMarketTime'] payload = { 'symbols': ticker, 'fields': ','.join(fields), 'exchange': 'NYSE', } payload.update(_DEFAULT_PARAMS) response = requests.get(url, params=payload) result = parse_response(response) try: price = Decimal(result['regularMarketPrice']) tzone = timezone( timedelta(hours=result['gmtOffSetMilliseconds'] / 3600000), result['exchangeTimezoneName']) trade_time = datetime.fromtimestamp(result['regularMarketTime'], tz=tzone) except KeyError as exc: raise YahooError("Invalid response from Yahoo: {}".format( repr(result))) from exc currency = parse_currency(result) return source.SourcePrice(price, trade_time, currency)
def fetch_quote(ticker): """Fetch the latest price for the given ticker.""" url = "https://api.iextrading.com/1.0/tops/last?symbols={}".format( ticker.upper()) response = requests.get(url) if response.status_code != requests.codes.ok: raise IEXError("Invalid response ({}): {}".format( response.status_code, response.text)) results = response.json() if len(results) != 1: raise IEXError("Invalid number of responses from IEX: {}".format( response.text)) result = results[0] price = Decimal(result['price']).quantize(Decimal('0.01')) # IEX is American markets. us_timezone = tz.gettz("America/New_York") time = datetime.datetime.fromtimestamp(result['time'] / 1000) time = time.astimezone(us_timezone) # As far as can tell, all the instruments on IEX are priced in USD. return source.SourcePrice(price, time, 'USD')
def _check_valid(self, query_date, out_time, out_price): candles = [ (datetime.datetime(2017, 1, 21, 0, 0, 0, tzinfo=UTC), Decimal('1.3100')), (datetime.datetime(2017, 1, 21, 8, 0, 0, tzinfo=UTC), Decimal('1.3300')), (datetime.datetime(2017, 1, 21, 16, 0, 0, tzinfo=UTC), Decimal('1.3500')), (datetime.datetime(2017, 1, 22, 0, 0, 0, tzinfo=UTC), Decimal('1.3700')), (datetime.datetime(2017, 1, 22, 8, 0, 0, tzinfo=UTC), Decimal('1.3900')), (datetime.datetime(2017, 1, 22, 16, 0, 0, tzinfo=UTC), Decimal('1.4100')), (datetime.datetime(2017, 1, 23, 0, 0, 0, tzinfo=UTC), Decimal('1.4300')), (datetime.datetime(2017, 1, 23, 8, 0, 0, tzinfo=UTC), Decimal('1.4500')), (datetime.datetime(2017, 1, 23, 16, 0, 0, tzinfo=UTC), Decimal('1.4700')), ] with mock.patch.object(oanda, '_fetch_candles', return_value=candles): query_time = datetime.datetime.combine(query_date, time=datetime.time( 16, 0, 0), tzinfo=UTC) srcprice = self.fetcher.get_historical_price('USD_CAD', query_time) if out_time is not None: self.assertEqual( source.SourcePrice(out_price, out_time, 'CAD'), srcprice) else: self.assertEqual(None, srcprice)
def get_latest_price(self, ticker): symbol, base = _parse_ticker(ticker) headers = { 'X-CMC_PRO_API_KEY': environ['COINMARKETCAP_API_KEY'], } params = { 'symbol': symbol, 'convert': base, } resp = requests.get( url='https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest', params=params, headers=headers) if resp.status_code != requests.codes.ok: raise CoinmarketcapApiError( "Invalid response ({}): {}".format(resp.status_code, resp.text) ) data = resp.json() if data['status']['error_code'] != 0: status = data['status'] raise CoinmarketcapApiError( "Invalid response ({}): {}".format( status['error_code'], status['error_message']) ) quote = data['data'][symbol]['quote'][base] price = Decimal(str(quote['price'])) date = parse(quote['last_updated']) return source.SourcePrice(price, date, base)
def get_daily_prices( self, ticker: str, time_begin: datetime, time_end: datetime) -> Optional[List[source.SourcePrice]]: """See contract in beanprice.source.Source.""" series, currency = get_price_series(ticker, time_begin, time_end) return [ source.SourcePrice(price, time, currency) for time, price in series ]
def fetch_time_series(ticker, time=None): """Fetch""" # Create request payload. ticker_spec = parse_ticker(ticker) url = "https://www.quandl.com/api/v3/datasets/{}/{}.json".format( ticker_spec.database, ticker_spec.dataset) payload = {"limit": 1} if time is not None: date = time.date() payload["start_date"] = (date - datetime.timedelta(days=10)).isoformat() payload["end_date"] = date.isoformat() # Add API key, if it is set in the environment. if 'QUANDL_API_KEY' in os.environ: payload['api_key'] = os.environ['QUANDL_API_KEY'] # Fetch and process errors. response = requests.get(url, params=payload) if response.status_code != requests.codes.ok: raise QuandlError("Invalid response ({}): {}".format( response.status_code, response.text)) result = response.json() if 'quandl_error' in result: raise QuandlError(result['quandl_error']['message']) # Parse result container. dataset = result['dataset'] column_names = dataset['column_names'] date_index = column_names.index('Date') if ticker_spec.column is not None: data_index = column_names.index(ticker_spec.column) else: try: data_index = column_names.index('Adj. Close') except ValueError: data_index = column_names.index('Close') data = dataset['data'][0] # Gather time and assume it's in UTC timezone (Quandl does not provide the # market's timezone). time = datetime.datetime.strptime(data[date_index], '%Y-%m-%d') time = time.replace(tzinfo=tz.tzutc()) # Gather price. # Quantize with the same precision default rendering of floats occur. price_float = data[data_index] price = Decimal(price_float) match = re.search(r'(\..*)', str(price_float)) if match: price = price.quantize(Decimal(match.group(1))) # Note: There is no currency information in the response (surprising). return source.SourcePrice(price, time, None)
def _test_valid(self): candles = [ (datetime.datetime(2017, 1, 21, 0, 45, 15, tzinfo=UTC), Decimal('1.330115')), (datetime.datetime(2017, 1, 21, 0, 45, 20, tzinfo=UTC), Decimal('1.330065')), ] with mock.patch.object(oanda, '_fetch_candles', return_value=candles): srcprice = self.fetcher.get_latest_price('USD_CAD') # Latest price, with current time as time. self.assertEqual(source.SourcePrice( Decimal('1.330065'), datetime.datetime(2017, 1, 21, 0, 45, 20, tzinfo=UTC), 'CAD'), srcprice)
def get_historical_price(self, ticker: str, time: datetime) -> Optional[source.SourcePrice]: """See contract in beanprice.source.Source.""" # Get the latest data returned over the last 5 days. series, currency = get_price_series(ticker, time - timedelta(days=5), time) latest = None for data_dt, price in sorted(series): if data_dt >= time: break latest = data_dt, price if latest is None: raise YahooError("Could not find price before {} in {}".format(time, series)) return source.SourcePrice(price, data_dt, currency)
def _get_quote(ticker, date): """Fetch a exchangerate from ratesapi.""" base, symbol = _parse_ticker(ticker) params = { 'base': base, 'symbol': symbol, } response = requests.get(url='https://api.exchangerate.host/' + date, params=params) if response.status_code != requests.codes.ok: raise RatesApiError("Invalid response ({}): {}".format(response.status_code, response.text)) result = response.json() price = Decimal(str(result['rates'][symbol])) time = parse(result['date']).replace(tzinfo=tz.tzutc()) return source.SourcePrice(price, time, symbol)
def fetch_quote(ticker, time=None): """Fetch a quote from Coinbase.""" url = "https://api.coinbase.com/v2/prices/{}/spot".format(ticker.lower()) options = {} if time is not None: options['date'] = time.astimezone(tz.tzutc()).date().isoformat() response = requests.get(url, options) if response.status_code != requests.codes.ok: raise CoinbaseError("Invalid response ({}): {}".format( response.status_code, response.text)) result = response.json() price = Decimal(result['data']['amount']) if time is None: time = datetime.datetime.now(tz.tzutc()) currency = result['data']['currency'] return source.SourcePrice(price, time, currency)
def _get_price(self, ticker, time=None): base, symbol = ticker.split(':') url_params = { 'base': base, 'symbols': symbol, } if time is None: date_str = 'latest' else: date_str = time.strftime('%Y-%m-%d') url = urljoin(API_BASE_URL, date_str) + '?' + urlencode(url_params) response = urlopen(url) result = json.loads(response.read().decode()) price = to_decimal(result['rates'][symbol], 4) price_time = datetime.datetime.\ strptime(result['date'], '%Y-%m-%d').\ replace(tzinfo=datetime.timezone.utc) return source.SourcePrice(price, price_time, base)
def _get_price(ticker, time=None): dataset, data_id, quote_currency = ticker.split(':') if time is None: date = datetime.utcnow().replace(tzinfo=pytz.utc) else: date = time # https://finmind.github.io/quickstart/#api_1 parameter = { "dataset": dataset, "data_id": data_id, "start_date": (date.strftime(Source.DATE_FORMAT)), } resp = requests.get(Source.API_ENDPOINT, params=parameter) data = resp.json()["data"] if len(data) == 0: # No data today return None close_price = data[0]["close"] price = D(close_price).quantize(D('0.00')) return source.SourcePrice(price, date, quote_currency)
def _get_price(self, ticker, time=None): base, symbol = ticker.split(':') url_params = {'base': base, 'symbols': symbol, 'source': SOURCE} if time is None: date_str = 'latest' else: date_str = time.strftime('%Y-%m-%d') url = urljoin(EXCHANGERATE_API_URL, date_str) + '?' + urlencode(url_params) request = Request(url) # Requests without User-Agent header can be blocked request.add_header('User-Agent', 'price-fetcher') response = urlopen(request) result = json.loads(response.read().decode()) price = to_decimal(result['rates'][symbol], 4) price_time = datetime.datetime.\ strptime(result['date'], '%Y-%m-%d').\ replace(tzinfo=datetime.timezone.utc) return source.SourcePrice(price, price_time, base)
def _fetch_price(params_dict, time): """Fetch a price from OANDA using the given parameters.""" ticker = params_dict['instrument'] _, quote_currency = _get_currencies(ticker) if quote_currency is None: logging.error("Invalid price source ticker '%s'; must be like 'EUR_USD'", ticker) return time_prices = _fetch_candles(params_dict) if not time_prices: logging.error("No prices returned.") return # Get all the prices before and on the same date and find the latest. sorted_prices = [item for item in time_prices if item[0] <= time] if not sorted_prices: logging.error("No prices matched.") return time, price = sorted_prices[-1] return source.SourcePrice(price, time, quote_currency)
def get_historical_price(self, fund, time): """See contract in beanprice.source.Source.""" if requests is None: raise TSPError("You must install the 'requests' library.") if fund not in TSP_FUND_NAMES: raise TSPError( "Invalid TSP Fund Name '{}'. Valid Funds are:\n\t{}".format( fund, "\n\t".join(TSP_FUND_NAMES))) url = "https://secure.tsp.gov/components/CORS/getSharePricesRaw.html" fields = ['startdate', 'enddate', 'download', 'Lfunds', 'InvFunds'] payload = { # Grabbing the last fourteen days of data in event the markets were closed. 'startdate': (time - datetime.timedelta(days=14)).strftime("%Y%m%d"), 'enddate': time.strftime("%Y%m%d"), 'download': '0', 'Lfunds': '1', 'InvFunds': '1' } response = requests.get(url, params=payload) result = parse_response(response) trade_day = list(result.items())[0] prices = trade_day[1] try: price = prices[TSP_FUND_NAMES.index(fund)] trade_time = trade_day[0] except KeyError as exc: raise TSPError("Invalid response from TSP: {}".format( repr(result))) from exc return source.SourcePrice(price, trade_time, CURRENCY)