I assume your concern about turnover is that it would get too low?
If not, applying a 21-day SMA on your alpha factor might do the trick.
What often worked for me without changing turnover rates is to store the weights and trade them later.
In this case I would make a list in initialize like:
context.wts = []
In the function where I normally would trade them, I store the weights in the list. And if the list is as long as the prefered delay (assuming daily rebalancing), I just take out the 0th element:
current_wts = what_my_alpha_factor_just_procuced
context.wts.append(current_wts)
if len(context.wts) > 21:
wts_to_trade_now = context.wts.pop(0)
Then order with the wts_to_trade_now.
Of course there are a few concerns about this:
First of all, I don't know if this approach would be considered as a bit hacky. But then again it's not so different from what Q does with the contest algos...
And allthough this works often, it doesn't always produce the desired effect (dont't ask me why, personally I think it should always work...).
Finnaly, what will we trade in the first 21 days? The current weights? Then there will very likely be a turnover spike on day 21. Nothing? This will mean a zero turnover rate for that time. In both cases you might get problems with the contest criteria. You could try to ease them in, like adding them to the current weights with slowly increasing weights. So far I wasn't able to get rid of the spike completely. However, I haven't tried this in the contest and if the spike is not too high you might get away with it...
I hope this helps!
Any feedback and comments are appreciated.