Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non-unit currency price #72

Open
upsuper opened this issue Jan 3, 2023 · 2 comments · May be fixed by #73
Open

Support non-unit currency price #72

upsuper opened this issue Jan 3, 2023 · 2 comments · May be fixed by #73

Comments

@upsuper
Copy link

upsuper commented Jan 3, 2023

For some of the commodities, quotes may not be on 1 unit of a currency. For example, this ETF on Yahoo is quoted in 0.01 GBP. If I put price: "GBP:yahoo/XSDR.L" in beancount file, the price fetched would be 100x larger than the actual price.

I've also considered to introduce a separate unit for it, e.g. CGBP, and assume it is 0.01 GBP, but the problem then becomes that there is no easy way to fetch the exchange rate for this artificial currency.

It would probably be better to extend the syntax and support non-unit currency price, for example, price: "0.01GBP:yahoo/XSDR.L", and convert the price accordingly.

@upsuper
Copy link
Author

upsuper commented Jan 3, 2023

A workaround, for now, is to create a custom price source like

from beancount.prices.source import SourcePrice
from beancount.prices.sources import yahoo


def div_100(sp: SourcePrice) -> SourcePrice:
    return SourcePrice(sp.price / 100, sp.time, sp.quote_currency)


class Source(yahoo.Source):
    def get_latest_price(self, ticker):
        source_price = super().get_latest_price(ticker)
        return div_100(source_price)

    def get_historical_price(self, ticker, time):
        source_price = super().get_historical_price(ticker, time)
        return div_100(source_price)

(Change the import module from beancount.prices to beanprice for the beanprice package.)

@SEIAROTg SEIAROTg linked a pull request Jan 3, 2023 that will close this issue
@ileodo
Copy link

ileodo commented Jul 30, 2023

can be archived by a plugin:

__copyright__ = "Copyright (C) 2023-2023  iLeoDo"
__license__ = "GNU GPLv2"

from typing import Dict
import collections
from decimal import Decimal
from beancount.core.data import Commodity, Currency, Price
import beancount.core.amount

__plugins__ = ("price_multiplier",)

import logging
logger = logging.getLogger("price_multiplier")
OverrideError = collections.namedtuple("CommodityError", "source message entry")


def price_multiplier(entries, unused_options_map, config_str=None):

    multiplier_map :Dict[Currency, Decimal] = {}
    for entry in entries:
        if isinstance(entry, Commodity):
            multiplier = entry.meta.get("multiplier", None)
            if multiplier:
                multiplier_map[entry.currency] = Decimal(multiplier)

    def transform(entry):
        if isinstance(entry, Price):
            if entry.currency in multiplier_map:
                override_entry = Price(entry.meta, entry.date, entry.currency, beancount.core.amount.mul(entry.amount, multiplier_map[entry.currency]))
                return override_entry
        return entry
        
    ret_entries = [
        transform(entry)
        for entry in entries
    ]
    errors = []

    return ret_entries, errors

in your bean file

plugin "plugins.price_multiplier"

1970-01-01 commodity ABCDEFG
    price: "GBP:yahoo/ABCDEFG"
    multiplier: 0.01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants