def classify_holdings_for_export(holdings_list, commodities_map): """Figure out what to do for example with each holding. Args: holdings_list: A list of Holding instances to be exported. commodities_map: A dict of commodity to Commodity instances. Returns: A pair of: action_holdings: A list of (symbol, holding) for each holding. 'Symbol' is the ticker to use for export, and may be "CASH" or "IGNORE" for holdings to be converted or ignored. """ # Get the map of commodities to tickers and export meta tags. exports = getters.get_values_meta(commodities_map, FIELD) # Classify the holdings based on their commodities' ticker metadata field. action_holdings = [] for holding in holdings_list: # Get export field and remove (MONEY:...) specifications. export = re.sub(r'\(.*\)', '', exports.get(holding.currency, None) or '').strip() if export: if export.upper() == "CASH": action_holdings.append(('CASH', holding)) elif export.upper() == "IGNORE": action_holdings.append(('IGNORE', holding)) else: action_holdings.append((export, holding)) else: logging.warning(("Exporting holding using default commodity name '{}'; this " "can potentially break the OFX import. Consider providing " "'export' metadata for your commodities.").format( holding.currency)) action_holdings.append((holding.currency, holding)) return action_holdings
def main(): import argparse, logging logging.basicConfig(level=logging.INFO, format='%(levelname)-8s: %(message)s') parser = argparse.ArgumentParser(description=__doc__.strip()) parser.add_argument('filename', help='Filename') args = parser.parse_args() entries, errors, options_map = loader.load_file(args.filename) commodity_map = getters.get_commodity_map(entries, options_map) ticker_info = getters.get_values_meta(commodity_map, 'name', 'ticker', 'quote') print('Fetching:') for currency, (name, ticker, cost_currency) in sorted(ticker_info.items()): if ticker: print('{:16} {:16} {:16} {}'.format(currency, ticker, cost_currency, name)) print() print('Skipping:') for currency, (name, ticker, cost_currency) in sorted(ticker_info.items()): if not ticker: print('{:16} {:16} {:16} {}'.format(currency, '', cost_currency or '', name or ''))
def classify_holdings_for_export(holdings_list, commodities_map): """Figure out what to do for example with each holding. Args: holdings_list: A list of Holding instances to be exported. commodities_map: A dict of commodity to Commodity instances. Returns: A pair of: action_holdings: A list of (symbol, holding) for each holding. 'Symbol' is the ticker to use for export, and may be "CASH" or "IGNORE" for holdings to be converted or ignored. """ # Get the map of commodities to tickers and export meta tags. tickers = getters.get_values_meta(commodities_map, 'ticker') exports = getters.get_values_meta(commodities_map, 'export') # Classify the holdings based on their commodities' ticker metadata field. action_holdings = [] for holding in holdings_list: export = exports.get(holding.currency, None) ticker = tickers.get(holding.currency, None) if isinstance(export, str) and export: if export.upper() == "CASH": action_holdings.append(('CASH', holding)) elif export.upper() == "IGNORE": action_holdings.append(('IGNORE', holding)) elif export.upper() == "MONEY": # Hmm this is an interesting case... an actual holding is in # units of our money-market standing. We could disallow this, # but we can also just export it. Let's export it with the # ticker value or commodity if present. action_holdings.append( (ticker if ticker else holding.currency, holding)) else: action_holdings.append((export, holding)) elif ticker: action_holdings.append((ticker, holding)) else: logging.warn( ("Exporting holding using default commodity name '{}'; this " "can potentially break the OFX import. Consider providing " "'ticker' or 'export' metadata for your commodities.").format( holding.currency)) action_holdings.append((holding.currency, holding)) return action_holdings
def test_get_values_meta__multi(self): entries, _, options_map = loader.load_string(TEST_INPUT) commodity_map = getters.get_commodity_map(entries, options_map) values = getters.get_values_meta(commodity_map, 'name', 'ticker') self.assertEqual({'HOOL': ('Hooli Corp.', 'NYSE:HOOLI'), 'PIPA': ('Pied Piper', None), 'USD': (None, None)}, values)
def test_get_values_meta__single(self): entries, _, options_map = loader.load_string(TEST_INPUT) commodity_map = getters.get_commodity_map(entries, options_map) values = getters.get_values_meta(commodity_map, 'name', default='BLA') self.assertEqual({'USD': 'BLA', 'PIPA': 'Pied Piper', 'HOOL': 'Hooli Corp.'}, values)
def generate_table(self, entries, errors, options_map): commodity_map = getters.get_commodity_map(entries, options_map) ticker_info = getters.get_values_meta(commodity_map, 'name', 'ticker', 'quote') price_rows = [ (currency, cost_currency, ticker, name) for currency, (name, ticker, cost_currency) in sorted(ticker_info.items()) if ticker] return table.create_table(price_rows, [(0, "Currency"), (1, "Cost-Currency"), (2, "Symbol"), (3, "Name")])
def main(): logging.basicConfig(level=logging.INFO, format='%(levelname)-8s: %(message)s') parser = argparse.ArgumentParser(description=__doc__.strip()) parser.add_argument('filename', help='Beancount input file') #parser.add_argument('docid', help="Spreadsheets doc id to update") parser.add_argument('-n', '--dry-run', action='store_true') args = parser.parse_args() # Load the file contents. entries, errors, options_map = loader.load_file(args.filename) # Enumerate the list of assets. def keyfun(posting): if posting.cost is None: return (1, posting.units.currency, posting.account) else: return (0, posting.account, posting.cost.currency) postings = sorted(get_balance_sheet_balances(clean_entries_for_balances(entries), options_map), key=keyfun) # Simplify the accounts to their root accounts. root_accounts = get_root_accounts(postings) postings = [posting._replace(account=root_accounts[posting.account]) for posting in postings] # Aggregate postings by account/currency. agg_postings = sorted(aggregate_postings(postings), key=keyfun) agg_postings = list(agg_postings) # Add prices to the postings. agg_postings = add_prices_to_postings(entries, agg_postings) # Get the map of commodities to export meta tags. commodities_map = getters.get_commodity_map(entries) exports = getters.get_values_meta(commodities_map, 'export') asset_type = getters.get_values_meta(commodities_map, 'assets') # Get the map of accounts to export meta tags. accounts_map = { account: open for account, (open, _) in getters.get_account_open_close(entries).items()} tax_map = populate_with_parents(getters.get_values_meta(accounts_map, 'tax'), 'TAXABLE') # Filter out postings to be ignored. agg_postings = [posting for posting in agg_postings if exports.get(posting.units.currency, None) != 'IGNORE'] # Realize the model. price_map = prices.build_price_map(entries) model = Model(price_map, list(agg_postings), exports, asset_type, tax_map) # Write out the assets to stdout in CSV format. if args.dry_run: return table = model_to_table(model) table[0][0] += ' ({:%Y-%m-%d %H:%M})'.format(datetime.datetime.now()) wr = csv.writer(sys.stdout) wr.writerows(table)