예제 #1
0
def join(holdings_list, features_map, keyfun):
    """Join a list of holdings with a vector of arbitrary features.

    This function joins a list of holdings with a features_map, where the join
    key between the two mappings is provided by a configuration callable
    'keyfun'. keyfun() is run on every holding, and the resulting key is looked
    up from features_map.

    All the values from features_map are first normalized to 100%, and when
    aggregating, the 'market_value' attribute of the holdings is multiplied by
    the normalized feature value.

    Args:
      holdings_list: A list of holdings.Holding instances.
      features_map: A dict of some key to a dict of features. The values are
        dicts of strings to a number.
      keyfun: A function that produces the join key from a Holding instance.
        The key is used to look up a feature vector from the features_map dict.
    Returns:
      A dict of labels (from the values of features_map) to a pair of: Decimal
      number of market_value amounts, and a list of corresponding scaled
      holdings.
    """
    # Get the full list of features.
    all_labels = set(label for features in features_map.values()
                     for label in features)
    features_total = {label: D('0') for label in all_labels}
    features_holdings = {label: [] for label in all_labels}

    # Normalize the feature vectors.
    norm_features_map = {
        key: normalize_features(features)
        for key, features in features_map.items()
    }

    # Accumulate the market value of each holding in buckets for each label.
    for holding in holdings_list:
        key = keyfun(holding)

        if key is None:
            logging.debug("Key not found: %s, %s, %s", holding.account,
                          holding.currency, holding.cost_currency)

        try:
            features = norm_features_map[key]
        except KeyError as exc:
            raise KeyError("Key '{}' not found in map {}".format(
                key, norm_features_map))
        for label, fraction in features.items():
            if not holding.market_value:
                continue
            scaled_holding = holdings.scale_holding(holding, D(fraction))
            features_total[label] += scaled_holding.market_value
            features_holdings[label].append(scaled_holding)

    return {
        label: (features_total[label], features_holdings[label])
        for label in all_labels
    }
예제 #2
0
 def test_scale_holding(self):
     test_holding = holdings.Holding(
         'Assets:US:Checking', D('100'), 'MSFT', D('54.34'), 'USD',
         D('5434.00'), D('6000.00'), D('60'), datetime.date(2012, 5, 2))
     expected_holding = holdings.Holding(
         'Assets:US:Checking', D('70.0'), 'MSFT', D('54.34'), 'USD',
         D('3803.80'), D('4200.00'), D('60'), datetime.date(2012, 5, 2))
     self.assertEqual(expected_holding, holdings.scale_holding(test_holding, D('0.7')))