Aside from normalization which scales values often 0 to 1, it can be useful to set some values proportionally to some other range of values. Scaling. It can move your code closer to being more gradually adaptive.
What are some examples where scaling might come in handy?
- Adjust number of stocks to trade proportionally based on available cash compared to starting_cash. With less cash, fewer and more cream-of-the-crop stocks.
- Increase price threshold of candidate stocks in before_trading_start() as portfolio increases.
- Set stops (to close any position) proportional to each stock's volatility.
- If portfolio is under starting_cash, progressively tighten amount of loss allowed per stock.
Below is an example of scale() using that last example to help progressively reduce the chances of an algo moving toward the 90% mark in the contest (where it would be disqualified).
pf is portfolio and prc is price here ...
For pf at .93 to 1, scale stop 1 (no wiggle room) to .97 (more loose),
that is, at pf .93 and below, stop is set to prc,
at pf 1 and above, stop is .97,
and with dropping pf toward .93 set stop proportionally tighter toward price.
Code something like this:
def set_stop(c, data, sec): # c is context, sec is security, stock
''' Set stop proportionally tighter with portfolio trending toward 93% of start.
scale(pf_ratio, .93, 1.0, 1.0, .97) Note low to high and inverse high to low
-------- --------
Portfolio range _______| |
Stop range ______________________|
'''
prc = data.current(sec, 'price')
amt = c.portfolio.positions[sec].amount
cb = c.portfolio.positions[sec].cost_basis
pf_ratio = c.portfolio.portfolio_value / c.portfolio.starting_cash
new_stop = prc
if amt > 0: # long
new_stop = max(cb, prc) * scale(pf_ratio, .93, 1.0, 1.0, .97)
elif amt < 0: # short, stop to buy back higher than prc
new_stop = min(cb, prc) * (2.0 - scale(pf_ratio, .93, 1.0, 1.0, .97))
return new_stop
def scale(wild, a_lo, a_hi, b_lo, b_hi):
''' Based on wild value relative to a_lo_hi range,
return its analog within b_hi_lo, with min b_lo and max b_hi
'''
return min(b_hi, max(b_lo, (b_hi * (wild - a_lo)) / (a_hi - a_lo)))
And use this to see straight.