Return an 2D array containing cls.expected_value(asset_id, date,
    colname) for each date/asset pair in the inputs.

    Missing locs are filled with 0 for volume and NaN for price columns:

        - Values before/after an asset's lifetime.
        - Values for asset_ids not contained in asset_info.
        - Locs defined in `holes`.

def expected_bar_values_2d(dates,
                           assets,
                           asset_info,
                           colname,
                           holes=None):
    """
    Return an 2D array containing cls.expected_value(asset_id, date,
    colname) for each date/asset pair in the inputs.

    Missing locs are filled with 0 for volume and NaN for price columns:

        - Values before/after an asset's lifetime.
        - Values for asset_ids not contained in asset_info.
        - Locs defined in `holes`.
    """
    if colname == 'volume':
        dtype = uint32
        missing = 0
    else:
        dtype = float64
        missing = float('nan')

    data = full((len(dates), len(assets)), missing, dtype=dtype)
    for j, asset in enumerate(assets):
        # Use missing values when asset_id is not contained in asset_info.
        if asset not in asset_info.index:
            continue

        start = asset_start(asset_info, asset)
        end = asset_end(asset_info, asset)
        for i, date in enumerate(dates):
            # No value expected for dates outside the asset's start/end
            # date.
            if not (start <= date <= end):
                continue

            if holes is not None:
                expected = expected_bar_value_with_holes(
                    asset,
                    date,
                    colname,
                    holes,
                    missing,
                )
            else:
                expected = expected_bar_value(asset, date, colname)

            data[i, j] = expected
    return data