def collect_commodities(book, gnc_accts, date): used_commodities = set() gnc_commodities = {} for acct in gnc_accts: gnc_commodity = acct.GetCommodity() ns = gnc_commodity.get_namespace() name = gnc_commodity.get_mnemonic() if (ns, name) not in used_commodities: used_commodities.add((ns, name)) gnc_commodities[name] = gnc_commodity tbl = book.get_table() commodities = [] for ns in tbl.get_namespaces(): for gnc_commodity in tbl.get_commodities(ns): name = gnc_commodity.get_mnemonic() if (ns, name) in used_commodities: name = Converter.normalize_commodity(name) meta = { 'export': '%s:%s' % (ns, name), 'name': gnc_commodity.get_fullname(), 'price': 'USD:yahoo/%s' % name } commodities.append(data.Commodity(meta, date, name)) return commodities, gnc_commodities
def extract_commodities(self, statement: FlexStatement, existing_entries: list = None): """ TODO: Better Matching (on conid) """ # Make a dict of all existing commodities existing_commodities = {} if existing_entries: for entry in existing_entries: if isinstance(entry, data.Commodity): existing_commodities[entry.currency] = entry results = [] for obj in statement.SecuritiesInfo: assert obj.conid, obj symbol = self.clean_symbol(obj.symbol) if symbol in existing_commodities: continue meta = { 'lineno': 0, 'filename': '', 'description': obj.description, 'multiplier': obj.multiplier, 'conid': obj.conid, 'asset-type': obj.assetCategory.name.lower(), } if obj.cusip: meta['cusip'] = obj.cusip if obj.securityID: meta['security-id'] = obj.securityID commodity = data.Commodity(currency=symbol, meta=meta, date=datetime.date(2010, 1, 1)) results.append(commodity) return results
def unwrap_entry(data: dict) -> bean.Directive: type, e = itemgetter("type", "entry")(data) meta = e.get("meta") date = parse_date(e["date"]) if type == "Open": return bean.Open( meta, date, account=e["account"], currencies=e.get("currencies", []), booking=e.get("booking"), ) if type == "Close": return bean.Close(meta, date, account=e["account"]) if type == "Commodity": return bean.Commodity(meta, date, currency=e["currency"]) if type == "Pad": return bean.Pad(meta, date, account=e["account"], source_account=e["source_account"]) if type == "Balance": return bean.Balance( meta, date, account=e["account"], amount=parse_amount(e["amount"]), tolerance=e.get("tolerance"), diff_amount=e.get("diff_amount"), ) if type == "Transaction": return bean.Transaction( meta, date, flag=e["flag"], payee=e.get("payee"), narration=e["narration"], tags=set(e["tags"] if "tags" in e else []), links=set(e["links"] if "links" in e else []), postings=[parse_posting(p) for p in e.get("postings", [])], ) if type == "Note": return bean.Note(meta, date, account=e["account"], comment=e.get("comment", "")) if type == "Event": return bean.Event(meta, date, type=e["type"], description=e["description"]) if type == "Query": return bean.Query(meta, date, name=e["name"], query_string=e["query_string"]) if type == "Price": return bean.Price(meta, date, currency=e["currency"], amount=parse_amount(e["amount"])) if type == "Document": return bean.Document( meta, date, account=e["account"], filename=e["filename"], tags=set(e["tags"] if "tags" in e else []), links=set(e["links"] if "links" in e else []), ) if type == "Custom": return bean.Custom(meta, date, type=e["type"], values=e["values"])
def Commodity(commodity, date): meta = meta_from(commodity, 'fullname') return data.Commodity(meta, date, commodity.mnemonic)
def Commodity(commodity, date): meta = meta_from(commodity, 'fullname') name = commodity_name(commodity) return data.Commodity(meta, date, name)
def DoTrade(txn, commodities): entry = CreateTransaction(txn, allow_fees=True) new_entries = [entry] # Add the common order id as metadata, to make together multi-leg options # orders. if 'orderId' in txn: match = re.match(r"([A-Z0-9]+)\.\d+", txn['orderId']) if match: order_id = match.group(1) else: order_id = txn['orderId'] entry.links.add('order-{}'.format(order_id)) # Figure out if this is a sale / clkosing transaction. item = txn['transactionItem'] inst = item['instrument'] assetType = inst.get('assetType', None) # It's a sale if the instruction says so. Pretty straightforward. is_sale = item.get('instruction', None) == 'SELL' # Add commodity leg. amount = DF(item['amount'], QO) if assetType in ('EQUITY', None): # Short sales will never have the 'CLOSING' flag but they will have a # 'SHORT SALE' description, which allows us to disambiguate them from # closing sales. Buys to close short sales will have a description of # 'CLOSE SHORT POSITION'. is_closing = txn['description'] in { 'CLOSE SHORT POSITION', 'SELL TRADE' } symbol = inst['symbol'] account = config['asset_position'].format(symbol=symbol) # TODO(blais): Re-enable this in v3 when booking code has been reviewed. # This triggers and error in booking, but that's how it should rendered. # is_closing = True elif assetType == 'OPTION': # Unfortunately, the 'CLOSING' flag isn't set consistently on the result # of the API (this would be very helpful if it were). It's only used # with options, and we only use it then. is_closing = item.get('positionEffect', None) == 'CLOSING' symbol = GetOptionName(inst, entry.date.year) account = config['asset_position'].format(symbol='Options') # Note: The contract size isn't present. If we find varying contract # size we could consider calling the API again to find out what it is. amount *= CSIZE # Open a new Commodity directive for that one option product. if not is_closing and symbol not in commodities: meta = data.new_metadata('<ameritrade>', 0) meta['name'] = inst['description'] if ADD_EXTRA_METADATA: meta['assetcls'] = 'Options' # Optional meta['strategy'] = 'RiskIncome' # Optional if 'cusip' in inst: meta['cusip'] = inst['cusip'] commodity = data.Commodity(meta, entry.date, symbol) commodities[symbol] = commodity new_entries.insert(0, commodity) else: assert False, "Invalid asset type: {}".format(inst) # Create number of units and price. units = Amount(amount, symbol) price = DF(item['price'], QP) if 'price' in item else None if is_sale: units = -units if is_closing: # If this is a closing trade, the price is the sales price and it needs # to be booked against a position. Set the price as price annotation, # not cost. cost = CostSpec(None, None, None, None, None, False) entry.postings.append(Posting(account, units, cost, Amount(price, USD))) else: # This is an opening transaction, so the price is the cost basis. cost = CostSpec(price, None, USD, entry.date, None, False) entry.postings.append(Posting(account, units, cost)) # Add fees postings. entry.postings.extend(CreateFeesPostings(txn)) # Cash leg. units = GetNetAmount(txn) entry.postings.append(Posting(config['asset_cash'], units)) # Add a P/L leg if we're closing. if is_closing: entry.postings.append(Posting(config['pnl'])) return new_entries
def Commodity(commodity, date): meta = meta_from(commodity, 'fullname') meta['gnucash_guid'] = commodity.guid name = commodity_name(commodity) return data.Commodity(meta, date, name)