A helper lesson for the course "Build your own arbitrage bot". Lesson 11 talks about a 10 percent chance that an order fails, and lesson 15 says a z-score of 2 happens on about 2 days in 100. This page makes both statements concrete.
The head-on way: guess
Ask a person "how likely is it that the order fails?" and they say "pretty likely" or "not very". That cannot go into a program, and two people mean different things by it. The fix is a number between 0 and 1: 0 means never, 1 means always, 0.1 means 10 times out of 100.
Probability is a share you can count
Instead of thinking, count. Flip a virtual coin 1000 times and see how often it lands heads:
import random
random.seed(1)
flips = [random.random() < 0.5 for _ in range(1000)]
print(sum(flips) / 1000)
rolls = [random.randint(1, 6) for _ in range(60000)]
print([round(rolls.count(k) / 60000, 3) for k in range(1, 7)])
0.473
[0.167, 0.168, 0.165, 0.168, 0.168, 0.164]
The coin gave 0.473, not exactly 0.5: with only 1000 flips there is noise. The die, with 60 000 rolls, gives every face close to 1/6 = 0.1667. More trials, less noise. This is the idea behind the simulation in lesson 11: run the trade 10 000 times and count.
Two rules are enough for this course:
- Probabilities of all possible outcomes add up to 1. If an order fails with 0.1, it works with 0.9.
- For two independent things, multiply: both happen with probability a × b. Two orders, each working with 0.9, both work with 0.81.
The bell curve
Many quantities, such as heights of people or small daily noise in a price, pile up around an average, with fewer and fewer values far away. The picture is a bell, and the distribution is called normal. Two numbers describe it fully: the mean (the center) and the standard deviation (the width), both from the previous primer.
The useful fact is how much of the bell lies within a given number of standard deviations from the center:
from statistics import NormalDist
random.seed(1)
r = [random.gauss(0, 1) for _ in range(100000)]
for k in (1, 2, 3):
print(k, round(sum(abs(x) < k for x in r) / len(r), 4), round(2 * NormalDist().cdf(k) - 1, 4))
print(round(sum(x > 2 for x in r) / len(r), 4), round(1 - NormalDist().cdf(2), 4))
1 0.6804 0.6827
2 0.9538 0.9545
3 0.9973 0.9973
0.0228 0.0228
In each row the first number is the count from 100 000 random values, the second is the exact value from a formula. They agree. So about 68 percent of values are within 1 standard deviation of the mean, about 95 percent within 2, and 99.7 percent within 3. The last line says that the chance of being more than 2 above the mean is 0.0228, about 2 in 100.
That is where the z-score rule of lesson 15 comes from: z above 2 happens on about 2 days in 100 (and below −2 on 2 more), so it is a rare event worth a look.
Where the bell lies to you
Real prices have more extreme moves than the bell predicts, and the world can change so that an old average no longer holds. A z-score of 2 is rare in the picture, but a strategy that depends on it still needs a stop and a test on data it has not seen. Lesson 15 shows this.
Your exercise
- Run the code and compare with the lesson.
- Count by hand: an order works with 0.9, and a trade needs 3 such orders in a row. What is the chance all three work? Check with a simulation of 100 000 trials.
- Change
kto 1.5 and 2.5 and compare the counted and the exact share. - Take the bus delays from the previous primer. Is the 30 minute day rare under the bell curve with the mean and standard deviation of the other nine days?
What comes next
Lessons 11 and 15 are open for you now.