Python for Finance Chapter4~6

Python for Finance Chapter4~6

Chapter4和Chapter5内容比较简单,Chapter6介绍了Numpy包和Scipy包。

Chapter4: 13 Lines of Python to Price a Call option

一个BS看涨期权定价模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def CND(X):
from math import sqrt, exp, pi
(a1, a2, a3, a4, a5) = (0.31938153, -0.356563782, 1.781477937, -1.821255978, 1.330274429)
L = abs(X)
K = 1.0/(1.0+0.2316419*L)
w = 1.0 - 1.0/sqrt(2*pi)*exp(-L*L/2.)*(a1*K+a2*K*K+a3*pow(K,3)+a4*pow(K,4)+a5*pow(K,5))
if X < 0:
w = 1.0 - w
return w

def bs_call(S, X, T, r, sigma):
"""
BS看涨期权定价模型
:param S: 股票价格
:param X: 期权执行价格
:param T: 到期时间
:param r: 无风险利率
:param sigma: 股价波动率
:return: 看涨期权的理论价格
"""
from math import sqrt, log, exp
d1 = (log(S/X) + (r + sigma*sigma/2.)*T)/(sigma*sqrt(T))
d2 = d1 - sigma * sqrt(T)
return S * CND(d1) - X * exp(-r*T) * CND(d2)

bs_call(40, 42, 0.5, 0.1, 0.2)
Out[3]: 2.2777859030683096

更多关于累积正态分布函数计算的方法可以参考上面的一篇博客中的介绍。

Chapter5: Introduction to Modules

调用系统时间

1
2
import time as tt
tt.localtime()

查看包的位置

1
2
3
4
5
例如查看numpy包的位置:
import numpy as np
print(np.__file__)

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/numpy/__init__.py

Chapter6: Introduction to NumPy and SciPy

numpy使用示例

numpy的数组只能包含相同格式的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import numpy as np
x = np.array([[1,2,3], [4,5,6]]) # 两行三列的数组
np.size(x) # x中的元素总个数
Out[4]: 6
np.size(x, 1) # x的列数
Out[5]: 3
np.std(x) # x中所有元素的标准差
Out[6]: 1.707825127659933
np.std(x, 1) # 每行元素的标准差
Out[7]: array([ 0.81649658, 0.81649658])
x.sum() # x中所有元素的和
Out[8]: 21
np.random.rand(50) # 生成50个均匀分布的[0, 1]的随机数
Out[9]:
array([ 0.735929 , 0.48371909, 0.48319465, 0.80330098, 0.71204551,
0.45927359, 0.21042451, 0.34032225, 0.86585374, 0.9184681 ,
0.25244703, 0.4655904 , 0.573158 , 0.69078721, 0.28420257,
0.01175873, 0.37011963, 0.05603916, 0.95709586, 0.95434385,
0.69367399, 0.4001506 , 0.25661519, 0.81710797, 0.09105194,
0.71402868, 0.90230726, 0.64391132, 0.14300242, 0.57655021,
0.56516528, 0.78893125, 0.67095094, 0.14877558, 0.65462795,
0.82230541, 0.90485454, 0.57629818, 0.64061762, 0.5215908 ,
0.6283539 , 0.31299468, 0.78966549, 0.80825757, 0.22523762,
0.02748282, 0.26128921, 0.36114289, 0.14665284, 0.68495554])
np.random.normal(size = 100) # 生成100个正态分布的随机数
Out[10]:
array([-0.7786169 , -0.19875422, -0.40590406, -0.45937584, -0.95609522,
2.22147808, -0.48074239, -1.13044399, -0.08728271, 0.04701358,
-0.10057447, -0.00421842, -0.2200504 , 0.00709479, 0.39573333,
0.04569029, -0.3109542 , 2.09839644, -1.67404649, 1.49882489,
-0.12399919, 0.51649219, 0.36264737, 1.1300972 , -1.7555352 ,
-0.05032842, -0.32048993, -1.18079056, -1.67942396, 0.10823867,
1.55580051, 0.42273131, -1.77889866, -1.1721452 , 1.06275237,
-1.02741104, 0.81434569, -0.21687411, 2.00752145, -1.78950106,
-1.31966175, 1.85773124, -0.01305868, 1.03553374, 0.55989939,
-1.07668277, 2.52250637, -1.78675082, -0.51463924, 0.84409031,
0.11243911, 1.85509026, -0.74423967, -0.55736923, 0.0502106 ,
-1.13825231, 0.03538882, -1.19465868, -1.62242723, -1.14388286,
1.46096833, 1.60316355, 0.16674316, -1.34344703, 0.41835526,
-0.69601261, -0.19510063, -0.83536413, -0.31742093, 0.564493 ,
0.94157268, -0.4884695 , 1.43735333, 1.45317239, -1.00098718,
0.6469232 , 0.56245913, -0.09012203, 0.38660999, 1.64892469,
0.23480097, -0.59815152, 0.33332819, 0.19106819, -0.53014824,
0.08541082, -0.2381628 , 2.04182807, 0.69091784, -1.5004019 ,
-0.94285749, 1.70695792, -1.48779331, -1.38634819, 0.14300425,
-0.60616028, 0.16554427, -0.17268446, 0.17657647, 0.11291312])
np.array(range(0,100), float)/100 # 生成 0.00, 0.01, 0.02 ··· 0.99, 1.00的序列
Out[11]:
array([ 0. , 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08,
0.09, 0.1 , 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17,
0.18, 0.19, 0.2 , 0.21, 0.22, 0.23, 0.24, 0.25, 0.26,
0.27, 0.28, 0.29, 0.3 , 0.31, 0.32, 0.33, 0.34, 0.35,
0.36, 0.37, 0.38, 0.39, 0.4 , 0.41, 0.42, 0.43, 0.44,
0.45, 0.46, 0.47, 0.48, 0.49, 0.5 , 0.51, 0.52, 0.53,
0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6 , 0.61, 0.62,
0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7 , 0.71,
0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8 ,
0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89,
0.9 , 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99])
np.array([100, 0.1, 2], float) # 一个数组中包含的元素应该是同一个格式的
Out[12]: array([ 100. , 0.1, 2. ])
# 改变数组的元素格式
y = [1,2,3,20]
y = np.array(y, dtype = float)
y
Out[15]: array([ 1., 2., 3., 20.])

