Ejemplo n.º 1
0
    def test_get_all_prices(self, entries, _, __):
        """
        2013-06-01 price  USD  1.01 CAD
        2013-06-03 price  USD  1.03 CAD
        2013-06-05 price  USD  1.05 CAD
        2013-06-07 price  USD  1.07 CAD
        2013-06-09 price  USD  1.09 CAD
        2013-06-11 price  USD  1.11 CAD
        """
        price_map = prices.build_price_map(entries)
        price_list = prices.get_all_prices(price_map, ('USD', 'CAD'))
        expected = [(datetime.date(2013, 6, 1), D('1.01')),
                    (datetime.date(2013, 6, 3), D('1.03')),
                    (datetime.date(2013, 6, 5), D('1.05')),
                    (datetime.date(2013, 6, 7), D('1.07')),
                    (datetime.date(2013, 6, 9), D('1.09')),
                    (datetime.date(2013, 6, 11), D('1.11'))]
        self.assertEqual(expected, price_list)

        inv_price_list = prices.get_all_prices(price_map, ('CAD', 'USD'))
        self.assertEqual(len(price_list), len(inv_price_list))

        # Test not found.
        with self.assertRaises(KeyError):
            prices.get_all_prices(price_map, ('EWJ', 'JPY'))
Ejemplo n.º 2
0
def generate_price_pages(account_data_map: Dict[Account, AccountData],
                         price_map: prices.PriceMap, output_dir: str):
    """Produce renders of price time series for each currency.
    This should help us debug issues with price recording, in particulawr,
    with respect to stock splits."""

    pricer = returnslib.Pricer(price_map)

    # Write out a returns file for every account.
    os.makedirs(output_dir, exist_ok=True)
    pairs = set((ad.currency, ad.cost_currency)
                for ad in account_data_map.values()
                if ad.currency and ad.cost_currency)

    for base_quote in sorted(pairs):
        logging.info("Producing price page for %s", base_quote)
        all_prices = prices.get_all_prices(price_map, base_quote)
        if not all_prices:
            continue

        dates = np.array([date for date, _ in all_prices])
        prices_ = np.array([price for _, price in all_prices])

        fig, ax = plt.subplots(1, 1, figsize=[10, 4])
        set_axis(ax, dates[0], dates[-1])
        ax.set_title("Prices for {} ({})".format(*base_quote))
        ax.plot(dates, prices_, linewidth=0.5)
        ax.scatter(dates, prices_, s=2.0)
        fig.autofmt_xdate()
        fig.tight_layout()
        filename = path.join(output_dir, "{}_{}.svg".format(*base_quote))
        plt.savefig(filename)
        plt.close(fig)
Ejemplo n.º 3
0
def plot_prices(output_dir: str, price_map: prices.PriceMap,
                pairs: List[CurrencyPair]) -> Dict[str, str]:
    """Render one or more plots of prices."""

    # Group by quote currencies.
    outplots = {}
    series = collections.defaultdict(list)
    for c, qc in pairs:
        series[qc].append(c)

    fig, axs = plt.subplots(len(series),
                            1,
                            sharex=True,
                            figsize=[10, 2 * len(series)])
    if len(series) == 1: axs = [axs]
    for index, (qc, currencies) in enumerate(sorted(series.items())):
        ax = axs[index]
        for currency in currencies:
            price_points = prices.get_all_prices(price_map, (currency, qc))

            # Render cash flows.
            dates = [date for date, _ in price_points]
            prices_ = [float(price) for _, price in price_points]

            set_axis(ax, dates[0], dates[-1])
            ax.plot(dates, prices_, linewidth=0.3)
            ax.scatter(dates, prices_, s=1.2)

    fig.autofmt_xdate()
    fig.tight_layout()
    filename = outplots["price"] = path.join(output_dir, "price.svg")
    plt.savefig(filename)
    plt.close(fig)

    return outplots
Ejemplo n.º 4
0
    def prices(self, base, quote):
        """List all prices."""
        all_prices = prices.get_all_prices(self.price_map, (base, quote))

        if self._filters["time"]:
            return [(date, price) for date, price in all_prices
                    if self._filters["time"].begin_date <= date <
                    self._filters["time"].end_date]
        return all_prices
Ejemplo n.º 5
0
    def prices(self, base, quote):
        """List all prices."""
        all_prices = prices.get_all_prices(self.price_map, (base, quote))

        if self._filters['time']:
            return [(date, price) for date, price in all_prices
                    if (date >= self._filters['time'].begin_date
                        and date < self._filters['time'].end_date)]
        return all_prices
Ejemplo n.º 6
0
    def prices(self, base, quote):
        """List all prices."""
        all_prices = prices.get_all_prices(self.price_map, (base, quote))

        if self._filters['time']:
            return [(date, price) for date, price in all_prices
                    if self._filters['time'].begin_date <=
                    date < self._filters['time'].end_date]
        return all_prices
Ejemplo n.º 7
0
    def prices(self, base: str,
               quote: str) -> List[Tuple[datetime.date, Decimal]]:
        """List all prices."""
        all_prices = prices.get_all_prices(self.price_map, (base, quote))

        if self.filters.time:
            return [(date, price) for date, price in all_prices
                    if self.filters.time.begin_date <= date <
                    self.filters.time.end_date]
        return all_prices
Ejemplo n.º 8
0
    def prices(self, base: str,
               quote: str) -> list[tuple[datetime.date, Decimal]]:
        """List all prices."""
        all_prices = get_all_prices(self.ledger.price_map, (base, quote))

        if (self.filters.time and self.filters.time.begin_date is not None
                and self.filters.time.end_date is not None):
            return [(date, price) for date, price in all_prices
                    if self.filters.time.begin_date <= date <
                    self.filters.time.end_date]
        return all_prices
