第二章 量化交易的基础(1)
2.1 基础语法与数据结构
- 无用的变量 Python 建议使用 '_' 声明:
price_array = ['30.14', '29.58', '26.36', '32.56', '32.82']
date_base = 20170118
date_array = [str(date_base + ind) for ind, _ in enumerate(price_array)]
date_array =
['20170118', '20170119', '20170120', '20170121', '20170122']
- 可命名元组:namedtuple
from collections import namedtuple
stock_namedtuple = namedtuple('stock', ('date', 'price'))
stock_namedtuple_list = [stock_namedtuple(date, price) for date, price in
zip(date_array, price_array)]
# namedtuple 访问 price
print('20170119日的价格:{}'.format(stock_namedtuple_list[1].price))
print(stock_namedtuple_list)
20170119日的价格:29.58
[stock(date='20170118', price='30.14'),
stock(date='20170119', price='29.58'), ...]
- 字典推导式
# 字典推导式:{key: value for in}
stock_dict = {date: price for date, price in zip(date_array, price_array)}
print('20170119日的价格:{}'.format(stock_dict['20170119']))
print(stock_dict)
20170119日的价格:29.58
{'20170118': '30.14', '20170119': '29.58', ...}
*字典的存储是无序的
- 有序字典:Orderdict
from collections import OrderedDict
stock_dict = OrderedDict((date, price) for date, price in zip(date_array, price_array))
print(stock_dict.keys())
odict_keys(['20170118', '20170119', '20170120', '20170121', '20170122'])
2.2 函数
1. 内置函数
需求3:从前面组合的字典数据中找到最小的一个收盘价格。
min_price = min(zip(stock_dict.values(), stock_dict.keys()))
min_price = ('26.36', '20170120')
2. 自定义函数
需求4:计算所有收盘价格中第二大的价格元素。
def find_second_max(dict_array):
# 对传入的 dict sorted 排序
stock_price_sorted = sorted(zip(dict_array.values(), dict_array.keys()))
# 第二大值的函数也就是倒数第二个
return stock_price_sorted[-2]
# 系统函数 callable() 验证是否为一个可调用(call)的函数
if callable(find_second_max):
print(find_second_max(stock_dict))
结果:('32.56', '20170121')
3. lambda 函数
上边使用 lambda 匿名函数直接定义更加简洁:
find_second_max_lambda = lambda dict_array: \
sorted(zip(dict_array.values(), dict_array.keys()))[-2]
print(find_second_max_lambda(stock_dict))
结果:('32.56', '20170121')
- 以上代码 lambda 函数赋给了一个变量 ,可以这样操作的原因是 Python 里一切皆为对象,所以函数也是一个对象。这个特点可以帮助开发者完成其他语言中一些很复杂的操作。
- Python 中的函数可以返回多个返回值,但实际上仍然是一个返回值,只不过返回值通过打包为 tuple 队列,实现多个返回值。
4. 高阶函数
- map():两个参数,一个是函数,一个是序列,map() 把传入的函数依次作用于序列的每个元素,把结果作为新的序列返回;
- filter():接收两个参数,一个是函数,一个是序列, filter() 把传入的函数依次作用于每个元素,根据返回值是 True 还是 False 决定是保留还是丢弃该元素,结果序列是所有返回值为 True 的子集;
- reduce():把一个函数作用在一个序列上,这个函数必须接收两个参数,其中 reduce() 函数把结果继续和序列的下一个元素做累积计算, reduce() 函数只返回值结果非序列。
需求5:从收盘价格,推导出每天的涨跌幅度。
pp_array = [(price1, price2) for price1, price2 in
zip(price_float_array[:-1], price_float_array[1:])]
结果:[(30.14, 29.58), (29.58, 26.36), (26.36, 32.56), (32.56, 32.82)]
下面使用高阶函数 map() 和 reduce() 结合 lambda 函数完成需求, 推导l士i每天的涨跌 幅度。 外层使用 map() 函数针对 pp_array 的每一个元素执行操作,内层使用 reduce() 函数即 两个相邻的价格,求出涨跌幅度,返回外层结果 list。
from functools import reduce
# round 将 float 保留几位小数,以下保留3位
change_array = list(map(lambda pp: reduce(lambda a, b: round((b-a)/a, 3), pp), pp_array))
# list insert 插入数据,降低一天的涨跌幅设置为0
change_array.insert(0, 0)
print(change_array)
涨跌幅数据:[0, -0.019, -0.109, 0.235, 0.008]
将计算出的涨跌幅数据加入 OrderedDict, 配合使用 namedtuple 重新构建数据结构 stock_dict
# 使用 namedtuple 重新构建数据结构
stock_namedtuple = namedtuple('stock', ('date', 'price', 'change'))
# 通过 zip 分别从 date_array, price_array, change_array 拿数据组成
# stock_namedtuple 然后以 date 作为 key 组成 OrderedDict
stock_dict = OrderedDict((date, stock_namedtuple(date, price, change))
for date, price, change in
zip(date_array, price_array, change_array))
print(stock_dict)
结果:
OrderedDict([('20170118', stock(date='20170118', price='30.14', change=0)),
('20170119', stock(date='20170119', price='29.58', change=-0.019)), ...])
- 定义一个通用函数完成所有需求:筛选上涨/下跌,计算所有 上涨/下跌 的 涨/跌幅 和数值。
def filter_stock(stock_array_dict, want_up=True, want_calc_sum=False):
if not isinstance(stock_array_dict, OrderedDict):
raise TypeError('stock_array_dict must be OrderedDict!')
# python 中的三目表达式的写法
filter_func = (lambda day: day.change > 0) \
if want_up else (lambda day: day.change < 0)
want_days = list(filter(filter_func, stock_array_dict.values()))
if not want_calc_sum:
return want_days
# 计算涨跌幅和
change_sum = 0.0
for day in want_days:
change_sum += day.change
return change_sum
print('所有上涨的交易日:{}'.format(filter_stock(stock_dict)))
print('所有下跌的交易日:{}'.format(filter_stock(stock_dict, want_up=False)))
print('所有上涨交易日的涨幅和:{}'.format(filter_stock(stock_dict, want_calc_sum=True)))
print('所有下跌交易日的跌幅和:{}'.format(filter_stock(stock_dict, want_up=False, want_calc_sum=True)))
- 所有上涨的交易日:
[stock(date='20170121', price='32.56', change=0.235), stock(date='20170122', price='32.82', change=0.008)]- 所有下跌的交易日:
[stock(date='20170119', price='29.58', change=-0.019), stock(date='20170120', price='26.36', change=-0.109)]- 所有上涨交易日的涨幅和:0.243
- 所有下跌交易日的跌幅和:-0.128
5. 偏函数
前面示例中的 filter_stock() 由于参数太多,很容易使用时产生错误。使用 functools.partial() 可以创建一个新的函数,从而在调用时更简单,函数功能也更加明确。
from functools import partial
# 筛选上涨交易日
filter_stock_up_days = partial(filter_stock, want_up=True, want_calc_sum=False)
print('所有上涨的交易日:{}'.format(filter_stock_up_days(stock_dict)))
- 所有上涨的交易日:
[stock(date='20170121', price='32.56', change=0.235), stock(date='20170122', price='32.82', change=0.008)]