scipy包使用示例

NPV函数

1
2
3
4
5
6
7
8
9
10
import scipy as sp
cashflows = [-100, 50, 40, 20, 10, 50]
npv = sp.npv(0.1, cashflows) # 计算NPV
round(npv, 2)
Out[75]: 31.41
# scipy包中的npv函数是一个真实的NPV函数
c = [-100]
sp.npv(0.1, c)
Out[78]: -100.0
# 注意,实际上Excel中的NPV函数是个PV函数

PMT函数

30年、年息4.5%、总额250000的贷款,月付是多少?

1
2
3
pmt = sp.pmt(0.045/12, 30*12, 250000, when = 'end')
round(pmt, 2)
Out[83]: -1266.71

PV函数和FV函数

1
2
3
4
5
6
7
8
sp.pv(0.1, 1, 0, 100) ## 100块1年前的现值,pmt = 0
Out[85]: -90.909090909090907
sp.fv(0.1, 1, 0, 100) ## 100块1年后的终值,pmt = 0
Out[86]: -110.00000000000001
sp.pv(0.1, 2, 100, 0, when='end') ## 年金现值(默认期末支付)
Out[87]: -173.55371900826461
sp.pv(0.1, 2, 100, 0, when='start') ## 应付年金现值(期初年金)
Out[88]: -190.90909090909108

收益率的算术均值和几何均值

$$
Arithmetic\ mean = \frac{\sum_{i =1}^{n}R_i}{n}
$$
$$
Geometric\ mean = \left[ \prod_{i=1}^{n}(1+R_i) \right]^{1/n} - 1
$$

1
2
3
4
5
ret = sp.array([0.1, 0.05, -0.02])
sp.mean(ret) # 算术均值
Out[91]: 0.043333333333333342
pow(sp.prod(ret + 1), 1./len(ret)) - 1 # 几何均值
Out[92]: 0.042163887067679262

sp.unique函数:查找奇异值

