Module E · Building Trading Systems - Chapter 24

Position Sizing & Trade Delays

Control how much you trade with SetPositionSize, and when fills happen with SetTradeDelays and SetOption.

Strategy
What you'll learn
  • ·SetPositionSize modes
  • ·Percent of equity vs fixed
  • ·spsShares / spsValue
  • ·SetTradeDelays explained
  • ·Initial equity & commissions
  • ·Margin & leverage

We can now enter, exit, and protect a trade with stops. Two questions remain before a backtest means anything: how much do we trade on each signal, and when exactly does the order fill? Get sizing wrong and a brilliant strategy can blow up; get the fill timing wrong and your backtest quietly cheats by buying at prices you could never have reached.

Good to know

Because AmiBroker's backtester is portfolio-aware, SetPositionSize can juggle sizing across a whole basket of symbols sharing one equity pool - not just one symbol at a time.

Both are one-line settings in AmiBroker, but they carry a lot of weight. This chapter covers SetPositionSize for the "how much", SetTradeDelays for the "when", and the account settings - starting capital, commissions and margin - that make the whole simulation honest.

SetPositionSize: how much to trade

SetPositionSize(amount, mode) tells the backtester how big each position should be. The second argument, mode, decides how the first one is read:

  • spsPercentOfEquity - amount is a percent of current equity. SetPositionSize(75, spsPercentOfEquity) puts 75% of your account into each trade. As the account grows, position size grows with it (compounding).
  • spsShares - amount is a fixed number of shares/contracts. SetPositionSize(100, spsShares) always buys 100 shares, whatever the price.
  • spsValue - amount is a fixed rupee value. SetPositionSize(100000, spsValue) puts Rs 1,00,000 into every trade regardless of account size.
  • spsNoChange - keep the previously set size; useful when you only want to change sizing on some bars.

Here is the sizing line from the author's 10 and 20 EMA Crossover.afl:

SetPositionSize(75, spsPercentOfEquity);   // risk 75% of equity per trade

To feel the difference, take the same Buy on a stock priced at Rs 1,318, with Rs 10,00,000 of equity. The three modes commit wildly different amounts:

Mode Setting Money committed Shares bought
spsShares SetPositionSize(100, spsShares) Rs 1,31,800 100
spsValue SetPositionSize(100000, spsValue) Rs 1,00,000 75
spsPercentOfEquity SetPositionSize(75, spsPercentOfEquity) Rs 7,50,000 569

Same signal, same price - three completely different trade sizes. spsShares buys a flat 100 shares whatever the cost; spsValue works backward from a fixed rupee budget (Rs 1,00,000 / Rs 1,318 is about 75 shares); and spsPercentOfEquity commits a slice of the whole account (75% of Rs 10,00,000 buys about 569 shares). That choice, more than the entry rule, often decides whether a system compounds smoothly or lurches.

Percent-of-equity vs fixed sizing

The choice of mode changes the shape of your equity curve, not just its height:

  • Percent of equity compounds. Winners make the next position bigger, so a good run snowballs - and a bad run shrinks positions, cushioning the fall. This is the realistic way most systematic traders size, and it is what makes a backtest curve bend upward exponentially.
  • Fixed shares or fixed value does not compound. Every trade is the same size, so the equity curve grows in a straight line. This is excellent for analysis - it shows the raw, per-trade edge of the system without the flattering curve of compounding masking a weak run.

A good habit while developing: test with a fixed size first to see the honest edge, then switch to percent-of-equity to see how it compounds.

Key idea

Sizing mode is not a detail - it changes the story the backtest tells. spsPercentOfEquity compounds and flatters; spsShares/spsValue stay flat and reveal the bare edge. Judge a new system on a fixed size first, then admire the compounding second.

SetTradeDelays: when orders fill

SetTradeDelays(buy, sell, short, cover) postpones each kind of fill by a whole number of bars. The four arguments map one-to-one to the four reserved arrays:

SetTradeDelays(1, 1, 1, 1);   // fill one bar AFTER each signal