Ejemplo n.º 9
0
    def test_project(self, entries, _, __):
        """
        2013-06-01 price  USD      1.12 CAD
        2013-06-15 price  HOOL  1000.00 USD
        2013-06-15 price  MFFT   200.00 USD

        2013-07-01 price  USD      1.13 CAD
        2013-07-15 price  HOOL  1010.00 USD
        """
        price_map = prices.build_price_map(entries)

        # Check that a degenerate request does nothing.
        noop_price_map = prices.project(price_map, "USD", "USD")
        self.assertIs(noop_price_map, price_map)

        # Project and make sure the old price map hasn't been mutated.
        new_price_map = prices.project(price_map, "USD", "CAD")
        self.assertFalse(("HOOL", "CAD") in price_map)

        # Check that the prices were converted. Note that this also checks that
        # no price was synthesized at 2013-07-01 (see {c1bd24f8d4b7}).
        self.assertEqual([(datetime.date(2013, 6, 15), D('1120.0000')),
                          (datetime.date(2013, 7, 15), D('1141.3000'))],
                         prices.get_all_prices(new_price_map, ("HOOL", "CAD")))

        # Make sure the inverted database has been updated.
        self.assertEqual([(datetime.date(
            2013, 6, 15), D('0.0008928571428571428571428571429')),
                          (datetime.date(2013, 7, 15),
                           D('0.0008761938140716726539910628231'))],
                         prices.get_all_prices(new_price_map, ("CAD", "HOOL")))

        # Check constraint on currencies. {4bb702d82c8a}
        cons_price_map = prices.project(price_map, "USD", "CAD", {"MFFT"})
        self.assertTrue(("HOOL", "CAD") in new_price_map)
        self.assertFalse(("HOOL", "CAD") in cons_price_map)

        # Check that the resulting price mapis sorted.
        for prices_ in new_price_map.values():
            self.assertTrue(
                all(x <= y for x, y in zip(prices_[:-1], prices_[1:])))
Ejemplo n.º 10
0
    def test_project_missing(self, entries, _, __):
        """
        2013-06-15 price  HOOL  1000.00 USD
        2013-07-01 price  USD      1.12 CAD
        2013-07-15 price  HOOL  1100.00 USD
        """
        price_map = prices.build_price_map(entries)
        new_price_map = prices.project(price_map, "USD", "CAD")

        # Check that there haven't been conversions before a price was
        # available. {b2b23353275d}
        self.assertEqual([(datetime.date(2013, 7, 15), D('1232.0000'))],
                         prices.get_all_prices(new_price_map, ("HOOL", "CAD")))
Ejemplo n.º 11
0
    def test_project_collisions(self, entries, _, __):
        """
        2013-06-01 price  USD      1.12 CAD
        2013-06-15 price  HOOL  1000.00 USD
        2013-06-15 price  HOOL  1125.00 CAD
        """
        price_map = prices.build_price_map(entries)

        new_price_map = prices.project(price_map, "USD", "CAD")

        # Check that the original prices in the database were not overridden.
        # See {97a5703ac517}.
        self.assertEqual([(datetime.date(2013, 6, 15), D('1120.0000')),
                          (datetime.date(2013, 6, 15), D('1125.00'))],
                         prices.get_all_prices(new_price_map, ("HOOL", "CAD")))
Ejemplo n.º 12
0
 def get_date_rates(self, entries):
     if not self.args.commodity:
         self.parser.error(
             "Commodity pair must be specified (in BASE/QUOTE format)")
     if not re.match('{ccy}/{ccy}$'.format(ccy=amount.CURRENCY_RE),
                     self.args.commodity):
         self.parser.error(
             ('Invalid commodity pair "{}"; '
              'must be in BASE/QUOTE format').format(self.args.commodity))
     price_map = prices.build_price_map(entries)
     try:
         date_rates = prices.get_all_prices(price_map, self.args.commodity)
     except KeyError:
         self.parser.error("Commodity not present in database: {}".format(
             self.args.commodity))
     return date_rates
Ejemplo n.º 13
0
def compute_portfolio_values(
        price_map: prices.PriceMap,
        transactions: data.Entries) -> Tuple[List[Date], List[float]]:
    """Compute a serie of portfolio values over time."""

    # Infer the list of required prices.
    currency_pairs = set()
    for entry in transactions:
        for posting in entry.postings:
            if posting.meta["category"] is Cat.ASSET:
                if posting.cost:
                    currency_pairs.add(
                        (posting.units.currency, posting.cost.currency))

    first = lambda x: x[0]
    price_dates = sorted(itertools.chain(
        ((date, None) for pair in currency_pairs
         for date, _ in prices.get_all_prices(price_map, pair)),
        ((entry.date, entry) for entry in transactions)),
                         key=first)

    # Iterate computing the balance.
    value_dates = []
    value_values = []
    balance = Inventory()
    for date, group in itertools.groupby(price_dates, key=first):
        # Update balances.
        for _, entry in group:
            if entry is None:
                continue
            for posting in entry.postings:
                if posting.meta["category"] is Cat.ASSET:
                    balance.add_position(posting)

        # Convert to market value.
        value_balance = balance.reduce(convert.get_value, price_map, date)
        cost_balance = value_balance.reduce(convert.convert_position, "USD",
                                            price_map)
        pos = cost_balance.get_only_position()
        value = pos.units.number if pos else ZERO

        # Add one data point.
        value_dates.append(date)
        value_values.append(value)

    return value_dates, value_values