第13章 面向对象和图形用户界面
13.1 面向对象
13.1.1 Python类基础知识
class ExampleOne(object):
pass
class ExampleTwo(object):
def __init__(self, a, b):
self.a = a
self.b = b
c = ExampleTwo(1, 'text')
c.a
# 1
c.b
# 'test'
c.a = 100
c.a
# 100
# 对象的属性可以在实例化之后定义
c = ExampleOne()
c.first_name = 'Jason'
c.last_name = 'Bourne'
c.movies = 4
print(c.first_name, c.last_name, c.movies)
# Jason Bourne 4
class ExampleThree(object):
def __init__(self, a, b):
self.a = a
self.b = b
def addition(self):
return self.a + self.b
c = ExampleThree(10, 15)
c.addition()
# 25
c.a += 10
c.addition()
# 35
# 继承
class ExampleFour(ExampleTwo):
def addition(self):
return self.a + self.b
c = ExampleFour(10, 15)
c.addition()
# 25
# Python 允许多重继承。然而,应该小心处理可读性和可维护性,特别是供其他人使用时
class ExampleFive(ExampleFour):
def multiplication(self):
return self.a * self.b
c = ExampleFive(10, 15)
c.addition()
# 25
c.multiplication()
# 150
# 自定义方法定义不一定要包含在类定义中。 它们可以放在其他位置, 只要它们处于全局命名空间, 就可以在类定义中使用
def multiplication(self):
return self.a * self.b
class ExampleSix(ExampleFour):
multiplication = multiplication
c = ExampleSix(10, 15)
c.addition()
# 25
c.multiplication()
# 150
class ExampleSeven(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.__sum = a + b
multiplication = multiplication
def addition(self):
return self.__sum
c = ExampleSeven(10, 15)
c.addition()
# 25
# 不能直接访问私有属性sum。但是, 通过如下语法仍然可以做到:
c._ExampleSeven__sum
# 25
c.a += 10
c.a
# 20
c.multiplication()
# 300
class sorted_list(object):
def __init__(self, elements):
self.elements = sorted(elements) # sorted list object
def __iter__(self):
self.position = -1
return self
def __next__(self):
if self.position == len(self.elements) - 1:
raise StopIteration
self.position += 1
return self.elements[self.position]
name_list = ['Sandra', 'Lilli', 'Guido', 'Zorro', 'Henry']
for name in name_list:
print(name)
# Sandra
# Lilli
# Guido
# Zorro
# Henry
sorted_name_list = sorted_list(name_list)
for name in sorted_name_list:
print(name)
# Guido
# Henry
# Lilli
# Sandra
# Zorro
type(sorted_name_list)
# __main__.sorted_list
13.1.2 简单的短期利率类
在连续折现的固定短期利率世界中,日期 t>0 时的未来现金流与当前日期 t=0 之间的折现因子定义为: 。
import numpy as np
def discount_factor(r, t):
"""
Function to calculate a discount factor.
:param r: float
positive, constant short rate
:param t: float
future date(s), in fraction of years:;
e.g. 0.5 means half a year from now
:return:float
discount factor
"""
# use of NumPy universal function for vectorization
df = np.exp(-r * t)
return df
import matplotlib.pyplot as plt
# 不同短期利率在5年内的折现因子
t = np.linspace(0, 5)
for r in [0.01, 0.05, 0.1]:
plt.plot(t, discount_factor(r, t), label='r= %4.2f' % r, lw=1.5)
plt.xlabel('years')
plt.ylabel('discount factor')
plt.grid(True)
plt.legend(loc=0)
上图展示了5年内不同固定短期利率下折现因子的表现。t=0时的折现因子都等于1;也就是说, 当日现金流 “没有折现”。如果短期利率为10%, 5 年后到期的现金流将被折算为略高于0.6个货币单位(即60%)。
# 基于类的实现方法
class short_rate(object):
"""
Class to model a constant shrot rate object.
"""
def __init__(self, name, rate):
"""
:param name: string
name of the object
:param rate: float
positive, constant short rate
"""
self.name = name
self.rate = rate
def get_discount_factors(self, time_list):
"""
获取折现因子
:param time_list: list/array-like
:return:
"""
time_list = np.array(time_list)
return np.exp(-self.rate * time_list)
sr = short_rate('r', 0.05)
sr.name, sr.rate
# ('r', 0.05)
# 包含年分数的时间列表
time_list = [0.0, 0.5, 1.0, 1.25, 1.75, 2.0]
sr.get_discount_factors(time_list)
# array([1. , 0.97530991, 0.95122942, 0.93941306, 0.91621887,
# 0.90483742])
# 生成图表
for r in [0.025, 0.05, 0.1, 0.15]:
sr.rate = r
plt.plot(t, sr.get_discount_factors(t), label='r=%4.2f' % sr.rate, lw=1.5)
plt.xlabel('year')
plt.ylabel('discount factor')
plt.grid(True)
plt.legend(loc=0)
通常, 折现因子 “ 只是” 达到某种目的的手段。例如, 你可能想要用它们计算未来现金流的现值。利用我们的短期利率对象, 在已知现金流和出现日期 / 时间的情况下, 这是很容易做到的。 考虑如下现金流示例, 在例子中包含今天的负现金流和经过一年及两年之后的正现金流, 这可能是某种投资机会的现金流配置:
sr.rate = 0.05
cash_flows = np.array([-100, 50, 75])
time_list = [0.0, 1.0, 2.0]
# 折现因子
disc_facts = sr.get_discount_factors(time_list)
disc_facts
# array([1. , 0.95122942, 0.90483742])
# 现金流乘以折现因子就可以得到所有现金流的现值
disc_facts * cash_flows
# array([-100. , 47.56147123, 67.86280635])
投资理论中的典型决策原则之一是,决策者应该在给定(短期)利率(表示投资的机会成本)下净现值(NPV)为正数的项目上投资。在我们的例子中, NPV就是简单地加总单个现值:
# net present value
np.sum(disc_facts * cash_flows)
# 15.424277577732667
sr.rate = 0.15
np.sum(sr.get_discount_factors(time_list) * cash_flows)
# -1.403234627618268
显然, 在短期利率为5%时应该做出投资。 对于15%的利率,此时NPV 变成负数, 不应该进行投资。
13.1.3 现金流序列类
定义一个现金流序列的模型。这个类提供方法,返回给定现金流系列(即现金流价值和日期/时间)现值和净现值的列表数
class cash_flow_series(object):
"""
Class to model a cash flow series.
"""
def __init__(self, name, time_list, cash_flows, short_rate):
"""
:param name: string
name of the object
:param time_list: list/array-like
list of (positive) year fractions
:param cash_flows: list/array-like
corresponding list of cash flow values
:param short_rate: instance of short_rate class
short rate object used for discounting
"""
self.name = name
self.time_list = time_list
self.cash_flows = cash_flows
self.shrt_rate = short_rate
def present_value_list(self):
df = self.shrt_rate.get_discount_factors(self.time_list)
return np.array(self.cash_flows) * df
def net_presnt_value(self):
return np.sum(self.present_value_list())
sr.rate = 0.05
cfs = cash_flow_series('cfs', time_list, cash_flows, sr)
cfs.cash_flows
# array([-100, 50, 75])
cfs.time_list
# [0.0, 1.0, 2.0]
cfs.present_value_list()
# array([-100. , 47.56147123, 67.86280635])
cfs.net_presnt_value()
# 15.424277577732667
# 定义新类,提供计算不同短期利率下NPV的方法——即敏感度分析
# 从cash_flow_series类继承:
class cfs_sensitivity(cash_flow_series):
def npv_sensitivity(self, short_rates):
npvs = []
for rate in short_rates:
sr.rate = rate
npvs.append(self.net_presnt_value())
return np.array(npvs)
cfs_sens = cfs_sensitivity('cfs', time_list, cash_flows, sr)
# 定义一个包含不同短期利率的列表, 就可以轻松地比较结果NPV
short_rates = [0.01, 0.025, 0.05, 0.075, 0.1, 0.125, 0.15, 0.2]
npvs = cfs_sens.npv_sensitivity(short_rates)
npvs
# array([23.01739219, 20.10770244, 15.42427758, 10.94027255, 6.64667738,
# 2.53490386, -1.40323463, -8.78945889])
plt.plot(short_rates, npvs, 'b')
plt.plot(short_rates, npvs, 'ro')
plt.plot((0, max(short_rates)), (0, 0), 'r', lw=2)
plt.grid(True)
plt.xlabel('short rate')
plt.ylabel('net present value')
13.2 图形用户界面
用的比较少,这里就不再赘述。参考 traits