@Paul Thank you for the clarification. I was going to post the same suggestion as @Joakim before he beat me to it. Knowing that you want an asset list passed to a custom factor (which may or may not be the same as the mask) helped.
You were correct in simply passing a filter to your custom factor. The filter will be passed to the compute function as an ndarray exactly like all other factor inputs. The values will be either True or False depending if the asset is included, or not included, in the filter. It seems the question then is how to use this data?
As an example, assume the following filter we want passed to a custom factor called 'CompareAssets'.
# Filter for specific assets to pass to the custom factor
# Could be the result of some calculation like '.top(10) but just using a static filter for testing
asset_list = StaticAssets(symbols(['IBM', 'C']))
# Some data to pass to our custom factor
factor_input = DailyReturns()
# Optional mask for universe to pass to the custom factor.
# Could be something big like QTradableStocksUS but made it small here for testing
factor_universe = StaticAssets(symbols(['IBM', 'AAPL', 'C', 'ARNC']))
# Create our custom factor. Could add more inputs if desired
# Don't necessarily need a mask but can include if desired
compare = CompareAssets(inputs=[asset_list, factor_input], mask=factor_universe)
Notice we instantiate our custom factor normally and simply pass a filter as one of the inputs. Could pass more than one filter if desired. The filter shows up as a 2D numpy array exactly like any other input with rows for each day and columns for each asset. However, we don't really want a 2D array of True/False values (the values will be all the same for every day anyway). We want a simple 1D array. There are several ways to accomplish this but maybe use the numpy 'all' method. Here is an example.
class CompareAssets(CustomFactor):
"""
Custom factor to demonstrate using a filter passed as an input to a factor.
The results of the filter can be used to select and slice other inputs and used in calc.
"""
# Window length can be anything as needed
window_length = 1
def compute(self, today, assets, out, my_filter, input_a):
# Let's get a series of True/False values indicating which assets are in my_filter.
# Remember that my_filter is a 2D array with assets for columns and days for rows.
# We probably want to know assets having True values for all days so use the 'all' method.
my_assets = my_filter.all(axis=0)
So far pretty straightforward. Now, how to use this info inside the custom factor? Numpy boolean indexing to the rescue! One can easily slice and select from numpy arrays using a True/False (ie boolean) list. In general this is called 'boolean-array-indexing' (see https://numpy.org/devdocs/reference/arrays.indexing.html#boolean-array-indexing) Below are just a few of the ways to select certain assets from the inputs to use in calculations inside a custom factor. Continuing the code from above...
# Select SIDs for just assets in my_filter
my_assets_sids = assets[my_assets]
# Get input values just for these assets
inputs_for_my_assets = input_a[:, my_assets]
# Get input values just for assets NOT in my_assets
inputs_not_in_my_assets = input_a[:, ~my_assets]
# Sum all the input values for all assets
sum_input_all = input_a.sum()
# Sum input values for just my_assets
sum_input_my_assets = inputs_for_my_assets.sum()
# Sum input values for all assets NOT in my_assets
sum_input_not_my_assets = inputs_not_in_my_assets.sum()
One typically doesn't need to access the actual SIDs but it's sometimes helpful for debugging. Most important are the input values. Knowing how to select specific values using boolean indexing is key. One can then craft calculations based upon these values.
Attached is a demo notebook. Specifically look at the output of the pipeline. There are print statements inside the custom factor which print out when the pipeline is run. These print the results of the various slicing operations to visually show what's being selected.
Hope this helps?