def parse_mnb_result(mnb_result, ticker): """Válasz XML feldolgozása, átalakítása a beancount által elvárt beancount.prices.source.SourcePrice() formátumra <MNBCurrentExchangeRates> <Day date="2019-02-22"> <Rate unit="1" curr="AUD">199,16000</Rate> <Rate unit="1" curr="BGN">162,51000</Rate> </Day> </MNBCurrentExchangeRates> """ # kézi XML feldolgozás, mert az MNB lusta volt XSD sémát mellékelni a servicehez root = etree.fromstring(mnb_result) if len(root) > 0: # ha a fa gyökerének van eleme (<Day>), dátum feldolgozása datum = datetime.strptime(root[0].attrib['date'], '%Y-%m-%d').replace(tzinfo=pytz.timezone('CET')) # végigmegyünk a <Rate> elemeken for currency in root[0]: if currency.attrib['curr'] == ticker: # ha a kért árfolyam devizaneme megfelel, árfolyam konvertálása 1 egységre arfolyam = Decimal(currency.text.replace(',', '.')) egyseg = int(currency.attrib['unit']) return source.SourcePrice(price=Decimal(arfolyam / egyseg), time=datum, quote_currency='HUF') else: # ha az xml fa gyökerének nincs eleme, akkor aznapra nincs érvényes árfolyam return source.SourcePrice(price=None, time=datetime.now(tz=pytz.timezone('CET')), quote_currency='HUF')
def _check_valid(self, query_date, out_time, out_price): candles = [ (datetime.datetime(2017, 1, 21, 0, 0, 0, tzinfo=tz.tzutc()), D('1.3100')), (datetime.datetime(2017, 1, 21, 8, 0, 0, tzinfo=tz.tzutc()), D('1.3300')), (datetime.datetime(2017, 1, 21, 16, 0, 0, tzinfo=tz.tzutc()), D('1.3500')), (datetime.datetime(2017, 1, 22, 0, 0, 0, tzinfo=tz.tzutc()), D('1.3700')), (datetime.datetime(2017, 1, 22, 8, 0, 0, tzinfo=tz.tzutc()), D('1.3900')), (datetime.datetime(2017, 1, 22, 16, 0, 0, tzinfo=tz.tzutc()), D('1.4100')), (datetime.datetime(2017, 1, 23, 0, 0, 0, tzinfo=tz.tzutc()), D('1.4300')), (datetime.datetime(2017, 1, 23, 8, 0, 0, tzinfo=tz.tzutc()), D('1.4500')), (datetime.datetime(2017, 1, 23, 16, 0, 0, tzinfo=tz.tzutc()), D('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=tz.tzutc()) 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_price_for_date(self, ticker, date=None): paramater = ticker.split("--") currency = paramater[1].upper() if date == None: timestamp = int(datetime.today().timestamp()) else: timestamp = int(date.timestamp()) url = BASE_URL_TEMPLATE.substitute(date_start=timestamp, date_end=timestamp + 1, ticker=paramater[0], currency=currency) try: content = requests.get(url).content ret = json.loads(content) quote = ret['data']['quotes'][0]['quote'][currency] price = D(quote['close']) return source.SourcePrice(price, date, CURRENCY) except KeyError: raise CoinmarketcapError( "Invalid response from Coinmarketcap: {}".format( repr(content))) except AttributeError: raise CoinmarketcapError( "Invalid response from Coinmarketcap: {}".format( repr(content)))
def _get_price(self, ticker, time=None): match = TICKER_REGEXP.match(ticker) base_currency, quote_currency = match.groups() data = get_data(base_currency) if time is None: price_str = data['rebalancing_set']['price_usd'] price = to_decimal(price_str, 6) price_time = datetime.datetime.now(tz=datetime.timezone.utc) else: time_utc = time.astimezone(datetime.timezone.utc) sorted_prices = sorted(zip( data['rebalancing_set']['historicals']['dates'], data['rebalancing_set']['historicals']['prices'], ), key=lambda item: item[0]) for price_time_str, price_str in sorted_prices: price_time = datetime.datetime.strptime( price_time_str, '%Y-%m-%dT%H:%M:%SZ', ).replace(tzinfo=datetime.timezone.utc) if price_time.date() == time_utc.date() \ and price_time > time_utc: price = to_decimal(price_str, 6) break else: return None return source.SourcePrice(price, price_time, base_currency)
def get_latest_price(self, ticker): """Fetch the current latest price. The date may differ. This routine attempts to fetch the most recent available price, and returns the actual date of the quoted price, which may differ from the date this call is made at. {1cfa25e37fc1} Args: ticker: A string, the ticker to be fetched by the source. This ticker may include structure, such as the exchange code. Also note that this ticker is source-specified, and is not necessarily the same value as the commodity symbol used in the Beancount file. Returns: A SourcePrice instance. If the price could not be fetched, None is returned and another source should be consulted. There is never any guarantee that a price source will be able to fetch its value; client code must be able to handle this. Also note that the price's returned time must be timezone-aware. """ try: theStock = Stock(ticker) theQuote = theStock.get_book()["quote"] # IEX is American markets. us_timezone = tz.gettz("America/New_York") theDate = datetime.datetime.fromtimestamp( theQuote["latestUpdate"] / 1000) theDate = theDate.astimezone(us_timezone) thePrice = D(theQuote["latestPrice"]).quantize(D("0.01")) return source.SourcePrice(thePrice, theDate, "USD") except Exception: raise IEXError("Erreur lors de l'execution de la requete") return None
def get_latest_price(self, ticker): token = environ['IBKR_TOKEN'] queryId = environ['IBKR_QUERY_ID'] try: response = client.download(token, queryId) except client.ResponseCodeError as e: if e.code == '1018': sleep(10) response = client.download(token, queryId) else: raise e statement = parser.parse(response) for position in statement.FlexStatements[0].OpenPositions: if position.symbol.rstrip('z') == ticker: price = D(position.markPrice) timezone = tz.gettz("Europe/Zurich") time = datetime.combine( position.reportDate, datetime.min.time()).astimezone(timezone) return source.SourcePrice(price, time, position.currency) return None
def _get_price_for_date(self, ticker, date=None): time.sleep(TIME_DELAY) if date == None: url = BASE_URL_TEMPLATE_WITHOUT_TIME.substitute(ticker=ticker) else: # Somehow it expects a 3 day timespan in the query time_start = int((date - timedelta(days=1)).timestamp()) time_end = int((date + timedelta(days=2)).timestamp()) url = BASE_URL_TEMPLATE_WITH_TIME.substitute(time_start=time_start, time_end=time_end, ticker=ticker) try: content = requests.get(url).content parsed_content = json.loads(content) quotes = parsed_content['data']['quotes'] if date == None: # If no date, pick the latest quote = max(quotes, key = lambda x: parser.isoparse(x['time_open'])) else: quote = list(filter(lambda x: parser.isoparse(x['time_open']) == date, quotes)) assert len(quote) == 1 quote = quote[0] date = parser.isoparse(quote['quote'][CURRENCY]['timestamp']) price = D(quote['quote'][CURRENCY]['close']).quantize(D('0.0000')) return source.SourcePrice(price, date, CURRENCY) except KeyError as e: raise CoinmarketcapError("Exception {}, Invalid response from Coinmarketcap: {}".format(e, repr(content))) except AttributeError as e: raise CoinmarketcapError("Exception {}, Invalid response from Coinmarketcap: {}".format(e, repr(content)))
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 = D(result['price']).quantize(D('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 _get_price_for_date(self, ticker, date=None): time.sleep(TIME_DELAY) if date == None: date_string = "" else: date_string = date.strftime("%Y%m%d") url = BASE_URL_TEMPLATE.substitute(date=date_string, ticker=ticker) try: content = requests.get(url).content soup = BeautifulSoup(content, 'html.parser') table = soup.find('table', {'class': 'table'}) tr = table.findChildren('tr')[1] data = [td.text.strip() for td in tr.findChildren('td')] parsed_date = parse_date_liberally(data[0]) date = datetime(parsed_date.year, parsed_date.month, parsed_date.day, tzinfo=utc) price = D(data[4]) return source.SourcePrice(price, date, CURRENCY) except KeyError: raise CoinmarketcapError( "Invalid response from Coinmarketcap: {}".format( repr(content))) except AttributeError: raise CoinmarketcapError( "Invalid response from Coinmarketcap: {}".format( repr(content)))
def get_latest_price(self, ticker): """See contract in beancount.prices.source.Source.""" _, quote_currency = _get_currencies(ticker) if quote_currency is None: logging.error( "Invalid price source ticker '%s'; must be like 'EUR_USD'", ticker) return # Query at the current (latest) time. params_dict = { 'instrument': ticker, 'granularity': 'S5', # Every two hours. 'count': '10', 'candleFormat': 'midpoint', } time_prices = _fetch_candles(params_dict) if time_prices is None: return # Get the latest price point available. time, price = sorted(time_prices)[-1] # Use current local date as the date. current_date = datetime.datetime.now() return source.SourcePrice(price, current_date, quote_currency)
def get_latest_price(self, ticker): """See contract in beancount.prices.source.Source.""" path_to_script = os.path.abspath(os.path.dirname(__file__)) + "/financequote.pl" params = [path_to_script] if ':' in ticker: exchange, symbol = ticker.split(':') else: # exchange and symbol were not both supplied return None params = [path_to_script, exchange, symbol] # output is a json object with keys in the form "$symbol\u001$variable" output = subprocess.check_output(params).decode() info = json.loads(output) # remove the `symbol` prefix from the keys (+1 for control character) info = {x[len(symbol) + 1:]: info[x] for x in info.keys()} if 'price' not in info: return None # data was not able to be fetched currency = info['currency'] # Finance::Quote returns date in mm/dd/YY format trade_date = datetime.datetime.strptime(info['date'], "%m/%d/%Y") price = D(info['price']) return source.SourcePrice(price, trade_date, currency)
def _get_price_for_date(self, ticker, date=None): paramater = ticker.split("--") currency = paramater[1].upper() if date == None: date = datetime.today().replace(hour=0, minute=0, second=0) + timedelta(days=-1) end_date = date + timedelta(days=1) url = BASE_URL_TEMPLATE.substitute(date_start=int(date.timestamp()), date_end=int(end_date.timestamp()), ticker=paramater[0], currency=currency) try: content = requests.get(url).content ret = json.loads(content) quote = ret['data']['quotes'][0]['quote'][currency] price = D(quote['close']) return source.SourcePrice(price, date, CURRENCY) except: raise CoinmarketcapError( "Invalid response from Coinmarketcap: {}".format( repr(content)))
def test_get_latest_price(self): self.response.read.return_value = textwrap.dedent("""\ { "instrument" : "USD_CAD", "granularity" : "S5", "candles" : [ { "time" : "2016-04-04T21:19:17.000000Z", "openMid" : 1.308475, "highMid" : 1.308475, "lowMid" : 1.308475, "closeMid" : 1.308475, "volume" : 1, "complete" : true } ] } """).encode('utf-8') srcprice = self.fetcher.get_latest_price('USD_CAD') self.assertIsNotNone(srcprice) self.assertTrue(isinstance(srcprice.price, Decimal)) expected = source.SourcePrice( D('1.308475'), datetime.datetime(2016, 4, 4, 21, 19, 17, tzinfo=dateutil.tz.tzutc()), 'CAD') self.assertEqual(expected, srcprice)
def get_latest_price(self, ticker): """See contract in beancount.prices.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 = D(result['regularMarketPrice']) timezone = datetime.timezone( datetime.timedelta(hours=result['gmtOffSetMilliseconds'] / 3600000), result['exchangeTimezoneName']) trade_time = datetime.datetime.fromtimestamp( result['regularMarketTime'], tz=timezone) except KeyError: raise YahooError("Invalid response from Yahoo: {}".format( repr(result))) currency = parse_currency(result) return source.SourcePrice(price, trade_time, currency)
def _get_daily_price(self, fund, date=None): assert fund[0] == "F" params = { "callback": "thecallback", "fundCode": fund[1:], "pageIndex": 1, "pageSize": 1, } if date is not None: dt_str = date.strftime("%Y-%m-%d") params.update({ "startDate": dt_str, "endDate": dt_str, }) resp = self.http.get( "https://api.fund.eastmoney.com/f10/lsjz", params=params, headers={ "Referer": "http://fundf10.eastmoney.com/jjjz_{fund}.html" } ) assert resp.status_code == 200, resp.text result_str = next(re.finditer("thecallback\((.*)\)", resp.text)).groups()[0] result = json.loads(result_str) records = result["Data"]["LSJZList"] if len(records) == 0: return trade_date = records[0]["FSRQ"] nav = D(records[0]["DWJZ"]).quantize(D('1.000000000000000000')) trade_date = datetime.strptime(trade_date, "%Y-%m-%d") trade_date = utils.default_tzinfo(trade_date, CN_TZ) return source.SourcePrice(nav, trade_date, 'CNY')
def setUp(self): fetch_cached = mock.patch( 'beancount.prices.price.fetch_cached_price').start() fetch_cached.return_value = source.SourcePrice( D('125.00'), datetime.datetime(2015, 11, 22, 16, 0, 0), 'JPY') self.dprice = find_prices.DatedPrice('JPY', 'USD', datetime.date(2015, 11, 22), None) self.addCleanup(mock.patch.stopall)
def get_latest_price(self, ticker): resp = self.cached_request("CURRENCY_EXCHANGE_RATE", ticker) base = resp['Realtime Currency Exchange Rate'] last_refreshed = base["6. Last Refreshed"] last_refreshed_tz = base['7. Time Zone'] return source.SourcePrice( D(base['5. Exchange Rate']), datetime.fromisoformat(last_refreshed).replace( tzinfo=self.get_timezone(last_refreshed_tz)), self.currency)
def get_latest_price(self, ticker): resp = requests.get(url='https://www.bitstamp.net/api/v2/ticker/' + ticker.lower() + 'eur/') data = resp.json() price = D(data['last']) date = datetime.datetime.fromtimestamp(int(data['timestamp'])) us_timezone = tz.gettz("Europe/Zurich") time = date.astimezone(us_timezone) return source.SourcePrice(price, time, 'EUR')
def get_historical_price(self, compound_ticker, date): """See contract in beancount.prices.source.Source.""" # security_type, exchange, ticker = compound_ticker.lower().split(':') # symbol = self.get_ft_symbol(security_type, exchange, ticker) symbol = compound_ticker if not symbol: logging.info("Could not find secId for %s" % compound_ticker) return None # Look back some number of days in the past in order to make sure we hop # over national holidays. begin_date = date - datetime.timedelta(days=5) end_date = date # template = 'http://mschart.morningstar.com/chartweb/defaultChart?type=getcc&secids={}&dataid={}&startdate={}&enddate={}¤cy=&format=1' template = 'https://markets.ft.com/data/equities/ajax/get-historical-prices?startDate={}&endDate={}&symbol={}' def fmt(d): return d.strftime('%Y/%m/%d') # symbol = 19753923 url = template.format(fmt(begin_date), fmt(end_date), symbol) logging.info("Fetching %s", url) try: response = net_utils.retrying_urlopen(url) if response is None: return None response = response.read().decode('utf-8').strip() response = json.loads(response) if 'status' in response: status = response['status'] if status['code'] != 200: logging.info("HTTP Status: [%s] %s" % (status['code'], status['message'])) return None soup = BeautifulSoup(response['html'], 'html.parser') except error.HTTPError: return None try: entries = soup.find_all('td') trade_date = entries[0].find_all('span')[0].contents[0] trade_date = datetime.datetime.strptime(trade_date, '%A, %B %d, %Y') trade_date = trade_date.replace(tzinfo=pytz.UTC) price = D(entries[4].contents[0]) return source.SourcePrice(price, trade_date, None) except: import sys logging.error("Error parsing data.", sys.exc_info()[0]) return None
def get_historical_price(self, ticker, date): """See contract in beancount.prices.source.Source.""" _, quote_currency = _get_currencies(ticker) if quote_currency is None: logging.error( "Invalid price source ticker '%s'; must be like 'EUR_USD'", ticker) return # Find the boundary dates to query in UTC timezone. start_utc = datetime.datetime(date.year, date.month, date.day, 0, 0, 0, tzinfo=tz.tzlocal()).astimezone( tz.tzutc()) end_utc = start_utc + datetime.timedelta(days=1) interval_begin_utc = (start_utc - datetime.timedelta(days=5)) interval_end_utc = (end_utc + datetime.timedelta(days=5)) # Build the query. params_dict = { 'instrument': ticker, 'granularity': 'H2', # Every two hours. 'candleFormat': 'midpoint', 'start': interval_begin_utc.isoformat('T'), 'end': interval_end_utc.isoformat('T'), } time_prices = _fetch_candles(params_dict) # Get all the prices with the same date. same_date = [ item for item in time_prices if start_utc <= item[0] < end_utc ] if same_date: # Find the min/max and return the median of all prices. sorted_prices = sorted(same_date, key=lambda item: item[1]) time, price = sorted_prices[len(sorted_prices) // 2] else: # No price matching the date were found; use the midpoint of the # last price before the day interval and the first price after the # day interval. before_time, before_price = min(item for item in time_prices if item[0] < start_utc) after_time, after_price = max(item for item in time_prices if item[0] >= end_utc) price = (after_price + before_price) / 2 time = before_time + (after_time - before_time) / 2 return source.SourcePrice(price, time, quote_currency)
def get_latest_price(self, ticker): resp = requests.get(url='https://api.ratesapi.io/latest?base=' + ticker + '&symbols=CHF') data = resp.json() price = D(str(data['rates']['CHF'])) date = parse(data['date']) us_timezone = tz.gettz("Europe/Zurich") time = date.astimezone(us_timezone) return source.SourcePrice(price, time, 'CHF')
def _get_price(self, ticker, time=None): match = TICKER_REGEXP.match(ticker) api_key, base_currency, quote_currency = match.groups() data = get_latest_quote(api_key, base_currency, quote_currency) price = to_decimal(data['price'], 8) price_time = datetime.datetime.strptime( data['last_updated'], '%Y-%m-%dT%H:%M:%S.%fZ', ).replace(tzinfo=datetime.timezone.utc) return source.SourcePrice(price, price_time, base_currency)
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 = D(price_float) match = re.search(r'(\..*)', str(price_float)) if match: price = price.quantize(D(match.group(1))) # Note: There is no currency information in the response (surprising). return source.SourcePrice(price, time, None)
def _get_price(self, ticker, time=None): match = TICKER_REGEXP.match(ticker) api_key, token_address, quote_currency = match.groups() token_price, symbol = get_exchange_rate(api_key, token_address) underlying_price, timestamp = cryptonator.get_latest_price( symbol[1:], # iDAI -> DAI quote_currency, ) price = to_decimal(token_price * float(underlying_price), 8) price_time = datetime.datetime.fromtimestamp(timestamp).\ replace(tzinfo=datetime.timezone.utc) return source.SourcePrice(price, price_time, symbol)
def get_historical_price(self, ticker, time): us_timezone = tz.gettz("Europe/Zurich") reqdate = time.astimezone(us_timezone).date() resp = requests.get(url='https://api.exchangeratesapi.io/' + str(reqdate) + '?base=' + ticker + '&symbols=CHF') data = resp.json() price = D(str(data['rates']['CHF'])) date = parse(data['date']) time = date.astimezone(us_timezone) return source.SourcePrice(price, time, 'CHF')
def get_historical_price(self, ticker, date): """See contract in beancount.prices.source.Source.""" # Look back some number of days in the past in order to make sure we hop # over national holidays. begin_date = date - datetime.timedelta(days=5) end_date = date # Make the query. params = parse.urlencode( sorted({ 'q': ticker, 'startdate': begin_date.strftime('%b+%d,%Y'), 'enddate': end_date.strftime('%b+%d,%Y'), 'num': 5, 'output': 'csv', }.items())) url = 'http://www.google.com/finance/historical?{}'.format(params) try: response = net_utils.retrying_urlopen(url) if response is None: return None data = response.read() except socket.timeout: logging.error("Connection timed out") return None except error.HTTPError: # When the instrument is incorrect, you will get a 404. return None # Note: utf-8-sig automatically skips the BOM here. data = data.decode('utf-8-sig').strip() lines = data.splitlines() assert len(lines) >= 2, "Too few lines in returned data: {}".format( len(lines)) # Parse the header, find the column for the adjusted close. columns = lines[0].split(',') index_price = columns.index('Close') assert index_price >= 0, "Could not find 'Adj Close' data column." index_date = columns.index('Date') assert index_date >= 0, "Could not find 'Date' data column." # Get the latest data returned. most_recent_data = lines[1].split(',') close_price = D(most_recent_data[index_price]) date = datetime.datetime.strptime(most_recent_data[index_date], '%d-%b-%y') return source.SourcePrice(close_price, date, None)
def get_latest_price(self, ticker): """Fetch the current latest price. The date may differ. This routine attempts to fetch the most recent available price, and returns the actual date of the quoted price, which may differ from the date this call is made at. {1cfa25e37fc1} Args: ticker: A string, the ticker to be fetched by the source. This ticker may include structure, such as the exchange code. Also note that this ticker is source-specified, and is not necessarily the same value as the commodity symbol used in the Beancount file. Returns: A SourcePrice instance. If the price could not be fetched, None is returned and another source should be consulted. There is never any guarantee that a price source will be able to fetch its value; client code must be able to handle this. Also note that the price's returned time must be timezone-aware. """ s = requests.Session() url = ( BASEURL + "/Bio/rech_part.aspx?varvalidform=on&CodeISIN=" + ticker + "&CLASSPROD=0&NumAgr=&selectNRJ=0&NomProd=&NomSOc=&action=new&valid_form=Lancer+la+recherche" ) r = s.get(url) soup = BeautifulSoup(r.text, "html.parser") try: soup.find("input", {"name": "NumProd"})["value"] except Exception: raise AMFGecoError("ISIN introuvable sur AMFGeco") return None theDate = soup.find("td", text="Date VL :").next_sibling.get_text( strip=True ) theDate = parse_datetime(theDate, dayfirst=True) fr_timezone = tz.gettz("Europe/Paris") theDate = theDate.astimezone(fr_timezone) thePrice = soup.find("td", text="Valeur (€) :").next_sibling.get_text( strip=True ) thePrice = D(thePrice.replace(" ", "").replace(",", ".")).quantize( D("0.01") ) return source.SourcePrice(thePrice, theDate, "EUR")
def fetch_quote(ticker): """Fetch a quote from Coinbase.""" url = "https://api.coinbase.com/v2/prices/{}/spot".format(ticker.lower()) response = requests.get(url) if response.status_code != requests.codes.ok: raise CoinbaseError("Invalid response ({}): {}".format(response.status_code, response.text)) result = response.json() price = D(result['data']['amount']).quantize(D('0.01')) time = datetime.datetime.now(tz.tzutc()) currency = result['data']['currency'] return source.SourcePrice(price, time, currency)
def get_historical_price(self, ticker, date): """See contract in beancount.prices.source.Source.""" # Look back some number of days in the past in order to make sure we hop # over national holidays. begin_date = date - datetime.timedelta(days=5) end_date = date # Make the query. params = parse.urlencode( sorted({ 's': ticker, 'a': begin_date.month - 1, 'b': begin_date.day, 'c': begin_date.year, 'd': end_date.month - 1, 'e': end_date.day, 'f': end_date.year, 'g': 'd', 'ignore': '.csv', }.items())) url = 'http://ichart.yahoo.com/table.csv?{}'.format(params) try: response = net_utils.retrying_urlopen(url) if response is None: return None data = response.read().decode('utf-8').strip() except error.HTTPError: return None if response is None: return None lines = data.splitlines() assert len(lines) >= 2, "Too few lines in returned data: {}".format( len(lines)) # Parse the header, find the column for the adjusted close. columns = lines[0].split(',') index_price = columns.index('Adj Close') assert index_price >= 0, "Could not find 'Adj Close' data column." index_date = columns.index('Date') assert index_date >= 0, "Could not find 'Date' data column." # Get the latest data returned. most_recent_data = lines[1].split(',') close_price = D(most_recent_data[index_price]) date = datetime.datetime.strptime(most_recent_data[index_date], '%Y-%m-%d') return source.SourcePrice(close_price, date, None)
def get_historical_price(self, ticker, time): date = time.strftime('%Y-%m-%d') resp = self.cached_request("FX_DAILY", ticker) walk_back = 0 while date not in resp['Time Series FX (Daily)']: if walk_back > self.walk_back_days: return None time -= timedelta(1) walk_back += 1 date = time.strftime('%Y-%m-%d') return source.SourcePrice( D(resp['Time Series FX (Daily)'][date]['4. close']), datetime.fromisoformat(date).replace( tzinfo=self.get_timezone(resp['Meta Data']['6. Time Zone'])), self.currency)