├── v_sources ├── __init__.py └── wtd.py ├── .gitignore ├── setup.py └── README.md /v_sources/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | .pytest_cache/* 3 | MANIFEST 4 | record.txt 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='beancount-wtd-prices', 5 | version='2.1', 6 | packages=['v_sources'], 7 | license='BSD', 8 | long_description=open('README.md').read(), 9 | ) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Additional importers for beancount plaintext accounting 2 | 3 | Installation 4 | ------------ 5 | python3 setup.py install 6 | 7 | World Trading Data price source 8 | ------------------------ 9 | Sign up for an API token at https://www.alphavantage.co/ 10 | 11 | set the api token in your shell. For example, add this to your `~/.bashrc` 12 | ``` 13 | export ALPHA_VANTAGE_KEY="..." 14 | ``` 15 | 16 | Run `bean-price` commands as normal: 17 | ``` 18 | bean-price --no-cache -e 'USD:v_sources.wtd/VTSAX' 19 | ``` 20 | 21 | The ticker can be a stock symbol or mutual fund. 22 | 23 | Seems to only work for USD for now. 24 | -------------------------------------------------------------------------------- /v_sources/wtd.py: -------------------------------------------------------------------------------- 1 | """Fetch prices from World Trading Data's JSON 'api' 2 | """ 3 | 4 | import datetime 5 | import json 6 | import logging 7 | import os 8 | import pytz 9 | import re 10 | import requests 11 | 12 | from beancount.core.number import D 13 | from beancount.prices import source 14 | from beancount.utils import net_utils 15 | from datetime import datetime, date, timedelta, timezone 16 | 17 | 18 | """ 19 | bean-price --no-cache -e 'USD:v_sources.wtd/VTSAX' 20 | """ 21 | 22 | current_tz = datetime.now(timezone.utc).astimezone().tzinfo 23 | 24 | class Source(source.Source): 25 | "World Trading Data API price extractor." 26 | 27 | def get_latest_price(self, ticker): 28 | return self.get_historical_price(ticker, date.today()) 29 | 30 | def get_historical_price(self, ticker, dt): 31 | """See contract in beancount.prices.source.Source.""" 32 | 33 | if isinstance(dt, datetime): 34 | dt = dt.date() 35 | 36 | date_from = dt - timedelta(days=5) 37 | date_to = dt 38 | 39 | 40 | url = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol={}&apikey={}".format( 41 | ticker, 42 | os.environ['ALPHA_VANTAGE_KEY'] 43 | ) 44 | 45 | logging.info("Fetching %s", url) 46 | 47 | r = requests.get(url) 48 | r.raise_for_status() 49 | 50 | response = r.json() 51 | 52 | key = 'Time Series (Daily)' 53 | 54 | curr_date = date_to 55 | 56 | while curr_date > date_from: 57 | price_by_date = response[key] 58 | if str(curr_date) not in price_by_date: 59 | curr_date = curr_date - timedelta(days=1) 60 | continue 61 | price_data = price_by_date[str(curr_date)] 62 | curr_date_parsed = datetime.strptime(str(curr_date), '%Y-%m-%d').replace(tzinfo=current_tz) 63 | return source.SourcePrice(D(price_data['4. close']), curr_date_parsed, 'USD') 64 | 65 | logging.error("Error retrieving stock info response=%s", response) 66 | return None 67 | 68 | 69 | if __name__ == '__main__': 70 | s = Source() 71 | print(s.get_latest_price('VTSAX')) 72 | --------------------------------------------------------------------------------