This says: when a signal appears on a bar, do not fill on that bar - wait one bar and fill then (at that next bar's price). This models reality: you see the signal at the close, you act on the next bar.

There are two clean ways to get a realistic next-bar fill, and you must pick exactly one:

  1. Engine delay. Keep signals on the cross bar and set SetTradeDelays(1, 1, 1, 1). AmiBroker shifts the fills for you.
  2. Manual delay. Delay the signals yourself with Ref(..., -1) (Chapter 21) and set SetTradeDelays(0, 0, 0, 0) so the engine adds nothing.

Both land the fill on the next bar. What you must never do is both at once - that double-delays every trade.

Heads up

Decide your delay style once and stick to it. Manual Ref(..., -1) pairs with SetTradeDelays(0,0,0,0); engine delay pairs with signals left on the cross bar. Combining a manual Ref(-1) with SetTradeDelays(1,1,1,1) shifts every fill two bars late and silently corrupts the report.

When the fill lands on the next bar, remember to price it there too - BuyPrice = ValueWhen(Buy, Open) fills at that next bar's open, the honest price.

Starting capital and commissions

A backtest needs to know how much money it starts with and what trading costs. These are SetOption calls:

SetOption("InitialEquity", 1000000);     // start with Rs 10,00,000
SetOption("CommissionMode", 2);          // 2 = a flat amount per trade
SetOption("CommissionAmount", 20);       // Rs 20 per executed order

InitialEquity is your starting capital - it anchors every percentage in the report and is what spsPercentOfEquity sizes against. CommissionMode chooses how costs are charged: mode 1 reads CommissionAmount as a percentage of trade value, while mode 2 reads it as a flat fee per trade. Always include a realistic cost - a strategy that looks great at zero commission can turn unprofitable once brokerage and charges are deducted, especially a high-frequency intraday one.

Note

You can set these in the Settings dialog instead of in code (we open it next chapter). Putting them in the AFL has one big advantage: the assumptions travel with the formula, so anyone who runs your file gets the same starting capital and costs - no hidden settings to forget.

Margin and leverage

By default a backtest assumes you pay the full value of every position in cash. Futures and intraday trades use leverage - you post only a fraction as margin. AccountMargin models this:

SetOption("AccountMargin", 100);   // 100 = no leverage, fully funded

The number is the percentage of a position's value you must put up as margin. 100 means you fund trades fully (cash equities). 50 means you only post half, giving 2x leverage; 20 means a fifth, giving 5x. Lower margin lets the same capital control a bigger position - which magnifies both profit and loss, and deepens drawdowns. Set it to match the instrument you are actually trading: 100 for delivery equities, a smaller figure for index futures.

// A realistic intraday-futures account header
SetOption("InitialEquity", 1000000);
SetOption("CommissionMode", 2);
SetOption("CommissionAmount", 40);
SetOption("AccountMargin", 20);          // 5x leverage on futures
SetPositionSize(50, spsPercentOfEquity);
SetTradeDelays(0, 0, 0, 0);              // we delay signals with Ref(-1)

That header block is worth saving as a template: drop it at the top of any system and you have realistic starting capital, costs, leverage and sizing in six lines - the difference between a backtest you can trust and a flattering fantasy.

Try it yourself

  • Add SetPositionSize(100, spsShares) to your EMA system, backtest, then switch to SetPositionSize(75, spsPercentOfEquity) and compare the shape of the two equity curves.
  • Set SetOption("CommissionAmount", 0) then 100 and watch net profit fall - costs are not optional.
  • Change AccountMargin from 100 to 25 and observe how leverage stretches both the gains and the drawdown.
  • Deliberately set SetTradeDelays(1,1,1,1) while your signals already use Ref(..., -1), then revert. Note how the entry prices shift.

Recap

  • SetPositionSize(amount, mode) sets trade size: spsPercentOfEquity (compounds), spsShares (fixed count), spsValue (fixed rupees), spsNoChange (keep prior).
  • Percent-of-equity flatters with compounding; fixed sizing reveals the bare per-trade edge - use both while developing.
  • SetTradeDelays(buy, sell, short, cover) shifts fills by whole bars; pick either engine delay or manual Ref(..., -1), never both.
  • SetOption("InitialEquity", ...) sets starting capital; CommissionMode/CommissionAmount set realistic costs.
  • SetOption("AccountMargin", ...) models leverage - 100 is fully funded, lower numbers magnify gains, losses and drawdown.

Next, we put every one of these pieces to work - running our first full backtest in the Analysis window and reading the equity curve it produces.