1
2
3
sp.unique([2,3,4,4,4,6,6])
Out[94]: array([2, 3, 4, 6])
`

sp.median函数:中位数

1
2
sp.median([2,3,4,5,6,6,7,10])
Out[96]: 5.5

显示numpy和scipy中的所有函数

1
2
3
4
5
6
7
8
x = np.array(dir(np))
len(x)
Out[102]: 613
x[200:210]
Out[103]:
array(['diagonal', 'diff', 'digitize', 'disp', 'divide', 'division',
'divmod', 'dot', 'double', 'dsplit'],
dtype='<U25')

numpy包中的数组生成函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import numpy as np
# 生成10个0的数组
np.zeros(10)
Out[63]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
# 生成3行2列的数组
np.zeros((3,2), dtype=float)
Out[65]:
array([[ 0., 0.],
[ 0., 0.],
[ 0., 0.]])
# 生成4行3列全是1的数组
np.ones((4,3), dtype=float)
Out[67]:
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
# 生成0-9的序列
np.array(range(10), dtype=float)
Out[69]: array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
# 生成4维的单位数组
np.identity(4)
Out[71]:
array([[ 1., 0., 0., 0.],
[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]])
# 生成4维的单位数组
np.eye(4)
Out[73]:
array([[ 1., 0., 0., 0.],
[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]])
# eye的更多用法
np.eye(4, k = 1)
Out[75]:
array([[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.],
[ 0., 0., 0., 0.]])
np.eye(4, k = 2)
Out[76]:
array([[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
# from 1 to 19 interval 3
np.arange(1, 20, 3, float)
Out[78]: array([ 1., 4., 7., 10., 13., 16., 19.])
# 2x3的数组
np.array([[2,2,2],[3,3,3]])
Out[80]:
array([[2, 2, 2],
[3, 3, 3]])
# 生成一个类似的全为0的数组
np.zeros_like(np.array([[2,2,2],[3,3,3]]))
Out[82]:
array([[0, 0, 0],
[0, 0, 0]])
# 生成一个类似的全为1的数组
np.ones_like(np.array([[2,2,2],[3,3,3]]))
Out[84]:
array([[1, 1, 1],
[1, 1, 1]])

numpy包中的运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 加法
c1 = np.array([-100, 50, 20])
c2 = np.array([-80, 100, 120])
c1 + c2
Out[88]: array([-180, 150, 140])
# 乘法
x1 = np.array([[1,2,3],[4,5,6],[7,8,9]])
np.dot(x1, x1) # 点乘
Out[91]:
array([[ 30, 36, 42],
[ 66, 81, 96],
[102, 126, 150]])
x1*x1 # 叉乘
Out[92]:
array([[ 1, 4, 9],
[16, 25, 36],
[49, 64, 81]])
# 数组与矩阵之间的转换
np.matrix(x1)
Out[94]:
matrix([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
np.array(np.matrix(x1))
Out[95]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 一些函数
x = np.array([[1,2,3],[4,5,6],[7,8,9]])
## 下面两种方式是等价的
x.sum()
Out[99]: 45
np.sum(x)
Out[100]: 45
x.min()
Out[101]: 1
np.min(x)
Out[102]: 1
x.mean()
Out[103]: 5.0
x.var()
Out[104]: 6.666666666666667
x.argmin() # 返回最小值的位置
Out[105]: 0
x.argmax()
Out[106]: 8
x.clip(min = 2) # 设定最低门限,低于门限值的都会被替换成门限值
Out[107]:
array([[2, 2, 3],
[4, 5, 6],
[7, 8, 9]])
x.clip(max = 3) # 设定最高门限,高于门限值的都会被替换成门限值
Out[108]:
array([[1, 2, 3],
[3, 3, 3],
[3, 3, 3]])
x.diagonal() # 返回x的对角线组成的数组
Out[109]: array([1, 5, 9])
x.tolist() # 返回列表
Out[110]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x.transpose() # 转置
Out[111]:
array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
x.flatten() # 返回x的元素组成的列数组
Out[112]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
c = np.array([-100, 30, 50, 100, 30, 40])
c.min()
Out[114]: -100
c.argmin()
for cash in c:
print(cash)
Out[113]:
-100
30
50
100
30
40

使用scipy包中的累积正态分布函数重新编写BS期权定价公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# scipy中的累积正态分布函数
from scipy.stats import norm
norm.cdf(0)
# 重新编写BS期权定价公式
from scipy import log, exp, sqrt, stats
def bs_call_new(S = 40, X = 40, T = 1, r = 0.03, sigma = 0.2):
"""
BS看涨期权定价公式
BS看涨期权定价模型
:param S: 股票价格,默认为40
:param X: 期权执行价格,默认为40
:param T: 到期时间,默认为1
:param r: 无风险利率,默认为0.03
:param sigma: 股价波动率,默认为0.2
:return: 看涨期权的理论价格
"""
d1 = (log(S/X) + (r + sigma*sigma/2.)*T)/(sigma*sqrt(T))
d2 = d1 - sigma*sqrt(T)
return (S*stats.norm.cdf(d1) - X*exp(-r*T)*stats.norm.cdf(d2))
bs_call_new()
Out[118]: 3.7653613535412056

numpy数组中的逻辑运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 数组中的逻辑运算
import numpy as np
x = np.array([True, False, True, False], bool)
any(x) # True if any element in x is True.
Out[122]: True
all(x) # True if all elements in x is True.
Out[123]: False
c = np.array([-100, 50, 40, 30, 100, -5])
c > 0
Out[125]: array([False, True, True, True, True, False], dtype=bool)
np.logical_and(c > 0, c < 60)
Out[126]: array([False, True, True, True, False, False], dtype=bool)
# 选择c中于0的子数组
c[(c > 0)]
Out[128]: array([ 50, 40, 30, 100])

scipy包中的统计量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from scipy import stats
import numpy as np
len(np.array(dir(stats)))
Out[132]: 246
# 设定种子产生正态随机数
np.random.seed(124)
x = np.random.normal(0, 1, 100)
# 计算skewness
stats.skew(x)
Out[137]: -0.22970945459723696
# 检验均值为0的t统计量和p值
stats.ttest_1samp(x, 0)
Out[139]: Ttest_1sampResult(statistic=1.1763312806843913, pvalue=0.24228334454436026)
# 从均匀分布中取值
np.random.uniform(low = 0.0, high = 1.0, size = 100)
Out[141]:
array([ 0.19275227, 0.00719085, 0.60879808, 0.34413601, 0.46909886,
0.41924856, 0.70729132, 0.19932869, 0.53038474, 0.65138402,
······
0.72680799, 0.7399658 , 0.61201165, 0.47996572, 0.04460141,
0.38995655, 0.71368747, 0.54200784, 0.91374774, 0.01945046,
0.87729023, 0.63369815, 0.94858029, 0.5828617 , 0.25943912])
np.random.randint(low = 1, high = 500, size = 20)
Out[142]:
array([232, 434, 258, 32, 211, 112, 326, 265, 71, 301, 193, 474, 240,
161, 479, 438, 75, 314, 454, 391])

scipy包中的插值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
x = np.linspace(0, 10, 10) # 0-10 长度为10
x
y = np.exp(-x/3.0)
f = interp1d(x, y)
f2 = interp1d(x, y, kind = 'cubic')
# help(interp1d)
# kind可选项 'linear', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic'
xnew = np.linspace(0, 10, 40)
plt.plot(x, y, 'o', xnew, f(xnew), '-', xnew, f2(xnew), '--')
plt.legend(['data', 'linear', 'cubic'], loc = 'best')
plt.show()

Figure_1.png

使用scipy包解方程

$$\left\{
\begin{array}{lr}
& x +2y+5z = 10 \\
& 2x+5y+z = 8 \\
& 2x+3y+8z =5 \\
\end{array} \right.
$$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import scipy as sp
import numpy as np
A = sp.mat('[1 2 5; 2 5 1; 2 3 8]')
b = sp.mat('[10; 8; 5]')
## A.I表示矩阵A的逆
A.I*b
Out[148]:
matrix([[-22.45454545],
[ 10.09090909],
[ 2.45454545]])
## numpy包中也提供了解方程的函数
np.linalg.solve(A, b)
Out[150]:
matrix([[-22.45454545],
[ 10.09090909],
[ 2.45454545]])

随机数种子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import scipy as sp
sp.random.rand(10) # 10 random numbers from [0, 1]
Out[152]:
array([ 0.30753252, 0.18513044, 0.65942764, 0.46920167, 0.52875454,
0.16858862, 0.00986553, 0.69832679, 0.4512412 , 0.75672126])
sp.random.rand(5, 2) # random numbers 5 by 2 array
Out[153]:
array([[ 0.88412845, 0.95585649],
[ 0.28965947, 0.36676156],
[ 0.21277316, 0.20550993],
[ 0.43206176, 0.69406982],
[ 0.95576823, 0.72806124]])
sp.random.normal(size=100)
Out[154]:
array([ -8.91784557e-01, 1.66045973e+00, 4.24440411e-01,
-1.40459412e+00, -7.24540377e-01, 3.79290558e-01,
······
8.83488474e-01, -2.18699157e-01, -1.05690114e+00,
-1.45189444e+00, 8.74852067e-02, -1.18728511e+00,
2.91119440e-01])
sp.random.seed(123456)
sp.random.rand(5)
Out[156]: array([ 0.12696983, 0.96671784, 0.26047601, 0.89723652, 0.37674972])

从一个包中搜索一个函数

1
2
3
4
5
6
7
8
9
import numpy as np
x = np.array(dir(np))
for k in x:
if (k.find("uni") != -1):
print(k)
unicode
unicode_
union1d
unique

最优化

1
2
3
4
5
6
7
8
9
import scipy.optimize as spop
def my_f(x):
return 3 + x**2
spop.fmin(my_f, 5) # 5是初始值
Optimization terminated successfully.
Current function value: 3.000000
Iterations: 20
Function evaluations: 40
Out[160]: array([ 0.])

CAPM

1
2
3
4
5
6
7
8
9
10
11
12
from scipy import stats
stock_ret = [0.065, 0.0265, -0.0593, -0.001, 0.0346]
mkt_ret = [0.055, -0.09, -0.041, 0.045, 0.022]
beta, alpha, r_value, p_value, std_err = stats.linregress(stock_ret, mkt_ret)
print(beta, alpha)
0.507743187877 -0.00848190035246
stats.linregress(stock_ret, mkt_ret)
Out[168]: LinregressResult(slope=0.50774318787708084, intercept=-0.0084819003524623845, rvalue=0.38455905003823676, pvalue=0.52271552390894616, stderr=0.70367055432215686)
print("R^2 =" , r_value**2)
R^2 = 0.147885662966
print("p-value =", p_value)
p-value = 0.522715523909

读取文本文件

我自己做了个平安银行过去40天的股价数据文件pingan.csv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
f = open("pingan.csv", "r")
data = f.readlines()
type(data)
Out[174]: list
data
Out[175]:
['stkcd,date,stknme,clsprc,hiprc,lowprc,opnprc,rit,turnover,volume,transaction,tcap,mcap\n',
'1,2018-04-23,pingan,11.57,11.61,11.26,11.3,.019383,.63260001,10.7028935,1228544000,198661659505,195741343744\n',
······
'1,2018-06-20,pingan,9.9099998,9.9499998,9.7600002,9.8699999,.004053,.4447,7.6363731,752950720,170158776637,170157229240\n']
import numpy as np
np.loadtxt("pingan.csv", delimiter=",") ## 这个出错,没能找到原因
# genfromtxt()函数会把非标准值例如3.5%视为NA
np.genfromtxt("pingan.csv", delimiter=",")
Out[179]:
array([[ nan, nan, nan,
nan, nan, nan,
nan, nan, nan,
nan, nan, nan,
nan],
[ 1.00000000e+00, nan, nan,
1.15700000e+01, 1.16100000e+01, 1.12600000e+01,
1.13000000e+01, 1.93830000e-02, 6.32600010e-01,
1.07028935e+01, 1.22854400e+09, 1.98661660e+11,
1.95741344e+11],
······
[ 1.00000000e+00, nan, nan,
9.90999980e+00, 9.94999980e+00, 9.76000020e+00,
9.86999990e+00, 4.05300000e-03, 4.44700000e-01,
7.63637310e+00, 7.52950720e+08, 1.70158777e+11,
1.70157229e+11]])

# Python

评论

程振兴

程振兴 @czxa.top
截止今天,我已经在本博客上写了659.4k个字了!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×