Thanks for the content to work on. I see flaws in your usage but that doesn't mean you aren't onto something to look into, meaning, I think nanfill() is behaving ok normally but my hunch is that it could be improved for extreme cases, I'll explain at the end.
Quite a few points to make:
1. In the custom factor, feed it a good window_length so it has something to forward fill from, rather than 1 for window_length.
2. Inputs to the custom factors shouldn't be .latest. Instead like below.
3. Do nanfill() first thing in compute, before any calculations.
4. Then after nanfill(), can do the calculations using [-1] for example, or [-3:].mean() or whatever, and if the window length is long enough to go back far enough to the point where there is a value, then all nans beyond it to the end should become that value.
Something along this line ...
class Capex_To_Cashflows(CustomFactor):
inputs=[cfs.capital_expenditure, cfs.free_cash_flow]
window_length=10
def compute(self, today, assets, out, capital_expenditure, free_cash_flow):
out[:] = (capital_expenditure[-1] * 4.) / (free_cash_flow[-1] * 4.)
class Capex_To_Cashflows_forwardfill(CustomFactor):
inputs=[cfs.capital_expenditure, cfs.free_cash_flow]
window_length=10
def compute(self, today, assets, out, capital_expenditure, free_cash_flow):
capital_expenditure = nanfill(capital_expenditure)
free_cash_flow = nanfill(free_cash_flow)
out[:] = (capital_expenditure[-1] * 4.) / (free_cash_flow[-1] * 4.)
#out[:] = nanfill((capital_expenditure * 4.) / (free_cash_flow * 4.))
With the change made, as you can see, the default window_lengths are set to 10, however in this case I'm feeding it an override when they are called, where window_length is set to 120, and the outputs seemed to be reasonable the first time I ran that, all different. But there's a fly in the ointment. When I had tried window_length=30, there were two stocks with--not the value of the one before them--but instead with the same value although separated from each other (BCE and BNS). As if, when it fails from no value to forward fill with, it has some information saved and just uses that instead, like in mask or something, I don't know. Then alongside the fly there's an elephant. On rerunning with 120, it was back to the same problem as with 30. Odd. This is a job for a Dan Whitnable or something.