pydata

Keep Looking, Don't Settle

买入股票时,买入价至少设为多少才能保证有九成的把握在当天成交?

这是我觉得很好的一篇知乎文章,原作者不仅简明扼要的把一个问题转化为数学问题,把书本上关于Ito积分应用到现实问题中,而且给出了一个Simulation解,做到了让人真正明白是怎么回事。

原文参见知乎。以下摘自原文。

原始问题是:买入股票时,买入价至少设为多少才能保证有九成的把握在当天成交?假定股价服从布朗运动。下单后不可修改。

下面完全是引用的最高票答案:

既然题目里面说了Brownian Motion,我只说说从金融数学的角度怎么处理这个问题,至于行为经济学、市场文化学这些太抽象,相关答案很多,我就不多嘴了。现在欢迎大家来到Q-measure(Risk-neutral Probability measure)。

这是程序做市(Electronic Market Making)里面常见的一个问题,不过这些假设太粗糙了,必须要加的一个条件是:购买股票,下限价订单(Limit order)——否则如同大部分答案所说——假设是市场订单(Market Order),那成交概率是100%。

如同游戏先要弄懂如何打金刷怪一样,数学没有前提条件做啥都是白瞎。前提假设先说清楚——所有市场无套利的假设全部装备,假设初始股价\(S_0\),股票年化波动性\(\sigma\),时间\(\frac{1}{252}\),由于一天之内,就不考虑分红了\(d = 0\),利率也没有影响\(r = 0\)。我们要求\(S_{bid}\),满足:\(P\left(\inf\left\{ S_t \lvert ~ t \in (0, T) \right\} \leq S_{bid}\right) = 90\%\),即股价在一天之内的下界小于等于的概率等于90%。因为此概率对于\(S_{bid}\)单调递增,故由唯一解。原问题可以两种做法,解析法和模拟法。

1. 解析法:

设符合条件的买入价为\(S_{bid} \leq S_0\),根据假设,股价服从Geometric Brownian Motion:

$$ \begin{align} \frac{dS_t}{S_t} = rdt + \sigma dW = \sigma dW ~~~~ (w.r.t. r = 0) \end{align} $$

因此,根据Ito's Calculus,我们有:

$$ \begin{align} \ln S_t = \ln S_0 + \left(- \frac{1}{2} \sigma^2 \right) t + \sigma W_t \end{align} $$

变形,易得:

$$W_t = \frac{ln \frac{S_t}{S_0} + \frac{1}{2}\sigma^2 t }{\sigma}$$

定义:

$$m(t) = \inf\left\{ \ln S_v \lvert ~ 0 \leq v \leq t \right\}$$

那么\(m(t)\)就是0到t时刻中对数股价的下界。由于对数函数的单调递增性质,我们的目标转化为:

$$P\{m(T) \leq S_{bid}\} = 90\%$$

由于我们有:

$$\ln S_t \sim N\left(\ln S_0 - \frac{1}{2} \sigma^2 T ,~~ \sigma^2 t\right)$$

我不加证明的给出\(m(t)\)的累积分布函数(CDF)如下,证明需要利用布朗运动的反射性质(Reflection Principle),有兴趣的可以要我补充:

$$ \begin{align} & \mu = -\frac{1}{2}\sigma^2, \alpha = \ln S_0 \\ & F_{m(t)}(x) = N\left(\frac{x - \alpha - \mu t}{\sigma \sqrt{t}}\right) + \exp \left(2 \frac{\mu(x - \alpha)}{\sigma^2}\right) N\left(\frac{x - \alpha + \mu t}{\sigma \sqrt{t}}\right) \end{align} $$

那么利用这一函数的反函数,分分钟搞定这个问题的解析解:

$$S_{bid} = \exp \left(F_{m(T)}^{-1}( 90\% )\right)$$

如有谬误,欢迎指出。这里不妨看看解析解对应的函数曲线(假设\(S_0 = 100, \sigma = 0.3\)):

由于我们恒有\(m(t)\leq S_0\),故\(F_{m(T)}(S_0)=100\%\)。而根据解析解,我们有: \(\exp\left(F^{-1}_m(T)(90\%)\right)=99.77\)。即在开盘价99.77%的水平下单,可以满足条件。

\(F_{m(T)}\)的分布函数不是很好反解。模拟了一下它的结果:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import scipy.stats as st
import seaborn as sns
%matplotlib inline
plt.rcParams['figure.figsize'] = (9, 6)
mu = -0.5 * 0.3**2
alpha = np.log(100)
capt = 1/252.0

x = np.log(99.761)
p = st.norm(alpha + mu*capt, 0.3*np.sqrt(capt)).cdf(x) + np.exp(2 * mu * (x - alpha) / 0.3**2) * st.norm(alpha - mu*capt, 0.3*np.sqrt(capt)).cdf(x)

res = []
xrange = np.arange(94, 100, 0.001)
for i in xrange:
    x = np.log(i)
    v = st.norm(alpha + mu*capt, 0.3*np.sqrt(capt)).cdf(x) + np.exp(2 * mu * (x - alpha) / 0.3**2) * st.norm(alpha - mu*capt, 0.3*np.sqrt(capt)).cdf(x)
    res.append(v)


plt.plot(np.arange(94, 100, 0.001), res)
plt.axvline(x = 99.77, color = '#33b5e5', linestyle = '--', label = "90% percentile: 99.761")
plt.axhline(y = 0.9, color = '#00C851', linestyle = '--', label = "90% Percent")
plt.legend(loc = 3)
plt.title("Distribution of $F_{m(T)}(x)$")
plt.show()

res_s = pd.Series(res)

ss = pd.Series(xrange)
fx = ss[res_s[(res_s-0.9<0.0005) & (res_s-0.9>0)].index[0]]
print("The 90th quantile is %.4f" %(fx) )

png

The 90th quantile is 99.7610

2. 模拟法:

简单来说就是做一次Monte Carlo的模拟,然后模拟N次股票的轨迹,取每个轨迹的下尾90%分位点.直接模拟\(\ln S_t = \ln S_0 + \left(- \frac{1}{2} \sigma^2 \right) t + \sigma W_t\),其中\(W_t\)是布朗运动。

S0 = 100
sigma = 0.3
mu = 0
deltaT = 1.0/252
N = 10000
M = 200
barrier = 0.45

S_q = []
for i in range(N):
    ts = np.arange(M)/float(M)
    St = S0 * np.exp((mu - 1/2.0*sigma**2)*ts*deltaT + sigma * np.sqrt(deltaT / M) * np.cumsum(np.random.normal(0, 1, M)))
    S_q.append(min(St))

dis = pd.Series(S_q, name = "S_q")

# dis.plot(kind = "density")
# plt.show()

# density = stats.kde.gaussian_kde(dis)
# xs = np.linspace(min(dis), max(dis), 200)

# plt.plot(xs, density(xs))
# plt.show()

sns.distplot(dis, bins = 500)
plt.axvline(x = np.percentile(dis, 90), color = '#00C851', label = "90% cutoff")
plt.legend(loc = 2)
plt.title("Density function of ")

np.percentile(dis, 90)
99.841974221885323

png