예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
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"])
예제 #4
0
def Commodity(commodity, date):
    meta = meta_from(commodity, 'fullname')

    return data.Commodity(meta, date, commodity.mnemonic)
예제 #5
0
def Commodity(commodity, date):
    meta = meta_from(commodity, 'fullname')
    name = commodity_name(commodity)

    return data.Commodity(meta, date, name)
예제 #6
0
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
예제 #7
0
def Commodity(commodity, date):
    meta = meta_from(commodity, 'fullname')
    meta['gnucash_guid'] = commodity.guid
    name = commodity_name(commodity)

    return data.Commodity(meta, date, name)