Trading Strategies
Chapter: 4.6 Trading Strategies Difficulty: Level 5 — Mastery (cross-domain: math + probability + coding) Time limit: 90 minutes Total marks: 60
Instructions: Answer ALL questions. Show full working. Round monetary values to 2 decimals and probabilities to 4 decimals unless stated. Pseudocode or Python accepted for coding parts.
Question 1 — Opening Range Breakout, VWAP filter, and expectancy (24 marks)
A trader runs an Opening Range Breakout (ORB) system on a single stock with a VWAP confirmation filter.
Market data (first candles of the session):
| Time | Volume (shares) | Typical Price |
|---|---|---|
| 09:15–09:20 | 4,000 | 100.00 |
| 09:20–09:25 | 6,000 | 100.50 |
| 09:25–09:30 | 10,000 | 101.00 |
The opening range (OR) is defined over 09:15–09:30 with high and low .
(a) Compute the session VWAP at 09:30 from the table above. (4)
(b) A long breakout entry is triggered at 09:31 when price closes at (above ). State whether the VWAP filter (price must be above VWAP) confirms the trade, and justify numerically. (3)
(c) The stop is placed at the OR midpoint. The target uses a fixed reward:risk ratio of . Compute the stop price, the risk per share, the target price, and the reward per share. (5)
(d) Backtesting gives a win rate . Using the reward:risk of (winners = units of risk, losers = unit), derive the expectancy per trade in units of risk and state whether the system is profitable. (4)
(e) Prove algebraically the break-even win rate as a function of the reward:risk ratio , then evaluate for . Comment on the margin of safety of the system. (5)
(f) Position sizing: account equity is \50{,}0001%$ per trade. Compute the number of shares (round down to nearest whole share) using the risk-per-share from (c). (3)
Question 2 — Moving-average crossover: cross-domain derivation + code (22 marks)
Consider a price series and two simple moving averages, the fast (period ) and slow (period ), with .
(a) A golden cross occurs when the fast SMA crosses above the slow SMA. For a price series that is a pure linear uptrend (), prove that is a constant (independent of ), and derive that constant in terms of . Explain what this proves about crossover signals in a perfectly linear trend. (8)
(b) Given the closing prices: Compute and at the final bar (index 9, last value ). State whether the fast SMA is above or below the slow SMA at that bar. (6)
(c) Write a function (Python or clear pseudocode) crossover_signals(prices, n, m) that returns a list of signals: +1 on the bar where a golden cross first occurs, -1 on a death cross, 0 otherwise. Handle the warm-up period (bars before both SMAs exist) as 0. (8)
Question 3 — RSI reversal system: math + probabilistic edge (14 marks)
The RSI over period is:
(a) Over the last 14 bars the average gain is and the average loss is . Compute and . Is this an overbought (), oversold (), or neutral reading? (5)
(b) Derive the general condition on the ratio for which , and hence state the threshold value of that marks the overbought line. (4)
(c) A mean-reversion trader only takes long trades when . Historically such signals win of the time with reward:risk . Compute the expectancy in units of risk and state whether combining this with the RSI oversold filter is a positive-edge system. (5)
Answer keyMark scheme & solutions
Question 1
(a) VWAP (4 marks) Numerator (2 marks) Denominator (2 marks)
(b) VWAP filter (3 marks) Entry price ⇒ price is above VWAP, so the long filter confirms the trade. (3 marks: correct comparison + conclusion)
(c) Stop, risk, target, reward (5 marks) OR midpoint (stop). (1) Risk per share . (1) Reward per share . (1) Target . (2)
(d) Expectancy (4 marks) units of risk. (3) ⇒ profitable system (expected per trade). (1)
(e) Break-even win rate (5 marks) Set expectancy to zero: (3) For : . (1) Actual , giving a comfortable margin of safety (~13 percentage points above break-even). (1)
(f) Position sizing (3 marks) Risk budget = 1\%\times 50{,}000 = \500= \dfrac{500}{0.85} = 588.24 \Rightarrow 588$ shares (round down). (2)
Question 2
(a) Constant SMA difference in linear trend (8 marks) For , the SMA of period ending at (using bars ): (4 marks) Therefore: (2 marks) This is constant (no ). Since and , the difference is positive: the fast SMA sits a fixed distance above the slow SMA. Interpretation: in a perfectly linear trend the SMAs never cross — crossover signals arise only from curvature / changes in slope, not from steady trends. (2 marks)
(b) SMA computation (6 marks) Last 3 prices (indices 7,8,9): ⇒ . (3) Last 5 prices (indices 5–9): ⇒ . (2) ⇒ fast SMA is above slow SMA (bullish). (1)
(c) Code (8 marks)
def crossover_signals(prices, n, m):
def sma(seq, k, i):
return sum(seq[i-k+1:i+1]) / k
signals = [0] * len(prices)
warmup = m - 1 # need m bars for slow SMA
for i in range(len(prices)):
if i < warmup:
signals[i] = 0
continue
fast_now = sma(prices, n, i)
slow_now = sma(prices, m, i)
fast_prev = sma(prices, n, i-1)
slow_prev = sma(prices, m, i-1)
if fast_prev <= slow_prev and fast_now > slow_now:
signals[i] = 1 # golden cross
elif fast_prev >= slow_prev and fast_now < slow_now:
signals[i] = -1 # death cross
else:
signals[i] = 0
return signalsMarks: warm-up handling (2), correct prev-vs-now cross detection (4), golden/death labelling (2).
Question 3
(a) RSI computation (5 marks) . (2) (2) is between 30 and 70 ⇒ neutral. (1)
(b) Threshold g for RSI=70 (4 marks) (3) So marks the overbought line (avg gain = avg loss). (1)
(c) Expectancy of RSI-oversold long (5 marks) units of risk. (3) ⇒ positive-edge system; the RSI<30 filter produces a profitable expectancy. (2)
[
{"claim":"VWAP equals 100.65","code":"num=100*4000+100.5*6000+101*10000; den=4000+6000+10000; result = (Rational(num,den)==Rational(20130,200)) and (num/den==100.65)"},
{"claim":"ORB risk 0.85, reward 2.125, target 103.475","code":"stop=Rational('100.50'); entry=Rational('101.35'); risk=entry-stop; reward=Rational(5,2)*risk; target=entry+reward; result = (risk==Rational('0.85')) and (reward==Rational('2.125')) and (target==Rational('103.475'))"},
{"claim":"Expectancy R=2.5 p=0.42 is 0.47","code":"p=Rational(42,100); E=p*Rational(5,2)+(1-p)*(-1); result = (E==Rational(47,100))"},
{"claim":"Break-even win rate for R=2.5 is 2/7","code":"R=Rational(5,2); pstar=1/(R+1); result = (pstar==Rational(2,7))"},
{"claim":"Shares = floor(500/0.85) = 588","code":"import math; result = (math.floor(500/Rational('0.85'))==588)"},
{"claim":"SMA3=18 SMA5=17 at last bar","code":"p=[10,11,13,12,14,16,15,17,19,18]; s3=Rational(sum(p[7:10]),3); s5=Rational(sum(p[5:10]),5); result = (s3==18) and (s5==17)"},
{"claim":"RSI with RS=1.5 equals 60","code":"RS=Rational(120,80); RSI=100-100/(1+RS); result = (RSI==60)"},
{"claim":"g for RSI=70 equals 7/3","code":"g=symbols('g'); sol=solve(Eq(100-100/(1+g),70),g); result = (sol[0]==Rational(7,3))"},
{"claim":"RSI-oversold expectancy = 0.5","code":"E=Rational(60,100)*Rational(3,2)+Rational(40,100)*(-1); result = (E==Rational(1,2))"}
]