1UNIVERSE = agri, energy and metal futures, FX, equity indices
2LOOKBACK = 12 months
3MIN_OBS = 10
5function positions(bar):
6 for symbol in UNIVERSE:
7 history = returns(symbol, LOOKBACK, ending bar)
9 if count(history) < MIN_OBS:
10 continue
12 direction = sign(sum(history)) # up over the year, or down
13 vol = stdev(returns(symbol, 36 months, ending bar.prev))
14 w[symbol] = direction / vol
16 return w
18# ── execution ────────────────────────────────────────────────────────
20COST_PER_TRADE = 15 bps of the notional that changes hands
22function on_bar(bar, book):
23 if not bar.is_month_end:
24 return # decisions are made monthly only
26 target = positions(bar) # the rule above
27 target = scale_to_gross(target, 1.0)
29 rebalance(book, target, bar)
31function scale_to_gross(w, limit):
32 gross = sum(abs(w)) # total capital at work
34 if gross == 0:
35 return w # flat is a valid target
37 return w * limit / gross # leverage fixed, never implicit
39function rebalance(book, target, bar):
40 for symbol in union(book.symbols, target.symbols):
41 delta = target[symbol] - book.weight(symbol)
43 if delta > 0:
44 buy(symbol, delta, fill = next_bar.close)
45 else if delta < 0:
46 sell(symbol, -delta, fill = next_bar.close)
48 book.charge(COST_PER_TRADE * abs(delta))
50# Orders are filled at the next month's close, never the one the decision
51# was made on. There is no stop_loss(), take_profit(), limit order or
52# position cap: a holding changes only when positions() returns a
53# different target at the next month end.