├── Chapter04 └── chapter04-Python tool.py ├── Chapter05 └── chapter05-Python tool.py ├── Chapter06 └── chapter06-Python tool.py ├── Chapter07 └── chapter07-Python tool.py ├── Chapter08 └── chapter08-Numpy tool.py ├── Chapter09 └── chapter09-Pandas tool.py ├── Chapter10 ├── chapter10-Pandas tool.py └── table源.csv ├── Chapter11 ├── chapter11-Pandas tool.py └── table-Volume.csv ├── Chapter12 └── chapter12-Pandas tool.py ├── Chapter13 └── chapter13-Matplotlib tool.py ├── Chapter14 └── chapter14-wxPython tool.py ├── Chapter15 └── chapter15-Get Stock Data.py ├── Chapter16 └── chapter16-Disp Stock Data.py ├── Chapter17 └── chapter17-Disp Excavate Data.py ├── Chapter18 ├── ZDWX600797.csv └── chapter18-Disp Trade Profit.py ├── Chapter19 ├── chapter19-Strategy_AverBreak.py └── chapter19-Strategy_NdaysBreak.py ├── Chapter20 └── chapter20-Strategy_MultiBreaks.py ├── Chapter21 └── chapter21-Strategy_AtrStopSell.py ├── Chapter22 └── chapter22-Strategy_CloLinearReg.py ├── Chapter23-Python3.6+ ├── GUI_QuantTradeSys.py ├── IndicatStrateMod.py ├── RedefPanelMod.py └── StockDataMod.py ├── Chapter23 ├── GUI_QuantTradeSys.py ├── IndicatStrateMod.py ├── RedefPanelMod.py └── StockDataMod.py └── README.md /Chapter04/chapter04-Python tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | #2.变量类型及动态特性 6 | #2.1变量类型的种类 7 | #查看数值的类型 注:type()用于判断数据类型 8 | print(type(123)) #结果为: 9 | print(type(1.12)) #结果为: 10 | print(type(3j + 1)) #结果为: 11 | #查看数值的运算 12 | print(1.0/3) #结果为:0.3333333333333333 13 | print(12.5*10) #结果为:125.0 14 | #查看数值变量的运算 15 | a = 1 16 | b = 3 17 | print(a + b) #结果为:4 18 | print(a - b) #结果为:-2 19 | print(a * b) #结果为:3 20 | print(a / b) #结果为:0 21 | print(a % b) #结果为:1 22 | print(a ** b) #结果为:1 23 | print(a // b) #结果为:0 24 | 25 | #查看字符串常量 26 | print('hello world!') #结果为:hello world! 27 | print("hello world!") #结果为:hello world! 28 | #查看字符串变量 29 | a = "hello world!" 30 | print(a) #结果为:hello world! 31 | 32 | #创建列表变量 33 | list1 = ['physics', 'chemistry', 1997, 2000] 34 | list2 = [1, 2, 3, 4, 5, 6, 7 ] 35 | #列表访问 36 | print(list1[2]) #结果为:1997 37 | print(list2[1:5]) #结果为:[2, 3, 4, 5] 38 | #列表二次赋值 39 | list1[2] = 2001; 40 | print(list1[2]) #结果为:2001 41 | 42 | #创建元组并初始化 43 | name_1 = ("Madonna", "Cory", ["Annie", "Nelly"], "Cory") 44 | #元组访问 45 | print(name_1) #结果为:('Madonna', 'Cory', ['Annie', 'Nelly'], 'Cory') 46 | print(name_1[1:3]) #结果为:('Cory', ['Annie', 'Nelly']) 47 | print(name_1.index("Madonna")) #结果为:0 48 | print(len(name_1)) #结果为:4 49 | 50 | #创建字典变量 51 | math_score = {'Madonna': 89, 'Cory': 99, 'Annie': 65, 'Nelly': 89} 52 | print(math_score) #{'Madonna': 89, 'Cory': 99, 'Annie': 65, 'Nelly': 89} 53 | #字典访问 54 | print(math_score['Madonna']) #结果为:89 55 | 56 | #2.2动态类型的特性 57 | #int 58 | i = 5 59 | print(i) #结果为:5 60 | print(hex(id(i))) #结果为:0xa26f880 61 | #重新创建值为6的int对象 62 | i += 1 63 | print(i) #结果为:6 64 | print(hex(id(i))) #结果为:0xa26f874 65 | #指向数值5的内存地址 66 | j = 5 67 | print(j) #结果为:5 68 | print(hex(id(j))) #结果为:0xa26f880 69 | 70 | #float相同 71 | i = 1.5 72 | print(i) #结果为:1.5 73 | print(hex(id(i))) #结果为:0x9e86c8c 74 | 75 | i += 1 76 | print(i) #结果为:2.5 77 | print(hex(id(i))) #结果为:0x9e86cac 78 | 79 | j = 1.5 80 | print(j) #结果为:1.5 81 | print(hex(id(j))) #结果为:0x9e86c8c 82 | 83 | 84 | #例程代码: 85 | #list 86 | i = [1, 2, 3] 87 | print(i) #结果为:[1, 2, 3] 88 | print(hex(id(i))) #结果为:0xb73fa1acL 89 | 90 | #append后仍指向同一内存地址 91 | i.append(4) 92 | print(i) #结果为:[1, 2, 3, 4] 93 | print(hex(id(i))) #结果为:0xb73fa1acL 94 | 95 | #j、k的值虽然相同,但指向的内存地址却不同 96 | j = [1.5, 2.5, 3.5] 97 | print(j) #结果为:[1.5, 2.5, 3.5] 98 | print(hex(id(j))) #结果为:0xb6fed06cL 99 | k = [1.5, 2.5, 3.5] 100 | print(k) #结果为:[1.5, 2.5, 3.5] 101 | print(hex(id(k))) #结果为:0xb6fed04cL 102 | 103 | #赋值语句让j、k指向同一个内存地址 104 | j = k 105 | print(j) #结果为:[1.5, 2.5, 3.5] 106 | print(hex(id(j))) #结果为:0xb6fed04cL 107 | print(k) #结果为:[1.5, 2.5, 3.5] 108 | print(hex(id(k))) #结果为:0xb6fed04cL 109 | 110 | #j、k任意一个list变量修改,会影响另外一个list变量的值 111 | j.append(4) 112 | print(j) #结果为:[1.5, 2.5, 3.5, 4] 113 | print(hex(id(j))) #结果为:0xb6fed04cL 114 | print(j) #结果为:[1.5, 2.5, 3.5, 4] 115 | print(hex(id(k))) #结果为:0xb6fed04cL 116 | 117 | #Python解释器优化情况 118 | s1 = 'a' * 20 119 | s2 = 'a' * 20 120 | print(hex(id(s1)), hex(id(s2))) #结果为:0xb7075320L 0xb7075320L 121 | 122 | s1 = 'a' * 21 123 | s2 = 'a' * 21 124 | print(hex(id(s1)), hex(id(s2))) #结果为:0xb70752f0L 0xb7075350L 125 | -------------------------------------------------------------------------------- /Chapter05/chapter05-Python tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 6 | #3、继承、派生和组合的应用 7 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 8 | #定义类ParentClass1 9 | class ParentClass1: 10 | def __init__(self, name, age): 11 | self.name = name 12 | self.age = age 13 | 14 | def speak(self): 15 | print('speak ParentClass1') 16 | 17 | #建立另外一个类SubClass1 继承ParentClass1 18 | class SubClass1(ParentClass1): 19 | def __init__(self, name, age, country): 20 | ParentClass1.__init__(self, name, age) 21 | self.country = country 22 | #新增write()方法 23 | def write(self): 24 | print('write SubClass1') 25 | 26 | b1 = SubClass1('jack', 21, 'China') 27 | #SubClass1包含了ParentClass1所有的属性 28 | print(b1.name) #结果为:jack 29 | print(b1.age) #结果为:21 30 | print(b1.country) #结果为: China 31 | b1.speak() #结果为: test ParentClass1 32 | b1.write() #结果为: test SubClass1' 33 | 34 | #例程代码 35 | #定义类ParentClass1 36 | class ParentClass1: 37 | def __init__(self, name, age): 38 | self.name = name 39 | self.age = age 40 | 41 | def speak(self): 42 | print('speak ParentClass1') 43 | 44 | #定义类ParentClass2 45 | class ParentClass2: 46 | def walk(self): 47 | print('walk ParentClass2') 48 | 49 | #子类SubClass2继承ParentClass1和ParentClass2两个类 50 | class SubClass2(ParentClass1,ParentClass2): 51 | def __init__(self, name, age, country): 52 | ParentClass1.__init__(self, name, age) 53 | self.country = country 54 | #SubClass2包含了ParentClass1和ParentClass2所有的属性 55 | b2 = SubClass2('jack', 21, 'China') 56 | b2.speak() #结果为:speak ParentClass1 57 | b2.walk() #结果为:walk ParentClass2 58 | 59 | """ 跳空缺口 基类""" 60 | class JumpGap_Base: 61 | def __init__(self, stock_dat): 62 | self.stock_dat = stock_dat 63 | self.jump_pd = []#跳空缺口 64 | 65 | def CaljumpGap(self): 66 | print("Calculating Gap") 67 | return self.jump_pd 68 | """ 69 | # 新版类继承派生实现方式 70 | class JumpGap_Redef(JumpGap_Base): 71 | def __init__(self, stock_dat, draw_obj): 72 | JumpGap_Base.__init__(self, stock_dat) 73 | 74 | def filtjumpGap(self): 75 | print("filter Gap") 76 | self.jump_pd = self.jump_pd[(np.abs(self.jump_pd.changeRatio) > 3)&(self.jump_pd.Volume > self.jump_pd.Volume.median())]#过滤缺口 77 | return self.jump_pd 78 | """ 79 | # 新版类组合实现方式 80 | class JumpGap_Redef(JumpGap_Base): 81 | def __init__(self, stock_dat, draw_obj): 82 | JumpGap_Base.__init__(self, stock_dat) 83 | self.draw_way = draw_obj 84 | 85 | def filtjumpGap(self): 86 | print("filter Gap") 87 | self.jump_pd = self.jump_pd[(np.abs(self.jump_pd.changeRatio) > 3)&(self.jump_pd.Volume > self.jump_pd.Volume.median())]#过滤缺口 88 | return self.jump_pd 89 | 90 | def DrawjumpGap(self): 91 | self.draw_way.draw_jumpgap(self.stock_dat,self.jump_pd) 92 | 93 | 94 | class draw_annotate: 95 | def __init__(self, draw_obj): 96 | self.am = draw_obj 97 | def draw_jumpgap(self,stockdat,jump_pd): 98 | print("draw Gap")#绘制跳空缺口 99 | 100 | 101 | #创建人类 102 | class Human: 103 | def __init__(self, name, age): 104 | self.name = name 105 | self.age = age 106 | 107 | def speak(self): 108 | print('Human speak skill') 109 | #创建电脑类 110 | class Computer: 111 | def __init__(self, model ,brand): 112 | self.model=model 113 | self.brand=brand 114 | 115 | #程序员继承人类的属性 116 | class Programmer(Human): 117 | def __init__(self, name, age, country, computer): 118 | Human.__init__(self, name, age) 119 | self.country = country 120 | self.computer = computer 121 | 122 | #程序员增加电脑属性 123 | b3=Programmer('jack', 21, 'China',Computer('X10','dell')) 124 | print(b3.computer.model,b3.computer.brand) #结果为:('X10','dell') 125 | -------------------------------------------------------------------------------- /Chapter06/chapter06-Python tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from threading import Thread 3 | from multiprocessing import Process,Manager 4 | from timeit import timeit 5 | 6 | #计算密集型任务 7 | def count(n): 8 | while n > 0: 9 | n-=1 10 | #不采用多任务方式 11 | def test_normal(): 12 | count(1000000) 13 | count(1000000) 14 | #多线程方式 15 | def test_Thread(): 16 | t1 = Thread(target=count,args=(1000000,)) 17 | t2 = Thread(target=count,args=(1000000,)) 18 | t1.start() 19 | t2.start() 20 | t1.join() 21 | t2.join() 22 | #多进程方式 23 | def test_Process(): 24 | t1 = Process(target=count,args=(1000000,)) 25 | t2 = Process(target=count,args=(1000000,)) 26 | t1.start() 27 | t2.start() 28 | t1.join() 29 | t2.join() 30 | 31 | if __name__ == '__main__': 32 | print "test_normal",timeit('test_normal()','from __main__ import test_normal',number=10) 33 | print "test_Thread",timeit('test_Thread()','from __main__ import test_Thread',number=10) 34 | print "test_Process",timeit('test_Process()','from __main__ import test_Process',number=10) 35 | -------------------------------------------------------------------------------- /Chapter07/chapter07-Python tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | 6 | #装饰器的剖析 7 | #输出一个股票当天的收盘价字符串 8 | #二层嵌套 9 | """ 10 | def log(func): 11 | def wrapper(*args, **kw): 12 | print('output %s():' % func.__name__) 13 | return func(*args, **kw) 14 | return wrapper 15 | 16 | """ 17 | """ 18 | @log 19 | def Stock_600213(): 20 | print('2018-12-25:6.54') 21 | #调用函数打印结果: 22 | Stock_600213() 23 | #2018-12-25:6.54 24 | """ 25 | """ 26 | @log 27 | def Stock_600213(Close): 28 | print('2018-12-25 {} :6.54'.format(Close)) 29 | #调用函数打印结果: 30 | Stock_600213('Close') 31 | #2018-12-25 Close :6.54 32 | 33 | """ 34 | 35 | """ 36 | #直接增加打印日志功能 37 | def Stock_600213(): 38 | print('output Stock_600213()') 39 | print('2018-12-25:6.54') 40 | #调用函数打印结果: 41 | Stock_600213() 42 | #output Stock_600213(): 43 | #2018-12-25:6.54 44 | """ 45 | 46 | #三层嵌套 47 | def log(text): 48 | def decorator(func): 49 | def wrapper(*args, **kw): 50 | print '%s %s():' % (text, func.__name__) 51 | return func(*args, **kw) 52 | return wrapper 53 | return decorator 54 | 55 | @log('Now Output') 56 | def Stock_600213(Close): 57 | print('2018-12-25 {} :6.54'.format(Close)) 58 | #调用函数打印结果: 59 | Stock_600213('Close') 60 | print(Stock_600213.__name__) 61 | #Now Output Stock_600213(): 62 | #2018-12-25 Close :6.54 63 | 64 | import functools 65 | def log(text): 66 | def decorator(func): 67 | @functools.wraps(func) 68 | def wrapper(*args, **kw): 69 | print '%s %s():' % (text, func.__name__) 70 | return func(*args, **kw) 71 | return wrapper 72 | return decorator 73 | 74 | @log('Now Output') 75 | def Stock_600213(Close): 76 | print('2018-12-25 {} :6.54'.format(Close)) 77 | #调用函数打印结果: 78 | Stock_600213('Close') 79 | print(Stock_600213.__name__) 80 | #Now Output Stock_600213(): 81 | #2018-12-25 Close :6.54 82 | #Stock_600213 83 | 84 | 85 | #装饰器讲解 86 | class SelfPools(): 87 | def __init__(self): 88 | self.routes = {} 89 | def route(self, route_str): 90 | def decorator(f): 91 | self.routes[route_str] = f 92 | return f 93 | return decorator 94 | 95 | def output(self, path): 96 | view_function = self.routes.get(path) 97 | if view_function: 98 | print u"输出[%s]板块股票:" % path 99 | for str in view_function(): 100 | print(str) 101 | return 102 | else: 103 | raise ValueError('Route "{}"" has not been registered'.format(path)) 104 | 105 | app = SelfPools() 106 | 107 | @app.route(u"5G") 108 | def Stock_pool(): 109 | stock_name = [u"600776:东方通信",u"002792:通宇通信",u"002268:卫士通",u"300698:万马科技"] 110 | return stock_name 111 | 112 | @app.route(u"量子通信") 113 | def Stock_pool(): 114 | stock_name = [u"600746:中国海防",u"002126:银轮股份",u"600522:中天科技",u"600468:百利电气"] 115 | return stock_name 116 | 117 | app.output(u"5G") 118 | #输出[5G]板块股票: 119 | #600776:东方通信 120 | #002792:通宇通信 121 | #002268:卫士通 122 | #300698:万马科技 123 | app.output(u"量子通信") 124 | #输出[量子通信]板块股票: 125 | #600746:中国海防 126 | #002126:银轮股份 127 | #600522:中天科技 128 | #600468:百利电气 129 | -------------------------------------------------------------------------------- /Chapter08/chapter08-Numpy tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import numpy as np#推荐引用方式 6 | from timeit import timeit 7 | 8 | print(np.ones((10, 5))) 9 | """ 10 | [[ 1. 1. 1. 1. 1.] 11 | [ 1. 1. 1. 1. 1.] 12 | [ 1. 1. 1. 1. 1.] 13 | [ 1. 1. 1. 1. 1.] 14 | [ 1. 1. 1. 1. 1.] 15 | [ 1. 1. 1. 1. 1.] 16 | [ 1. 1. 1. 1. 1.] 17 | [ 1. 1. 1. 1. 1.] 18 | [ 1. 1. 1. 1. 1.] 19 | [ 1. 1. 1. 1. 1.]] 20 | """ 21 | print(np.ones((10, 5)).shape) 22 | #(10, 5) 23 | print(np.ones((10, 5)).dtype) 24 | #float64 25 | print(np.ones((10, 5)).strides) 26 | #(40, 8) 27 | 28 | #测试Numpy数组和等价的Python列表性能差距 29 | my_arr = np.arange(1000000) 30 | my_list = list(range(1000000)) 31 | t1 = timeit('for _ in range(10): my_arr2 = my_arr * 2','from __main__ import my_arr',number=1) 32 | t2 = timeit('for _ in range(10): my_list2 = [x * 2 for x in my_list]','from __main__ import my_list',number=1) 33 | print(t1,t2) 34 | 35 | #矢量化运算 36 | arrA = np.array([[1., 2., 3.], [4., 5., 6.]]) 37 | arrB = np.array([[2., 2., 2.], [2., 2., 2.]]) 38 | print(arrA) 39 | """ 40 | [[ 1. 2. 3.] 41 | [ 4. 5. 6.]] 42 | """ 43 | print(arrB) 44 | """ 45 | [[ 2. 2. 2.] 46 | [ 2. 2. 2.]] 47 | """ 48 | print(arrA * arrB) 49 | """ 50 | [[ 2. 4. 6.] 51 | [ 8. 10. 12.]] 52 | """ 53 | print(arrA * 2.0) 54 | """ 55 | [[ 2. 4. 6.] 56 | [ 8. 10. 12.]] 57 | """ 58 | arrA = np.array([[0., 0., 0.], [1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]) 59 | print(arrA) 60 | """ 61 | [[ 0. 0. 0.] 62 | [ 1. 1. 1.] 63 | [ 2. 2. 2.] 64 | [ 3. 3. 3.]] 65 | """ 66 | print(arrA.shape) 67 | #(4, 3) 68 | arrB = np.array([1., 2., 3.]) 69 | print(arrB) 70 | #[ 1. 2. 3.] 71 | print(arrB.shape) 72 | #(3,) 73 | print(arrA+arrB) 74 | """ 75 | [[ 1. 2. 3.] 76 | [ 2. 3. 4.] 77 | [ 3. 4. 5.] 78 | [ 4. 5. 6.]] 79 | """ 80 | 81 | x = np.ones((4,1)) 82 | print(x) 83 | y = np.ones((1,4,3)) 84 | print(y) 85 | print(x+y) 86 | #operands could not be broadcast together with shapes (2,1) (5,4,3) 87 | """ 88 | [[ 1.] 89 | [ 1.] 90 | [ 1.] 91 | [ 1.]] 92 | [[[ 1. 1. 1.] 93 | [ 1. 1. 1.] 94 | [ 1. 1. 1.] 95 | [ 1. 1. 1.]]] 96 | [[[ 2. 2. 2.] 97 | [ 2. 2. 2.] 98 | [ 2. 2. 2.] 99 | [ 2. 2. 2.]]] 100 | """ 101 | #data = np.random.randn(3, 4) 102 | 103 | data = np.array([[ 0.81516464,0.54699707,0.25469129,-0.35725194], 104 | [-0.1594436,0.47096122,-0.51086806,-0.82336626], 105 | [-0.76274312,0.66010544,0.45585599,0.80401797]]) 106 | print(data) 107 | """ 108 | [[ 0.81516464 0.54699707 0.25469129 -0.35725194] 109 | [-0.1594436 0.47096122 -0.51086806 -0.82336626] 110 | [-0.76274312 0.66010544 0.45585599 0.80401797]] 111 | """ 112 | print(data[[True, False, False]]) 113 | #[[ 0.81516464 0.54699707 0.25469129 -0.35725194]] 114 | print(data[[True, False, False],1]) 115 | #[ 0.54699707] 116 | print(data < 0) 117 | """ 118 | [[False False False True] 119 | [ True False True True] 120 | [ True False False False]] 121 | """ 122 | print(data[data < 0]) 123 | #[-0.35725194 -0.1594436 -0.51086806 -0.82336626 -0.76274312] 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /Chapter09/chapter09-Pandas tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import pandas as pd 5 | import numpy as np 6 | import datetime #存储CSV文件使用 7 | #1、Pandas工具快速入门:数据生成和访问 8 | #1.1Series数据生成和访问 9 | print(u"**********************************1.1Series数据生成和访问************************************************") 10 | #以列表作为数据类型创建一个Series对象 11 | s = pd.Series([-1.55666192,-0.75414753,0.47251231,-1.37775038,-1.64899442], index=['a', 'b', 'c', 'd', 'e']) 12 | print(s) 13 | """ 14 | #执行结果如下: 15 | a -1.556662 16 | b -0.754148 17 | c 0.472512 18 | d -1.377750 19 | e -1.648994 20 | dtype: float64 21 | """ 22 | 23 | #list数据包含数字和字符串 24 | s = pd.Series(['a',-0.75414753,123,66666,-1.64899442], index=['a', 'b', 'c', 'd', 'e'],) 25 | print(s) 26 | """ 27 | #执行结果如下: 28 | a a 29 | b -0.754148 30 | c 123 31 | d 66666 32 | e -1.64899 33 | dtype: object 34 | """ 35 | #dtype指定int8类型 36 | s = pd.Series([-1.55666192,-0.75414753,0.47251231,-1.37775038,-1.64899442], index=['a', 'b', 'c', 'd', 'e'],dtype='int8' ) 37 | print(s) 38 | """ 39 | #执行结果如下: 40 | a -1 41 | b 0 42 | c 0 43 | d -1 44 | e -1 45 | dtype: int8 46 | """ 47 | 48 | #以ndarray作为数据类型创建一个Series对象 49 | s = pd.Series(np.random.randn(5)) 50 | print(s) 51 | """ 52 | #执行结果如下: 53 | 0 0.485468 54 | 1 -0.912130 55 | 2 0.771970 56 | 3 -1.058117 57 | 4 0.926649 58 | dtype: float64 59 | """ 60 | 61 | #指定的内容创建索引 62 | s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) 63 | print(s) 64 | """ 65 | #执行结果如下: 66 | a 0.485468 67 | b -0.912130 68 | c 0.771970 69 | d -1.058117 70 | e 0.926649 71 | dtype: float64 72 | """ 73 | 74 | #以常量值作为数据创建一个Series对象 75 | s = pd.Series(5., index=['a', 'b', 'c', 'd', 'e']) 76 | print(s) 77 | """ 78 | #执行结果如下: 79 | a 5.0 80 | b 5.0 81 | c 5.0 82 | d 5.0 83 | e 5.0 84 | dtype: float64 85 | """ 86 | 87 | #以字典作为数据类型创建一个Series对象 88 | s = pd.Series({'a' : 0., 'b' : 1., 'c' : 2.},index=['b', 'c', 'd', 'a']) 89 | print(s) 90 | """ 91 | #执行结果如下: 92 | b 1.0 93 | c 2.0 94 | d NaN 95 | a 0.0 96 | dtype: float64 97 | """ 98 | 99 | #访问Series全部元素数值 100 | print(s.values) 101 | #执行结果:[ 1. 2. nan 0.] 102 | 103 | #访问Series全部索引值 104 | print(s.index) 105 | #执行结果:Index([u'b', u'c', u'd', u'a'], dtype='object 106 | 107 | #访问a索引的元素值 108 | print(s['a']) 109 | #执行结果:0.0 110 | 111 | #访问a和b索引的元素值 112 | print(s[['a','b']]) 113 | """ 114 | #执行结果如下: 115 | a 0.0 116 | b 1.0 117 | dtype: float64 118 | """ 119 | 120 | #访问a、b、c索引的元素值 121 | print(s[['a','b','c']]) 122 | """ 123 | #执行结果如下: 124 | a 0.0 125 | b 1.0 126 | c 2.0 127 | dtype: float64 128 | """ 129 | 130 | #访问前两个数据 131 | print(s[:2]) 132 | """ 133 | #执行结果如下: 134 | b 1.0 135 | c 2.0 136 | dtype: float64 137 | """ 138 | print(u"**********************************1.1Series数据生成和访问************************************************") 139 | print(u"*********************************************************************************************************") 140 | print(u"*********************************************************************************************************") 141 | print(u"**********************************1.2dataframe数据生成和访问************************************************") 142 | 143 | #以列表组成的字典形式创建DataFrame 144 | df = pd.DataFrame({'one': [1., 2., 3., 5], 'two': [1., 2., 3., 4.]}) 145 | print(df) 146 | """ 147 | #执行结果如下: 148 | one two 149 | 0 1.0 1.0 150 | 1 2.0 2.0 151 | 2 3.0 3.0 152 | 3 5.0 4.0 153 | """ 154 | #以嵌套列表形式创建DataFrame 155 | df = pd.DataFrame([[1., 2., 3., 5],[1., 2., 3., 4.]],index=['a', 'b'],columns=['one','two','three','four']) 156 | print(df) 157 | """ 158 | #执行结果如下: 159 | one two three four 160 | a 1.0 2.0 3.0 5.0 161 | b 1.0 2.0 3.0 4.0 162 | """ 163 | #创建一个二维ndarray阵列 164 | data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')]) 165 | print(data) 166 | """ 167 | #执行结果如下: 168 | [(0, 0., '') (0, 0., '')] 169 | """ 170 | #以整数、浮点和字符串类型对data进行赋值 171 | data[:] = [(1,2.,'Hello'), (2,3.,"World")] 172 | #二维ndarray形式创建DataFrame 173 | df = pd.DataFrame(data) 174 | print(df) 175 | """ 176 | #执行结果如下: 177 | A B C 178 | 0 1 2.0 Hello 179 | 1 2 3.0 World 180 | """ 181 | #指定行索引为['first', 'second'] 182 | df = pd.DataFrame(data, index=['first', 'second']) 183 | print(df) 184 | """ 185 | #执行结果如下: 186 | A B C 187 | first 1 2.0 Hello 188 | second 2 3.0 World 189 | """ 190 | #指定列索引columns 191 | df = pd.DataFrame(data, columns=['C', 'A', 'B']) 192 | print(df) 193 | """ 194 | #执行结果如下: 195 | C A B 196 | 0 Hello 1 2.0 197 | 1 World 2 3.0 198 | """ 199 | #创建一组以Series组成的字典 200 | data = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), 201 | 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])} 202 | #以Series组成的字典形式创建DataFrame 203 | df = pd.DataFrame(data) 204 | print(df) 205 | """ 206 | #执行结果如下: 207 | one two 208 | a 1.0 1.0 209 | b 2.0 2.0 210 | c 3.0 3.0 211 | d NaN 4.0 212 | """ 213 | #创建一组字典的列表数据 214 | data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}] 215 | #字典的列表创建DataFrame 216 | df = pd.DataFrame(data2) 217 | print(df) 218 | """ 219 | #执行结果如下: 220 | a b c 221 | 0 1 2 NaN 222 | 1 5 10 20.0 223 | """ 224 | 225 | print(u"**********************************dataframe数据访问************************************************") 226 | #创建一组以Series组成的字典 227 | data = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), 228 | 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])} 229 | #以Series组成的字典形式创建DataFrame 230 | df = pd.DataFrame(data) 231 | print(df) 232 | """ 233 | #执行结果如下: 234 | one two 235 | a 1.0 1.0 236 | b 2.0 2.0 237 | c 3.0 3.0 238 | d NaN 4.0 239 | """ 240 | print(df.loc['a']) 241 | """ 242 | #执行结果如下: 243 | one 1.0 244 | two 1.0 245 | Name: a, dtype: float64 246 | """ 247 | print(df.loc[:,['one','two'] ]) 248 | """ 249 | #执行结果如下: 250 | one two 251 | a 1.0 1.0 252 | b 2.0 2.0 253 | c 3.0 3.0 254 | d NaN 4.0 255 | """ 256 | print(df.loc[['a',],['one','two']]) 257 | """ 258 | #执行结果如下: 259 | one two 260 | a 1.0 1.0 261 | """ 262 | print(df.iloc[0:2,0:1]) 263 | """ 264 | #执行结果如下: 265 | one 266 | a 1.0 267 | b 2.0 268 | """ 269 | print(df.iloc[0:2]) 270 | """ 271 | #执行结果如下: 272 | one two 273 | a 1.0 1.0 274 | b 2.0 2.0 275 | """ 276 | print(df.iloc[[0,2],[0,1]]) 277 | """ 278 | #执行结果如下: 279 | one two 280 | a 1.0 1.0 281 | c 3.0 3.0 282 | """ 283 | 284 | print(df.ix['a']) 285 | """ 286 | #执行结果如下: 287 | one 1.0 288 | two 1.0 289 | Name: a, dtype: float64 290 | """ 291 | print(df.ix['a',['one','two']]) 292 | """ 293 | #执行结果如下: 294 | one 1.0 295 | two 1.0 296 | Name: a, dtype: float64 297 | """ 298 | 299 | print(df.ix['a',[0,1]]) 300 | """ 301 | #执行结果如下: 302 | one 1.0 303 | two 1.0 304 | Name: a, dtype: float64 305 | """ 306 | 307 | print(df.ix[['a','b'],[0,1]]) 308 | """ 309 | #执行结果如下: 310 | one two 311 | a 1.0 1.0 312 | b 2.0 2.0 313 | """ 314 | print(df.ix[df.one>1,:1]) 315 | """ 316 | #执行结果如下: 317 | one 318 | b 2.0 319 | c 3.0 320 | """ 321 | print(u"**********************************1.2dataframe数据生成和访问************************************************") 322 | print(u"*********************************************************************************************************") 323 | print(u"*********************************************************************************************************") 324 | -------------------------------------------------------------------------------- /Chapter10/chapter10-Pandas tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import pandas as pd 5 | import numpy as np 6 | import datetime #存储CSV文件使用 7 | 8 | print(u"**********************************1.1加载CSV文件*********************************************************") 9 | 10 | #read_csv 测试 11 | #加载csv文件数据 12 | df_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table.csv',parse_dates=True,index_col=0,encoding='gb2312') 13 | print(df_csvload) 14 | 15 | #参数header=1 16 | df_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table.csv',header=1,parse_dates=True,index_col=0,encoding='gb2312') 17 | print(df_csvload) 18 | print(df_csvload.index) 19 | #参数parse_dates=False 20 | df_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table.csv',parse_dates=False,index_col=0,encoding='gb2312') 21 | print(df_csvload.index) 22 | 23 | print(u"**********************************1.2存储CSV文件*********************************************************") 24 | 25 | #to_csv 测试 26 | #加载csv文件数据 27 | df_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table.csv',parse_dates=True,index_col=0,encoding='gb2312') 28 | #扩充2个交易日的股票数据 29 | df_adddat = pd.DataFrame([{u'Open':1.1, u'High':1.2, u'Low':1.3, u'Close':1.4}, {u'Open':2.1, u'High':2.2, u'Low':2.3, u'Close':2.4}],index=[datetime.datetime.strptime("2016-06-25 00:00:00", "%Y-%m-%d %H:%M:%S"),datetime.datetime.strptime("2016-06-26 00:00:00", "%Y-%m-%d %H:%M:%S")]) 30 | df_csvload = df_csvload.append(df_adddat) 31 | print(df_csvload) 32 | #存储csv文件数据 33 | df_csvload.to_csv('C:\\Users\\Administrator\\Desktop\\table-add.csv',columns=df_csvload.columns,index=True) 34 | print(u"*********************************************************************************************************") 35 | print(u"*********************************************************************************************************") 36 | 37 | -------------------------------------------------------------------------------- /Chapter10/table源.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close 2 | 2018/1/2,16.2143,17.6714,16.2143,17.6714 3 | 2018/1/3,17.492901,17.8857,17.378599,17.5786 4 | 2018/1/4,17.8643,19.1429,17.8643,18.871401 5 | 2018/1/5,18.592899,19.371401,18.3571,19.092899 6 | 2018/1/8,18.592899,19.371401,18.3571,19.092899 7 | 2018/1/9,19.8857,20.6786,19.257099,19.6714 8 | 2018/1/10,20.064301,20.1071,18.6429,18.950001 9 | 2018/1/11,19.1429,19.164301,18.721399,18.9 10 | 2018/1/12,18.992901,19.814301,18.8571,19.242901 11 | 2018/1/15,19.4286,19.4286,18,18.507099 12 | 2018/1/16,18.4571,19.4286,18.450001,18.757099 13 | 2018/1/17,18.8571,18.8571,17.585699,17.764299 14 | 2018/1/18,17.9786,18.985701,17.8071,18.842899 15 | 2018/1/19,18.842899,19.3571,18.492901,18.878599 16 | 2018/1/22,18.9214,19.507099,18.621401,19.485701 17 | 2018/1/23,19.75,19.7857,18.2143,18.8929 18 | 2018/1/24,18.9286,18.9786,18.435699,18.907101 19 | 2018/1/25,19,19.7714,18.985701,19.2286 20 | 2018/1/26,19.1071,19.764299,18.9286,19.3643 21 | 2018/1/29,19.3571,20.7857,19.221399,20.735701 22 | 2018/1/30,20.5571,21.0357,19.935699,20.5214 23 | 2018/1/31,20.15,20.15,19.5429,19.778601 24 | -------------------------------------------------------------------------------- /Chapter11/chapter11-Pandas tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import pandas as pd 5 | import numpy as np 6 | import datetime #存储CSV文件使用 7 | 8 | 9 | #3、Pandas工具快速入门:数据规整化处理 10 | 11 | print(u"**********************************1.1、数据信息查看*********************************************************") 12 | 13 | #加载csv文件数据 14 | df_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table.csv',parse_dates=True,index_col=0,encoding='gb2312') 15 | 16 | print(df_csvload.head())#查看前几行 17 | print(df_csvload.tail())#查看后几行 18 | print(df_csvload.columns)#查看列名 19 | print(df_csvload.index)#查看索引 20 | print(df_csvload.shape)#查看形状 21 | 22 | print(df_csvload.describe())#查看各列数据描述性统计 23 | print(df_csvload.info())#查看缺失及每列数据类型 事先去除数值 24 | print(u"**********************************1.2、缺失值处理*********************************************************") 25 | 26 | print(df_csvload.isnull())#判断数据缺失值 27 | print(df_csvload[df_csvload.isnull().T.any().T])#查看NAN值所在行 28 | #只要有一个缺失值就删除该行 29 | #df_csvload = df_csvload.dropna(axis=0,how='any')#NAN值删除 30 | #print(df_csvload) 31 | df_csvload = df_csvload.dropna(axis=0,how='all')#NAN值删除 所有值都为缺失值时才删除该行 32 | df_csvload.fillna(method='bfill',axis=0,inplace=True)#NAN值填充 列方向前值填充 33 | print(df_csvload[df_csvload.isnull().values==True])#查看NAN值删除填充后值 34 | 35 | print(u"**********************************1.3、特殊值处理*********************************************************") 36 | 37 | #df_csvload = df_csvload.applymap(lambda x:'%0.2f'%x)#保留2位小数 38 | #print(df_csvload) 39 | #print(df_csvload.info()) 40 | df_csvload = df_csvload.round(2)#保留2位小数 41 | print(df_csvload) 42 | print(df_csvload.info()) 43 | print(df_csvload[df_csvload.values==0])#查看df_csvload数据中所有0值的元素 44 | df_csvload.loc[df_csvload.loc[:,'Low']==0,'Low'] = df_csvload.Low.median()# 45 | print(df_csvload.loc['2018-01-15']) 46 | 47 | print(u"**********************************1.4、数据运算转化*********************************************************") 48 | 49 | #数据运算 50 | change = df_csvload.High - df_csvload.Low#最高价-最低价 51 | df_csvload['pct_change'] = (change / df_csvload['Close'].shift(1)) #/昨收 52 | print(df_csvload) 53 | df_csvload['pct_change'].fillna(df_csvload['pct_change'].mean(),inplace=True) #序列第一个值Na 54 | print(df_csvload) 55 | 56 | print(u"**********************************1.5、数据合并及连接*********************************************************") 57 | 58 | #数据合并 59 | dfv_csvload = pd.read_csv('C:\\Users\\Administrator\\Desktop\\table-Volume.csv',parse_dates=True,index_col=0,encoding='gb2312') 60 | df_concat =pd.concat([df_csvload, dfv_csvload],axis=1,keys=['Price','amount']) 61 | print(df_concat) 62 | 63 | -------------------------------------------------------------------------------- /Chapter11/table-Volume.csv: -------------------------------------------------------------------------------- 1 | Date,Volume 2 | 2018/1/2,57186488 3 | 2018/1/3,41095849 4 | 2018/1/4,52611597 5 | 2018/1/5,45723087 6 | 2018/1/8,43435641 7 | 2018/1/9,41148196 8 | 2018/1/10,38860750 9 | 2018/1/11,36573305 10 | 2018/1/12,34285859 11 | 2018/1/15,31998414 12 | 2018/1/16,29710968 13 | 2018/1/17,27423523 14 | 2018/1/18,25136077 15 | 2018/1/19,22848632 16 | 2018/1/22,20561186 17 | 2018/1/23,18273741 18 | 2018/1/24,15986295 19 | 2018/1/25,13698850 20 | 2018/1/26,11411404 21 | 2018/1/29,9123959 22 | 2018/1/30,6836513 23 | 2018/1/31,4549068 24 | -------------------------------------------------------------------------------- /Chapter12/chapter12-Pandas tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import pandas as pd 5 | import numpy as np 6 | import datetime #存储CSV文件使用 7 | 8 | 9 | #4、Pandas工具快速入门:数据遍历的方法 10 | 11 | print(u"**********************************1.for..in循环迭代方式*********************************************************") 12 | 13 | 14 | """ 15 | #迭代循环测试 16 | x = [1,2,3] 17 | its = x.__iter__() #列表是可迭代对象,否则会提示不是迭代对象 18 | print(its) 19 | # 20 | print(next(its)) # its包含此方法,说明its是迭代器 21 | #1 22 | print(next(its)) 23 | #2 24 | print(next(its)) 25 | #3 26 | print(next(its)) 27 | """ 28 | """ 29 | #生成器测试 30 | def gensquares(N): 31 | for i in range(N): 32 | yield i**2 33 | print(gensquares(5)) 34 | for i in gensquares(5): 35 | print(i) 36 | 37 | print(x**2 for x in range(5)) 38 | print(list(x**2 for x in range(5))) 39 | 40 | # at 0xb3d31fa4> 41 | #[0, 1, 4, 9, 16] 42 | """ 43 | 44 | from timeit import timeit 45 | def iterator_looping(df): 46 | disftance_list = [] 47 | for i in range(0,len(df)): 48 | disftance_list.append(df.iloc[i]['Open']-df.iloc[i]['Close']) 49 | return disftance_list 50 | 51 | print(iterator_looping(df_csvload)) 52 | 53 | disftance_list = [(df_csvload.iloc[i]['Open']-df_csvload.iloc[i]['Close']) for i in range(0,len(df_csvload))] 54 | print(disftance_list) 55 | 56 | def iterrows_loopiter(df): 57 | disftance_list = [] 58 | for index,row in df.iterrows(): 59 | disftance_list.append(row['Open']-row['Close']) 60 | return disftance_list 61 | print(iterrows_loopiter(df_csvload)) 62 | 63 | 64 | disftance_list = df_csvload.apply(lambda row: (row['Open']-row['Close']), axis =1) 65 | print(disftance_list) 66 | 67 | df_csvload['rate'] = df_csvload['Open']-df_csvload['Close'] 68 | print(df_csvload['rate']) 69 | 70 | df_csvload['rate'] = df_csvload['Open'].values-df_csvload['Close'].values 71 | print(df_csvload['rate']) 72 | 73 | def test1(): 74 | iterator_looping(df_csvload) 75 | def test2(): 76 | iterrows_loopiter(df_csvload) 77 | def test3(): 78 | disftance_list = df_csvload.apply(lambda row: (row['Open']-row['Close']), axis =1) 79 | def test4(): 80 | df_csvload['rate'] = df_csvload['Open']-df_csvload['Close'] 81 | def test5(): 82 | df_csvload['rate'] = df_csvload['Open'].values-df_csvload['Close'].values 83 | 84 | #for..in循环迭代方式 85 | t1 = timeit('test1()', 'from __main__ import test1', number=1000) 86 | #iterrows()遍历方式 87 | t2 = timeit('test2()', 'from __main__ import test2', number=1000) 88 | #apply()方法循环方式 89 | t3 = timeit('test3()', 'from __main__ import test3', number=1000) 90 | #Pandas series 的矢量化方式 91 | t4 = timeit('test4()', 'from __main__ import test4', number=1000) 92 | #Numpy arrays的矢量化方式: 93 | t5 = timeit('test5()', 'from __main__ import test5', number=1000) 94 | 95 | print(t1,t2,t3,t4,t5) 96 | -------------------------------------------------------------------------------- /Chapter13/chapter13-Matplotlib tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import matplotlib 6 | import matplotlib.pyplot as plt#(1) 7 | from matplotlib.figure import Figure 8 | 9 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 10 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 11 | 12 | #基础数据 13 | y_value = np.random.randn(200) 14 | x_value = np.arange(200) 15 | 16 | ylim_min = y_value.min()-1 17 | ylim_max = y_value.max()+1 18 | 19 | yticks_min = y_value.min()+0.5 20 | yticks_max = y_value.max()-0.5 21 | ylim_setp = (yticks_max - yticks_min)/2.1 22 | #基础数据 23 | """ 24 | #函数式编程 25 | plt.xlim(0,len(x_value))#注释(2) 26 | plt.ylim(ylim_min,ylim_max)#注释(2) 27 | plt.xticks(np.arange(0,len(x_value),20),['2015-02-01','2015-03-01','2015-04-02','2015-05-02','2015-06-02','2015-07-02','2015-08-02','2015-09-02','2015-10-02','2015-11-02'],rotation=45)#注释(3) 28 | plt.yticks(np.arange(yticks_min,yticks_max,ylim_setp),[u'上限预警值',u'标准值',u'下限预警值'])#注释(3) 29 | plt.title(u"函数式编程")#注释(4) 30 | plt.xlabel(u"日期")#注释(5): 31 | plt.ylabel(u"数值") #注释(5): 32 | plt.grid(True)#注释(6) 33 | plt.legend(loc='best')#注释(7) 34 | plt.plot(x_value,y_value,label=u"随机误差",ls='-',c='r',lw=1) #注释(8) 35 | plt.show() 36 | #函数式编程 37 | """ 38 | 39 | ''' 对象式编程 ''' 40 | """ 41 | fig = plt.figure() 42 | 43 | #ax1 = fig.add_subplot(211) 44 | ax1 = fig.add_axes([0.1, 0.1, 0.4, 0.3]) 45 | 46 | ax1.plot(x_value,y_value,label=u"随机误差",ls='-',c='r',lw=1) 47 | ax1.set_xlim(0,len(x_value))#调节X轴范围 48 | ax1.set_ylim(ylim_min,ylim_max)#调节Y轴范围 49 | 50 | ax1.set_xticks(np.arange(0,len(x_value),20)) 51 | ax1.set_yticks(np.arange(yticks_min,yticks_max,ylim_setp)) 52 | ax1.set_xticklabels(['2015-02-01','2015-03-01','2015-04-02','2015-05-02','2015-06-02','2015-07-02','2015-08-02','2015-09-02','2015-10-02','2015-11-02'],fontsize='small') 53 | ax1.set_yticklabels([u'上限预警值',u'标准值',u'下限预警值']) 54 | ax1.set_title(u"对象式编程子图1") 55 | ax1.set_xlabel(u"日期") 56 | ax1.set_ylabel(u"数值") 57 | 58 | #ax2 = fig.add_subplot(212) 59 | ax2 = fig.add_axes([0.5, 0.5, 0.4, 0.3]) 60 | ax2.plot(x_value,y_value,label=u"随机误差",ls='-',c='y',lw=1) 61 | 62 | ax2.set_xlim(0,len(x_value))#调节X轴范围 63 | ax2.set_ylim(ylim_min,ylim_max)#调节Y轴范围 64 | 65 | ax2.set_xticks(np.arange(0,len(x_value),20)) 66 | ax2.set_yticks(np.arange(yticks_min,yticks_max,ylim_setp)) 67 | ax2.set_xticklabels(['2015-02-01','2015-03-01','2015-04-02','2015-05-02','2015-06-02','2015-07-02','2015-08-02','2015-09-02','2015-10-02','2015-11-02'],rotation=45,fontsize='small') 68 | ax2.set_yticklabels([u'上限预警值',u'标准值',u'下限预警值']) 69 | 70 | ax2.set_title(u"对象式编程子图2") 71 | ax2.set_xlabel(u"日期") 72 | ax2.set_ylabel(u"数值") 73 | plt.show() 74 | """ 75 | ''' 对象式编程 ''' 76 | 77 | 78 | """ subplots 遍历显示图形""" 79 | """ 80 | fig_ps,axes_ps = plt.subplots(2,3) 81 | print(axes_ps) 82 | for i in range(2): 83 | for j in range(3): 84 | axes_ps[i,j].hist(np.random.randn(500),bins=50,color='k',alpha=0.5) 85 | plt.show() 86 | """ 87 | """ subplots 遍历显示图形""" 88 | """ Artist 对象测试""" 89 | 90 | fig = plt.figure() 91 | ax = fig.add_subplot(111) 92 | line = ax.plot(x_value,y_value,label=u"随机误差",ls='-',c='r',lw=1) 93 | print("line",line) 94 | print("ax.line",ax.lines) 95 | 96 | plt.show() 97 | """ 98 | -------------------------------------------------------------------------------- /Chapter14/chapter14-wxPython tool.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import wx 6 | import matplotlib 7 | from matplotlib.figure import Figure 8 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 9 | 10 | class Panel(wx.Panel): 11 | def __init__(self,parent): 12 | wx.Panel.__init__(self,parent=parent, id=-1) 13 | 14 | self.figure = Figure() 15 | #self.am = self.figure.add_subplot(1,1,1) 16 | self.FigureCanvas = FigureCanvas(self, -1, self.figure)#figure加到FigureCanvas 17 | self.TopBoxSizer = wx.BoxSizer(wx.VERTICAL) 18 | self.TopBoxSizer.Add(self.FigureCanvas,proportion = -1, border = 2,flag = wx.ALL | wx.EXPAND) 19 | self.SetSizer(self.TopBoxSizer) 20 | 21 | class Frame(wx.Frame): 22 | def __init__(self): 23 | wx.Frame.__init__(self, parent = None, title = u'量化软件', size=(1000,600), 24 | style=wx.DEFAULT_FRAME_STYLE^wx.MAXIMIZE_BOX) 25 | #创建显示区面板 26 | self.DispPanel = Panel(self) 27 | 28 | #创建参数区面板 29 | self.ParaPanel = wx.Panel(self,-1) 30 | paraInput_Box = wx.StaticBox(self.ParaPanel, -1, u'参数输入') 31 | paraInput_Sizer = wx.StaticBoxSizer(paraInput_Box, wx.VERTICAL) 32 | 33 | Stock_Name_ComboBox = ["浙大网新", "高鸿股份", "天威视讯", "北方导航"] 34 | stockName_CMBO = wx.ComboBox(self.ParaPanel, -1, "浙大网新", choices = Stock_Name_ComboBox, style = wx.CB_READONLY|wx.CB_DROPDOWN) #股票名称 35 | stockCode_Text = wx.StaticText(self.ParaPanel, -1, u'股票名称') 36 | 37 | #日历控件选择数据周期 38 | self.dpcEndTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_ALLOWNONE)#结束时间 39 | self.dpcStartTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_SHOWCENTURY|wx.DP_ALLOWNONE)#起始时间 40 | DateTimeNow = wx.DateTime.Now()#wx.DateTime格式"03/03/18 00:00:00" 41 | self.dpcEndTime.SetValue(DateTimeNow) 42 | self.dpcStartTime.SetValue(DateTimeNow) 43 | stockData_Text = wx.StaticText(self.ParaPanel, -1, u'日期(Start-End)') 44 | 45 | paraInput_Sizer.Add(stockCode_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 46 | paraInput_Sizer.Add(stockName_CMBO, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 47 | paraInput_Sizer.Add(stockData_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 48 | paraInput_Sizer.Add(self.dpcStartTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 49 | paraInput_Sizer.Add(self.dpcEndTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 50 | 51 | RadioList = ["跳空缺口","金叉\死叉", "N日突破", "均线突破"] 52 | self.StratInputBox = wx.RadioBox(self.ParaPanel, -1, label=u'策略选取', choices=RadioList,majorDimension = 4, style = wx.RA_SPECIFY_ROWS) 53 | 54 | self.TextAInput = wx.TextCtrl(self.ParaPanel, -1, "交易信息提示:", style = wx.TE_MULTILINE|wx.TE_READONLY)#多行|只读 55 | 56 | vboxnetA = wx.BoxSizer(wx.VERTICAL)#纵向box 57 | vboxnetA.Add(paraInput_Sizer,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) #proportion参数控制容器尺寸比例 58 | vboxnetA.Add(self.StratInputBox,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) 59 | vboxnetA.Add(self.TextAInput,proportion=1,flag=wx.EXPAND|wx.ALL,border=2) 60 | self.ParaPanel.SetSizer(vboxnetA) 61 | 62 | #创建Right面板 63 | self.CtrlPanel = wx.Panel(self,-1) 64 | #创建FlexGridSizer布局网格 65 | self.FlexGridSizer=wx.FlexGridSizer(rows=3, cols=1, vgap=3, hgap=3) 66 | 67 | #实盘按钮 68 | self.Firmoffer = wx.Button(self.CtrlPanel,-1,"实盘") 69 | #选股按钮 70 | self.Stockpick = wx.Button(self.CtrlPanel,-1,"选股") 71 | #回测按钮 72 | self.Backtrace = wx.Button(self.CtrlPanel,-1,"回测") 73 | 74 | #加入Sizer中 75 | self.FlexGridSizer.Add(self.Firmoffer,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 76 | self.FlexGridSizer.Add(self.Stockpick,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 77 | self.FlexGridSizer.Add(self.Backtrace,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 78 | self.FlexGridSizer.SetFlexibleDirection(wx.BOTH) 79 | 80 | self.CtrlPanel.SetSizer(self.FlexGridSizer) 81 | 82 | self.HBoxPanel = wx.BoxSizer(wx.HORIZONTAL) 83 | self.HBoxPanel.Add(self.ParaPanel,proportion = 1.5, border = 2,flag = wx.EXPAND|wx.ALL) 84 | self.HBoxPanel.Add(self.DispPanel,proportion = 8, border = 2,flag = wx.EXPAND|wx.ALL ) 85 | self.HBoxPanel.Add(self.CtrlPanel,proportion = 1, border = 2,flag = wx.EXPAND|wx.ALL ) 86 | self.SetSizer(self.HBoxPanel) 87 | 88 | class App(wx.App): 89 | def OnInit(self): 90 | self.frame = Frame() 91 | self.frame.Show() 92 | self.SetTopWindow(self.frame) 93 | return True 94 | 95 | if __name__ == '__main__': 96 | app = App() 97 | app.MainLoop() 98 | 99 | -------------------------------------------------------------------------------- /Chapter15/chapter15-Get Stock Data.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pandas as pd 7 | import pandas_datareader.data as web 8 | import datetime 9 | #import tushare as ts 10 | 11 | df_stockload = web.DataReader("000001.SS", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 12 | #print(type(datetime.datetime.now().strftime('%Y-%m-%d'))) 13 | #df_stockload = ts.get_hist_data('sh',start='2017-01-01',end=datetime.datetime.now().strftime('%Y-%m-%d')) 14 | print (df_stockload.columns)#查看列名 15 | print (df_stockload.index)#查看索引 16 | print (df_stockload.describe())#查看各列数据描述性统计 17 | 18 | #绘制移动平均线 19 | df_stockload.Close.plot(c='b') 20 | df_stockload.Close.rolling(window=30).mean().plot(c='r') #pd.rolling_mean(df_stockload.Close,window=30).plot(c='r') 21 | df_stockload.Close.rolling(window=60).mean().plot(c='g') #pd.rolling_mean(df_stockload.Close,window=60).plot(c='g') 22 | plt.legend(['Close','30ave','60ave'],loc='best') 23 | plt.show() 24 | """ 25 | df_concept = ts.get_concept_classified()#概念分类 26 | print (df_concept.head(20)) 27 | """ 28 | -------------------------------------------------------------------------------- /Chapter16/chapter16-Disp Stock Data.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import matplotlib.dates as mdates 7 | import matplotlib.gridspec as gridspec#分割子图 8 | import mpl_finance as mpf #替换 import matplotlib.finance as mpf 9 | import pandas as pd 10 | import pandas_datareader.data as web 11 | import datetime 12 | import talib 13 | import tushare as ts 14 | 15 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 16 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 17 | 18 | df_stockload = web.DataReader("600797.SS", "yahoo", datetime.datetime(2018,1,1), datetime.date.today()) 19 | #print(type(datetime.datetime.now().strftime('%Y-%m-%d'))) 20 | #df_stockload = ts.get_hist_data('600797',start='2018-01-01',end=datetime.datetime.now().strftime('%Y-%m-%d')) 21 | #df_stockload = df_stockload.sort_index(ascending=False)#降序排序 22 | #df_stockload = df_stockload.sort_index()#升序排序 23 | 24 | #python3.7打印 25 | print (df_stockload.head())#查看前几行 26 | print (df_stockload.columns)#查看列名 27 | print (df_stockload.index)#查看索引 28 | print (df_stockload.describe())#查看各列数据描述性统计 29 | 30 | fig = plt.figure(figsize=(8,6), dpi=100,facecolor="white")#创建fig对象 31 | #fig.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.90, wspace=0.2, hspace=0) 32 | #graph_KAV = fig.add_subplot(1,1,1)#创建子图 33 | 34 | gs = gridspec.GridSpec(4, 1, left=0.05, bottom=0.2, right=0.96, top=0.96, wspace=None, hspace=0, height_ratios=[3.5,1,1,1]) 35 | graph_KAV = fig.add_subplot(gs[0,:]) 36 | graph_VOL = fig.add_subplot(gs[1,:]) 37 | graph_MACD = fig.add_subplot(gs[2,:]) 38 | graph_KDJ = fig.add_subplot(gs[3,:]) 39 | 40 | 41 | """ 绘制K线图 """ 42 | #方法1 43 | ohlc = [] 44 | ohlc = list(zip(np.arange(0,len(df_stockload.index)),df_stockload.Open,df_stockload.Close,df_stockload.High,df_stockload.Low))#使用zip方法生成数据列表 45 | mpf.candlestick_ochl(graph_KAV, ohlc, width=0.2, colorup='r', colordown='g', alpha=1.0)#绘制K线走势 46 | #方法2 47 | #mpf.candlestick2_ochl(graph_KAV, df_stockload.Open,df_stockload.Close,df_stockload.High,df_stockload.Low, width=0.5, colorup='r', colordown='g')#绘制K线走势 48 | """ 绘制K线图 """ 49 | 50 | """ 绘制移动平均线图 """ 51 | 52 | df_stockload['Ma20'] = df_stockload.Close.rolling(window=20).mean()#pd.rolling_mean(df_stockload.Close,window=20) 53 | df_stockload['Ma30'] = df_stockload.Close.rolling(window=30).mean()#pd.rolling_mean(df_stockload.Close,window=30) 54 | df_stockload['Ma60'] = df_stockload.Close.rolling(window=60).mean()#pd.rolling_mean(df_stockload.Close,window=60) 55 | 56 | numt = np.arange(0, len(df_stockload.index)) 57 | 58 | #绘制均线走势 59 | graph_KAV.plot(numt, df_stockload['Ma20'],'black', label='M20',lw=1.0) 60 | graph_KAV.plot(numt, df_stockload['Ma30'],'green',label='M30', lw=1.0) 61 | graph_KAV.plot(numt, df_stockload['Ma60'],'blue',label='M60', lw=1.0) 62 | graph_KAV.legend(loc='best') 63 | 64 | """ 绘制移动平均线图 """ 65 | 66 | #fig.suptitle('600797 浙大网新', fontsize = 14, fontweight='bold') 67 | graph_KAV.set_title(u"600797 浙大网新-日K线") 68 | #graph_KAV.set_xlabel("日期") 69 | graph_KAV.set_ylabel(u"价格") 70 | graph_KAV.set_xlim(0,len(df_stockload.index)) #设置一下x轴的范围 71 | graph_KAV.set_xticks(range(0,len(df_stockload.index),15))#X轴刻度设定 每15天标一个日期 72 | graph_KAV.grid(True,color='k') 73 | #graph_KAV.set_xticklabels([df_stockload.index.strftime('%Y-%m-%d')[index] for index in graph_KAV.get_xticks()])#标签设置为日期 74 | 75 | 76 | """ 绘制成交量图 """ 77 | 78 | graph_VOL.bar(numt, df_stockload.Volume,color=['g' if df_stockload.Open[x] > df_stockload.Close[x] else 'r' for x in range(0,len(df_stockload.index))]) 79 | 80 | graph_VOL.set_ylabel(u"成交量") 81 | #graph_VOL.set_xlabel("日期") 82 | graph_VOL.set_xlim(0,len(df_stockload.index)) #设置一下x轴的范围 83 | graph_VOL.set_xticks(range(0,len(df_stockload.index),15))#X轴刻度设定 每15天标一个日期 84 | #graph_VOL.set_xticklabels([df_stockload.index.strftime('%Y-%m-%d')[index] for index in graph_VOL.get_xticks()])#标签设置为日期 85 | 86 | """ 绘制成交量图 """ 87 | 88 | 89 | ''' 绘制MACD ''' 90 | 91 | macd_dif, macd_dea, macd_bar = talib.MACD(df_stockload['Close'].values, fastperiod=12, slowperiod=26, signalperiod=9) 92 | graph_MACD.plot(np.arange(0, len(df_stockload.index)), macd_dif, 'red', label='macd dif') #dif 93 | graph_MACD.plot(np.arange(0, len(df_stockload.index)), macd_dea, 'blue', label='macd dea') #dea 94 | #绘制BAR>0 柱状图 95 | bar_red = np.where(macd_bar>0, 2*macd_bar, 0) 96 | #绘制BAR<0 柱状图 97 | bar_green = np.where(macd_bar<0, 2*macd_bar, 0) 98 | graph_MACD.bar(np.arange(0, len(df_stockload.index)), bar_red, facecolor='red') 99 | graph_MACD.bar(np.arange(0, len(df_stockload.index)), bar_green, facecolor='green') 100 | graph_MACD.legend(loc='best',shadow=True, fontsize ='10') 101 | 102 | graph_MACD.set_ylabel(u"MACD") 103 | #graph_MACD.set_xlabel("日期") 104 | graph_MACD.set_xlim(0,len(df_stockload.index)) #设置一下x轴的范围 105 | graph_MACD.set_xticks(range(0,len(df_stockload.index),15))#X轴刻度设定 每15天标一个日期 106 | #graph_MACD.set_xticklabels([df_stockload.index.strftime('%Y-%m-%d')[index] for index in graph_MACD.get_xticks()])#标签设置为日期 107 | 108 | ''' 绘制MACD ''' 109 | 110 | ''' 绘制KDJ ''' 111 | 112 | xd = 9-1 113 | date = df_stockload.index.to_series() 114 | RSV = pd.Series(np.zeros(len(date)-xd),index=date.index[xd:]) 115 | Kvalue = pd.Series(0.0,index=RSV.index) 116 | Dvalue = pd.Series(0.0,index=RSV.index) 117 | Kvalue[0],Dvalue[0] = 50,50 118 | 119 | for day_ind in range(xd, len(df_stockload.index)): 120 | RSV[date[day_ind]] = (df_stockload.Close[day_ind] - df_stockload.Low[day_ind-xd:day_ind+1].min())/(df_stockload.High[day_ind-xd:day_ind+1].max()-df_stockload.Low[day_ind-xd:day_ind+1].min())*100 121 | if day_ind > xd: 122 | index = day_ind-xd 123 | Kvalue[index] = 2.0/3*Kvalue[index-1]+RSV[date[day_ind]]/3 124 | Dvalue[index] = 2.0/3*Dvalue[index-1]+Kvalue[index]/3 125 | df_stockload['RSV'] = RSV 126 | df_stockload['K'] = Kvalue 127 | df_stockload['D'] = Dvalue 128 | df_stockload['J'] = 3*Kvalue-2*Dvalue 129 | 130 | graph_KDJ.plot(np.arange(0, len(df_stockload.index)), df_stockload['K'], 'blue', label='K') #K 131 | graph_KDJ.plot(np.arange(0, len(df_stockload.index)), df_stockload['D'], 'g--', label='D') #D 132 | graph_KDJ.plot(np.arange(0, len(df_stockload.index)), df_stockload['J'], 'r-', label='J') #J 133 | graph_KDJ.legend(loc='best',shadow=True, fontsize ='10') 134 | 135 | graph_KDJ.set_ylabel(u"KDJ") 136 | graph_KDJ.set_xlabel("日期") 137 | graph_KDJ.set_xlim(0,len(df_stockload.index)) #设置一下x轴的范围 138 | graph_KDJ.set_xticks(range(0,len(df_stockload.index),15))#X轴刻度设定 每15天标一个日期 139 | graph_KDJ.set_xticklabels([df_stockload.index.strftime('%Y-%m-%d')[index] for index in graph_KDJ.get_xticks()])#标签设置为日期 140 | 141 | ''' 绘制KDJ ''' 142 | 143 | #X-轴每个ticker标签都向右倾斜45度 144 | 145 | for label in graph_KAV.xaxis.get_ticklabels(): 146 | #label.set_rotation(45) 147 | #label.set_fontsize(10)#设置标签字体 148 | label.set_visible(False) 149 | 150 | for label in graph_VOL.xaxis.get_ticklabels(): 151 | #label.set_rotation(45) 152 | #label.set_fontsize(10)#设置标签字体 153 | label.set_visible(False) 154 | 155 | for label in graph_MACD.xaxis.get_ticklabels(): 156 | #label.set_rotation(45) 157 | #label.set_fontsize(10)#设置标签字体 158 | label.set_visible(False) 159 | 160 | for label in graph_KDJ.xaxis.get_ticklabels(): 161 | label.set_rotation(45) 162 | label.set_fontsize(10)#设置标签字体 163 | 164 | plt.show() 165 | -------------------------------------------------------------------------------- /Chapter17/chapter17-Disp Excavate Data.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import matplotlib.dates as mdates 7 | import matplotlib.gridspec as gridspec#分割子图 8 | #import mpl_finance as mpf #替换 import matplotlib.finance as mpf 9 | import matplotlib.finance as mpf 10 | import pandas as pd 11 | import pandas_datareader.data as web 12 | import datetime 13 | import talib 14 | #import tushare as ts#cmd /k C:\Python27.15\python.exe "$(FULL_CURRENT_PATH)" & PAUSE & EXIT 更改为2.7.15下运行 15 | 16 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 17 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 18 | 19 | df_stockload = web.DataReader("600797.SS", "yahoo", datetime.datetime(2018,1,1), datetime.date.today()) 20 | #print(type(datetime.datetime.now().strftime('%Y-%m-%d'))) 21 | #df_stockload = ts.get_hist_data('600797',start='2018-01-01',end=datetime.datetime.now().strftime('%Y-%m-%d')) 22 | #df_stockload = df_stockload.sort_index(ascending=False)#降序排序 23 | #df_stockload = df_stockload.sort_index()#升序排序 24 | 25 | #python3.7打印 26 | print (df_stockload.head())#查看前几行 27 | print (df_stockload.columns)#查看列名 28 | print (df_stockload.index)#查看索引 29 | print (df_stockload.describe())#查看各列数据描述性统计 30 | 31 | 32 | fig = plt.figure(figsize=(8,6), dpi=100,facecolor="white")#创建fig对象 33 | fig.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.90, wspace=0.2, hspace=0) 34 | graph_KAV = fig.add_subplot(1,1,1)#创建子图 35 | 36 | 37 | """ 绘制K线图 """ 38 | #方法1 39 | ohlc = [] 40 | ohlc = list(zip(np.arange(0,len(df_stockload.index)),df_stockload.Open,df_stockload.Close,df_stockload.High,df_stockload.Low))#使用zip方法生成数据列表 41 | #mpf.candlestick_ochl(graph_KAV, ohlc, width=0.2, colorup='r', colordown='g', alpha=1.0)#绘制K线走势 Python3.7.1 42 | mpf.candlestick(graph_KAV, ohlc, width=0.2, colorup='r', colordown='g', alpha=1.0)#绘制K线走势 Python2.7.5 43 | #方法2 44 | #mpf.candlestick2_ochl(graph_KAV, df_stockload.Open,df_stockload.Close,df_stockload.High,df_stockload.Low, width=0.5, colorup='r', colordown='g')#绘制K线走势 Python3.7.1 45 | 46 | """ 绘制K线图 """ 47 | 48 | """ 绘制移动平均线图 """ 49 | 50 | df_stockload['Ma20'] = pd.rolling_mean(df_stockload.Close,window=20)#df_stockload.Close.rolling(window=20).mean() 51 | df_stockload['Ma30'] = pd.rolling_mean(df_stockload.Close,window=30)#df_stockload.Close.rolling(window=30).mean() 52 | df_stockload['Ma60'] = pd.rolling_mean(df_stockload.Close,window=60)#df_stockload.Close.rolling(window=60).mean() 53 | 54 | numt = np.arange(0, len(df_stockload.index)) 55 | 56 | #绘制收盘价走势 57 | graph_KAV.plot(numt, df_stockload['Ma20'],'black', label='M20',lw=1.0) 58 | graph_KAV.plot(numt, df_stockload['Ma30'],'green',label='M30', lw=1.0) 59 | graph_KAV.plot(numt, df_stockload['Ma60'],'blue',label='M60', lw=1.0) 60 | graph_KAV.legend(loc='best') 61 | """ 绘制移动平均线图 """ 62 | 63 | #fig.suptitle('600797 浙大网新', fontsize = 14, fontweight='bold') 64 | graph_KAV.set_title(u"600797 浙大网新-日K线") 65 | graph_KAV.set_xlabel(u"日期") 66 | graph_KAV.set_ylabel(u"价格") 67 | graph_KAV.set_xlim(0,len(df_stockload.index)) #设置一下x轴的范围 68 | graph_KAV.set_xticks(range(0,len(df_stockload.index),15))#X轴刻度设定 每15天标一个日期 69 | graph_KAV.grid(True,color='k') 70 | graph_KAV.set_xticklabels([df_stockload.index.strftime('%Y-%m-%d')[index] for index in graph_KAV.get_xticks()])#标签设置为日期 71 | 72 | for label in graph_KAV.xaxis.get_ticklabels(): 73 | label.set_rotation(45) 74 | label.set_fontsize(10)#设置标签字体 75 | 76 | """ 跳空缺口 基类""" 77 | """ 78 | # 新版类继承派生实现方式 79 | class JumpGap_Base: 80 | def __init__(self, stock_dat): 81 | self.stock_dat = stock_dat 82 | 83 | self.jump_pd = pd.DataFrame() 84 | self.stock_dat['changeRatio'] = self.stock_dat.Close.pct_change()*100#计算涨/跌幅 (今收-昨收)/昨收*100% 判断向上跳空缺口/向下跳空缺口 85 | self.stock_dat['preClose'] = self.stock_dat.Close.shift(1) #增加昨收序列 86 | 87 | def CaljumpGap(self): 88 | jump_threshold = self.stock_dat.Close.median()*0.01 #跳空阈值 收盘价中位数*0.01 89 | 90 | for kl_index in np.arange(0, self.stock_dat.shape[0]): 91 | today = self.stock_dat.ix[kl_index]#若版本提示已经弃用 可使用loc或iloc替换 92 | 93 | if (today.changeRatio > 0) and ((today.Low-today.preClose) > jump_threshold): 94 | #向上跳空 (今最低-昨收)/阈值 95 | today['jump_power'] = (today.Low-today.preClose)/jump_threshold 96 | #self.DrawjumpGap('up',kl_index,today) 97 | self.jump_pd = self.jump_pd.append(today) 98 | elif (today.changeRatio < 0) and ((today.preClose-today.High) > jump_threshold): 99 | #向下跳空 (昨收-今最高)/阈值 100 | today['jump_power'] = (today.High-today.preClose)/jump_threshold 101 | #self.DrawjumpGap('down',kl_index,today) 102 | self.jump_pd = self.jump_pd.append(today) 103 | 104 | print(self.jump_pd.filter(['jump_power','preClose','changeRatio','Close','Volume']))#按顺序只显示该列 105 | 106 | return self.jump_pd 107 | 108 | class draw_annotate: 109 | def __init__(self, draw_obj): 110 | self.am = draw_obj 111 | def draw_jumpgap(self,stockdat,jump_pd): 112 | ''' 绘制跳空缺口 ''' 113 | for kl_index in np.arange(0, jump_pd.shape[0]): 114 | today = jump_pd.ix[kl_index]#若版本提示已经弃用 可使用loc或iloc替换 115 | inday = stockdat.index.get_loc(jump_pd.index[kl_index]) 116 | if today['jump_power'] > 0: 117 | self.am.annotate('up',xy=(inday,today.Low*0.95),xytext=(inday, today.Low*0.9),arrowprops=dict(facecolor='red',shrink=0.1),horizontalalignment='left',verticalalignment='top') 118 | elif today['jump_power'] < 0: 119 | self.am.annotate('down',xy=(inday,today.High*1.05),xytext=(inday, today.High*1.1),arrowprops=dict(facecolor='green',shrink=0.1),horizontalalignment='left',verticalalignment='top') 120 | 121 | class JumpGap_Redef(JumpGap_Base): 122 | def __init__(self, stock_dat, draw_obj): 123 | JumpGap_Base.__init__(self, stock_dat) 124 | self.draw_way = draw_obj 125 | 126 | def filtjumpGap(self): 127 | #self.CaljumpGap() 128 | self.jump_pd = self.jump_pd[(np.abs(self.jump_pd.changeRatio) > 3)&(self.jump_pd.Volume > self.jump_pd.Volume.median())]#abs取绝对值 129 | 130 | print(self.jump_pd.filter(['jump_power','preClose','changeRatio','Close','Volume']))#按顺序只显示该列 131 | return self.jump_pd 132 | 133 | def DrawjumpGap(self): 134 | self.draw_way.draw_jumpgap(self.stock_dat,self.jump_pd) 135 | 136 | 137 | app_jumpd = JumpGap_Redef(df_stockload,draw_annotate(graph_KAV)) 138 | app_jumpd.CaljumpGap() 139 | app_jumpd.filtjumpGap() 140 | app_jumpd.DrawjumpGap() 141 | """ 142 | 143 | """ 检测跳空缺口 """ 144 | """ 145 | # 以前粗暴实现方式 146 | jump_pd = pd.DataFrame() 147 | df_stockload['changeRatio'] = df_stockload.Close.pct_change()*100#计算涨/跌幅 (今收-昨收)/昨收*100% 判断向上跳空缺口/向下跳空缺口 148 | df_stockload['preClose'] = df_stockload.Close.shift(1) #增加昨收序列 149 | jump_threshold = df_stockload.Close.median()*0.01 #跳空阈值 收盘价中位数*0.01 150 | 151 | for kl_index in np.arange(0, df_stockload.shape[0]): 152 | today = df_stockload.ix[kl_index]#若版本提示已经弃用 可使用loc或iloc替换 153 | 154 | if (today.changeRatio > 0) and ((today.Low-today.preClose) > jump_threshold): 155 | #向上跳空 (今最低-昨收)/阈值 156 | today['jump_power'] = (today.Low-today.preClose)/jump_threshold 157 | jump_pd = jump_pd.append(today) 158 | graph_KAV.annotate('up',xy=(kl_index,today.Low-0.2),xytext=(kl_index, today.Low-1),arrowprops=dict(facecolor='red',shrink=0.1),horizontalalignment='left',verticalalignment='top') 159 | 160 | elif (today.changeRatio < 0) and ((today.preClose-today.High) > jump_threshold): 161 | #向下跳空 (昨收-今最高)/阈值 162 | today['jump_power'] = (today.High-today.preClose)/jump_threshold 163 | jump_pd = jump_pd.append(today) 164 | graph_KAV.annotate('down',xy=(kl_index,today.High+0.2),xytext=(kl_index, today.High+1),arrowprops=dict(facecolor='green',shrink=0.1),horizontalalignment='left',verticalalignment='top') 165 | 166 | 167 | jump_pd = jump_pd[(np.abs(jump_pd.changeRatio) > 2)&(jump_pd.Volume > jump_pd.Volume.median())]#abs取绝对值 168 | format = lambda x: '%.2f' % x 169 | jump_pd = jump_pd.applymap(format)#处理后数据为str 170 | 171 | print(jump_pd.filter(['jump_power','preClose','changeRatio','Close','Volume']))#按顺序只显示该列 172 | """ 173 | """ 检测跳空缺口 """ 174 | 175 | 176 | 177 | """ 显示均线金叉/死叉提示符 """ 178 | 179 | #显示均线金叉/死叉提示符 180 | list_diff = np.sign(df_stockload['Ma20']-df_stockload['Ma60']) 181 | list_signal = np.sign(list_diff-list_diff.shift(1)) 182 | 183 | #print("list_diff",list_diff) 184 | #list_signal = list_signal[list_signal !=0] 185 | #list_signal = list_signal.dropna(axis=0,how='any')#去除NA值 186 | #print("list_signal",list_signal) 187 | 188 | #循环方式实现 189 | for i in range(len(list_signal)): 190 | if list_signal[i] < 0: 191 | graph_KAV.annotate(u"死叉", xy=(i, df_stockload['Ma20'][i]), xytext=(i, df_stockload['Ma20'][i]+1.5), 192 | arrowprops=dict(facecolor='green', shrink=0.2)) 193 | print(df_stockload.iloc[i]) 194 | if list_signal[i] > 0: 195 | graph_KAV.annotate(u"金叉", xy=(i, df_stockload['Ma20'][i]), xytext=(i, df_stockload['Ma20'][i]-1.5), 196 | arrowprops=dict(facecolor='red', shrink=0.2)) 197 | print(df_stockload.iloc[i]) 198 | 199 | """ 显示均线金叉/死叉提示符 """ 200 | 201 | plt.show() 202 | -------------------------------------------------------------------------------- /Chapter18/ZDWX600797.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanxiao1/Python-Quantitative-Trading/a7932429fe08388f9f9583c0bd51e30a85137937/Chapter18/ZDWX600797.csv -------------------------------------------------------------------------------- /Chapter18/chapter18-Disp Trade Profit.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import matplotlib.dates as mdates 7 | import matplotlib.gridspec as gridspec#分割子图 8 | import mpl_finance as mpf #替换 import matplotlib.finance as mpf 9 | import pandas as pd 10 | import pandas_datareader.data as web 11 | import datetime 12 | import talib 13 | import csv,os,codecs 14 | import tushare as ts 15 | 16 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 17 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 18 | 19 | #规整化 测试 #cmd /k C:\Users\Administrator\AppData\Local\Programs\Python\Python37-32\python.exe "$(FULL_CURRENT_PATH)" & PAUSE & EXIT 20 | 21 | def plot_trade(stock_df): 22 | 23 | if os.path.isfile('C:\\Users\\Administrator\\Desktop\\ZDWX600797.csv'): 24 | f=codecs.open('C:\\Users\\Administrator\\Desktop\\ZDWX600797.csv','rb','gb2312')#GB2312编码——>unicode 25 | #u = f.read() 26 | #print type(u)# 27 | reader = csv.DictReader(f) 28 | 29 | for row in reader: 30 | #print type(row["名称"]),row["名称"].encode('gb2312')# 31 | #start = stock_df[stock_df.index == buy_date].key.values[0]#起始时间 32 | #end = stock_df[stock_df.index == sell_date].key.values[0]#终止时间 33 | buy_date = row["买入时间"] 34 | sell_date = row["卖出时间"] 35 | hands_num = row["股数"] 36 | #print len("卖"),len(u"卖"),buy_date,sell_date,hands_num,row #3 / 1 37 | start = stock_df.index.get_loc(buy_date)#'2017-01-16' 38 | end = stock_df.index.get_loc(sell_date)#'2017-03-16' 39 | 40 | if stock_df.Close[end] < stock_df.Close[start]:#赔钱显示绿色 41 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='green',alpha=0.38) 42 | is_win = False 43 | else:#赚钱显示绿色 44 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='red',alpha=0.38) 45 | is_win = True 46 | plt.annotate('获利\n'+ hands_num+u'手' if is_win else '亏损\n'+hands_num+u'手',xy=(sell_date,stock_df.Close.asof(sell_date)),xytext=(sell_date, stock_df.Close.asof(sell_date)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 47 | #print(buy_date,sell_date) 48 | f.close() 49 | plt.plot(stock_df.index,stock_df.Close,color='r') 50 | 51 | """整个时间序列填充为底色blue 透明度alpha小于后标注区间颜色""" 52 | plt.fill_between(stock_df.index,0,stock_df.Close,color='blue',alpha=.08) 53 | 54 | plt.xlabel('time') 55 | plt.ylabel('close') 56 | plt.title(u'浙大网新') 57 | plt.grid(True) 58 | plt.ylim(np.min(stock_df.Close)-5,np.max(stock_df.Close)+5)#设置Y轴范围 59 | plt.legend(['Close'],loc='best') 60 | plt.show() 61 | 62 | def plot_trade_profit(stock_df): 63 | 64 | fig = plt.figure(figsize=(8,6), dpi=100,facecolor="white")#创建fig对象 65 | gs = gridspec.GridSpec(3, 1, left=0.05, bottom=0.15, right=0.96, top=0.96, wspace=None, hspace=0.2, height_ratios=[4.5,2,2]) 66 | graph_trade = fig.add_subplot(gs[0,:]) 67 | graph_total = fig.add_subplot(gs[1,:]) 68 | graph_profit = fig.add_subplot(gs[2,:]) 69 | 70 | if os.path.isfile('C:\\Users\\Administrator\\Desktop\\ZDWX600797.csv'): 71 | f=codecs.open('C:\\Users\\Administrator\\Desktop\\ZDWX600797.csv','rb','gb2312')#GB2312编码——>unicode 72 | reader = csv.DictReader(f) 73 | 74 | for row in reader: 75 | buy_date = row["买入时间"] 76 | sell_date = row["卖出时间"] 77 | hands_num = row["股数"] 78 | 79 | start = stock_df.index.get_loc(buy_date)#'2017-01-16' 80 | end = stock_df.index.get_loc(sell_date)#'2017-03-16' 81 | 82 | stock_df.loc[buy_date,'signal'] = 1 #买入股票符号 83 | stock_df.loc[buy_date,'price'] = float(row["买入价格"]) #买入股票价格 84 | 85 | stock_df.loc[sell_date,'signal'] = 0 #卖出股票符号 86 | stock_df.loc[sell_date,'price'] = float(row["卖出价格"]) #卖出股票价格 87 | 88 | if stock_df.Close[end] < stock_df.Close[start]:#赔钱显示绿色 89 | graph_trade.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='green',alpha=0.38) 90 | is_win = False 91 | else:#赚钱显示绿色 92 | graph_trade.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='red',alpha=0.38) 93 | is_win = True 94 | graph_trade.annotate('获利\n'+ hands_num+u'手' if is_win else '亏损\n'+hands_num+u'手',xy=(sell_date,stock_df.Close.asof(sell_date)),xytext=(sell_date, stock_df.Close.asof(sell_date)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 95 | 96 | f.close() 97 | graph_trade.plot(stock_df.index,stock_df.Close,color='r') 98 | 99 | """整个时间序列填充为底色blue 透明度alpha小于后标注区间颜色""" 100 | graph_trade.fill_between(stock_df.index,0,stock_df.Close,color='blue',alpha=.08) 101 | 102 | graph_trade.set_ylabel('close') 103 | graph_trade.set_title(u'浙大网新') 104 | graph_trade.grid(True) 105 | graph_trade.set_ylim(np.min(stock_df.Close)-5,np.max(stock_df.Close)+5)#设置Y轴范围 106 | graph_trade.legend(['Close'],loc='best') 107 | 108 | skip_days = 0 109 | cash_hold = 100000#初始资金 110 | posit_num = 0#持股数目 111 | market_total = 0#持股市值 112 | profit_curve = [] 113 | 114 | stock_df['keep'] = stock_df.signal 115 | stock_df['keep'].fillna(method = 'ffill',inplace = True) 116 | 117 | """ 计算基准收益 """ 118 | stock_df['benchmark_profit'] = np.log(stock_df.Close/stock_df.Close.shift(1)) 119 | print('benchmark_profit',stock_df['benchmark_profit']) 120 | """ 计算趋势突破策略收益 """ 121 | stock_df['trend_profit'] = stock_df.keep*stock_df.benchmark_profit 122 | 123 | for kl_index,today in stock_df.iterrows(): 124 | if today.signal == 1:# 买入 125 | start = stock_df.index.get_loc(kl_index) 126 | skip_days = -1 127 | posit_num = int(cash_hold/today.Close) 128 | cash_hold = 0 129 | elif today.signal == 0:# 卖出 130 | if skip_days == -1:#避免未买先卖 131 | end = stock_df.index.get_loc(kl_index) 132 | skip_days = 0 133 | cash_hold = int(posit_num*today.Close) 134 | market_total = 0 135 | if skip_days == -1: 136 | market_total = int(posit_num*today.Close) 137 | profit_curve.append(market_total) 138 | else: 139 | profit_curve.append(cash_hold) 140 | 141 | stock_df['total'] = profit_curve 142 | print(stock_df['total']) 143 | stock_df.total.plot(grid=True,ax = graph_total)#ax选择图形显示的子图 144 | graph_total.legend(['total'],loc='best') 145 | 146 | stock_df[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = graph_profit) 147 | graph_profit.set_xlabel('time') 148 | graph_profit.legend(['benchmark_profit','trend_profit'],loc='best') 149 | 150 | for label in graph_trade.xaxis.get_ticklabels(): 151 | label.set_visible(False) 152 | 153 | for label in graph_total.xaxis.get_ticklabels(): 154 | label.set_visible(False) 155 | 156 | for label in graph_profit.xaxis.get_ticklabels(): 157 | label.set_rotation(45) 158 | label.set_fontsize(10)#设置标签字体 159 | 160 | plt.show() 161 | """ 162 | stock = web.DataReader("AAPL", "yahoo", datetime.datetime(2017,1,1), datetime.date.today())#苹果公司数据获取 163 | """ 164 | stock = web.DataReader("600797.SS", "yahoo", datetime.datetime(2018,1,1), datetime.date.today()) 165 | 166 | #plot_trade(stock) 167 | plot_trade_profit(stock) 168 | -------------------------------------------------------------------------------- /Chapter19/chapter19-Strategy_AverBreak.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import matplotlib 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import pandas as pd 9 | import pandas_datareader.data as web 10 | import datetime 11 | 12 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 13 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 14 | #cmd /k C:\Users\Administrator\AppData\Local\Programs\Python\Python37-32\python.exe "$(FULL_CURRENT_PATH)" & PAUSE & EXIT 15 | class QuantAverBreak: 16 | def __init__(self): 17 | self.skip_days = 0 18 | self.cash_hold = 100000#初始资金 19 | self.posit_num = 0#持股数目 20 | self.market_total = 0#持股市值 21 | self.profit_curve = [] 22 | plt.figure(figsize=(25,12), dpi=80, facecolor="white") 23 | 24 | 25 | def run_factor_plot(self, stock_df): 26 | 27 | stock_df['Ma20'] = stock_df.Close.rolling(window=20).mean()#增加M60移动平均线 28 | list_diff = np.sign(stock_df.Close-stock_df.Ma20) 29 | stock_df['signal'] = np.sign(list_diff-list_diff.shift(1)) 30 | 31 | p1 = plt.subplot(2,1,1) 32 | plt.title(u'浙大网新') 33 | plt.ylim(np.min(stock_df.Close)-5,np.max(stock_df.Close)+5)#设置Y轴范围 34 | stock_df.Close.plot() 35 | stock_df.Ma20.plot(c='black') 36 | plt.legend(['Close','20ave'],loc='best') 37 | 38 | for kl_index,today in stock_df.iterrows(): 39 | #print "kl_index",kl_index 40 | #print "today",today 41 | if today.signal > 0:# 买入 42 | print("buy",kl_index) 43 | start = stock_df.index.get_loc(kl_index) 44 | self.skip_days = -1 45 | self.posit_num = int(self.cash_hold/today.Close) 46 | self.cash_hold = 0 47 | elif today.signal < 0:# 卖出 48 | if self.skip_days == -1:#避免未买先卖 49 | print("sell",kl_index) 50 | end = stock_df.index.get_loc(kl_index) 51 | self.skip_days = 0 52 | self.cash_hold = int(self.posit_num*today.Close) 53 | self.market_total = 0 54 | if stock_df.Close[end] < stock_df.Close[start]:#赔钱显示绿色 55 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='green',alpha=0.38) 56 | is_win = False 57 | else:#赚钱显示红色 58 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='red',alpha=0.38) 59 | is_win = True 60 | if self.skip_days == -1: 61 | self.market_total = int(self.posit_num*today.Close) 62 | self.profit_curve.append(self.market_total) 63 | else: 64 | self.profit_curve.append(self.cash_hold) 65 | p1 = plt.subplot(2,1,2) 66 | stock_df['profit'] = self.profit_curve 67 | stock_df.profit.plot() 68 | plt.legend(['profit'],loc='best') 69 | 70 | #plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0.25) 71 | plt.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.95, wspace=0.2, hspace=0) 72 | plt.show() 73 | 74 | 75 | stock = web.DataReader("600797.SS", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 76 | examp_trade = QuantAverBreak() 77 | examp_trade.run_factor_plot(stock) 78 | 79 | -------------------------------------------------------------------------------- /Chapter19/chapter19-Strategy_NdaysBreak.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import matplotlib 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import pandas as pd 9 | import pandas_datareader.data as web 10 | import datetime 11 | 12 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 13 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 14 | 15 | class QuantNdaysBreak: 16 | def __init__(self): 17 | self.skip_days = 0 18 | self.cash_hold = 100000#初始资金 19 | self.posit_num = 0#持股数目 20 | self.market_total = 0#持股市值 21 | self.profit_curve = [] 22 | plt.figure(figsize=(25,12), dpi=80, facecolor="white") 23 | #plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0.25) 24 | plt.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.95, wspace=0.2, hspace=0) 25 | 26 | def run_factor_plot(self, stock_df): 27 | N1 = 22 28 | N2 = 11 29 | stock_df['N1_High'] = stock_df.High.rolling(window=N1).max()#计算最近N1个交易日最高价 30 | expan_max = stock_df.Close.expanding().max() 31 | stock_df['N1_High'].fillna(value=expan_max,inplace=True)#目前出现过的最大值填充前N1个nan 32 | 33 | stock_df['N2_Low'] = stock_df.Low.rolling(window=N2).min()#计算最近N2个交易日最低价 34 | expan_min = stock_df.Close.expanding().min() 35 | stock_df['N2_Low'].fillna(value=expan_min,inplace=True)#目前出现过的最小值填充前N2个nan 36 | 37 | """ 收盘价超过N1最高价 买入股票持有""" 38 | buy_index = stock_df[stock_df.Close > stock_df.N1_High.shift(1)].index #报错 TypeError: '<' not supported between instances of 'float' and 'method' 39 | #print("buy_index",buy_index) 40 | stock_df.loc[buy_index,'signal'] = 1 41 | 42 | """ 收盘价超过N2最低价 卖出股票持有""" 43 | sell_index = stock_df[stock_df.Close < stock_df.N2_Low.shift(1)].index #报错 TypeError: '<' not supported between instances of 'float' and 'method' 44 | stock_df.loc[sell_index,'signal'] = 0 45 | 46 | stock_df['keep'] = stock_df.signal.shift(1) 47 | stock_df['keep'].fillna(method = 'ffill',inplace = True) 48 | 49 | """ 计算基准收益 """ 50 | stock_df['benchmark_profit'] = np.log(stock_df.Close/stock_df.Close.shift(1)) 51 | #stock_df['benchmark_profit'] = (stock_df.Close-stock_df.Close.shift(1))/stock_df.Close.shift(1) 52 | print('benchmark_profit',stock_df['benchmark_profit']) 53 | 54 | """ 计算趋势突破策略收益 """ 55 | stock_df['trend_profit'] = stock_df.keep*stock_df.benchmark_profit 56 | 57 | 58 | """ 可视化收益情况对比 """ 59 | p1 = plt.subplot(2,1,1) 60 | plt.title(u'浙大网新') 61 | plt.ylim(np.min(stock_df.Close)-5,np.max(stock_df.Close)+5)#设置Y轴范围 62 | stock_df.Close.plot(ax = p1) 63 | plt.legend(['Close'],loc='best') 64 | 65 | stock_df['watsignal'] = np.sign(stock_df['keep']-stock_df['keep'].shift(1)) 66 | 67 | for kl_index,today in stock_df.iterrows(): 68 | if today.watsignal == 1:# 买入 69 | self.skip_days = -1 70 | start = stock_df.index.get_loc(kl_index) 71 | elif today.watsignal == -1:# 卖出 72 | if self.skip_days == -1: 73 | self.skip_days = 0 74 | end = stock_df.index.get_loc(kl_index) 75 | if stock_df.Close[end] < stock_df.Close[start]:#赔钱显示绿色 76 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='green',alpha=0.38) 77 | else:#赚钱显示绿色 78 | plt.fill_between(stock_df.index[start:end],0,stock_df.Close[start:end],color='red',alpha=0.38) 79 | 80 | p2 = plt.subplot(2,1,2) 81 | stock_df[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = p2) 82 | plt.legend(['benchmark_profit','trend_profit'],loc='best') 83 | 84 | plt.show() 85 | 86 | 87 | stock = web.DataReader("000851.SZ", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 88 | examp_trade= QuantNdaysBreak() 89 | examp_trade.run_factor_plot(stock) 90 | 91 | -------------------------------------------------------------------------------- /Chapter20/chapter20-Strategy_MultiBreaks.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import matplotlib 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.dates as mdates 9 | import pandas as pd 10 | import pandas_datareader.data as web 11 | import datetime 12 | import copy 13 | 14 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 15 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 16 | 17 | class FactorBuyAverBreak: 18 | def __init__(self,**kwargs): 19 | self.xd = kwargs['xd'] 20 | 21 | def make_buy_order(self): 22 | buy_signal = True 23 | return buy_signal 24 | 25 | def fit_day(self,kl_index, today, stock_df): 26 | day_ind = stock_df.index.get_loc(kl_index) 27 | 28 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 29 | return False 30 | 31 | if today.Close > stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 32 | print('FactorBuyAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 33 | return self.make_buy_order() 34 | return False 35 | 36 | class FactorSellAverBreak: 37 | def __init__(self,**kwargs): 38 | self.xd = kwargs['xd'] 39 | 40 | def fit_sell_order(self): 41 | sell_signal = True 42 | return sell_signal 43 | 44 | def fit_day(self,kl_index, today, stock_df): 45 | day_ind = stock_df.index.get_loc(kl_index) 46 | 47 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 48 | return False 49 | 50 | if today.Close < stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 51 | print('FactorSellAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 52 | #print 'FactorSellAverBreak for data',stock_df.Close[day_ind-self.xd+1:day_ind+1] 53 | return self.fit_sell_order() 54 | return False 55 | 56 | class FactorBuyNdayBreak: 57 | def __init__(self,**kwargs): 58 | self.xd = kwargs['xd'] 59 | 60 | def make_buy_order(self): 61 | buy_signal = True 62 | return buy_signal 63 | 64 | def fit_day(self,kl_index, today, stock_df): 65 | day_ind = stock_df.index.get_loc(kl_index) 66 | 67 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 68 | return False 69 | 70 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].max(): 71 | print('FactorBuyNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].max()) 72 | return self.make_buy_order() 73 | return False 74 | 75 | class FactorSellNdayBreak: 76 | def __init__(self,**kwargs): 77 | self.xd = kwargs['xd'] 78 | 79 | def fit_sell_order(self): 80 | sell_signal = True 81 | return sell_signal 82 | 83 | def fit_day(self,kl_index, today, stock_df): 84 | day_ind = stock_df.index.get_loc(kl_index) 85 | 86 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 87 | return False 88 | 89 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].min(): 90 | print('FactorSellNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].min()) 91 | return self.fit_sell_order() 92 | return False 93 | 94 | 95 | class QuantPickTimeSys: 96 | def __init__(self, kl_pd, buy_factors, sell_factors): 97 | """ 98 | :param cap: 初始资金 99 | :param kl_pd: 择时时间段交易数据 100 | :param buy_factors: 买入因子序列,序列中的对象为dict,每一个dict针对一个具体因子 101 | :param sell_factors: 卖出因子序列,序列中的对象为dict,每一个dict针对一个具体因子 102 | """ 103 | 104 | # 回测阶段kl 105 | self.kl_pd = kl_pd 106 | 107 | # 初始化买入因子列表 108 | self.init_buy_factors(buy_factors) 109 | # 初始化卖出因子列表 110 | self.init_sell_factors(sell_factors) 111 | 112 | self.cash_hold = 100000#初始资金 113 | self.posit_num = 0#持股数目 114 | self.market_total = 0#持股市值 115 | self.profit_curve = [] 116 | plt.figure(figsize=(25,12), dpi=80, facecolor="white") 117 | 118 | 119 | def init_buy_factors(self, buy_factors): 120 | 121 | """ 122 | 通过buy_factors实例化各个买入因子 123 | :param buy_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 124 | :return: 125 | """ 126 | self.buy_factors = list() 127 | 128 | if buy_factors is None: 129 | return 130 | 131 | for factor_class in buy_factors: 132 | if factor_class is None: 133 | continue #执行下个循环 134 | if 'class' not in factor_class: 135 | raise ValueError('factor class key must name class!!') 136 | #print "before copy",id(factor_class) 137 | factor_class = copy.deepcopy(factor_class) 138 | #print "after copy",id(factor_class) 139 | class_fac = copy.deepcopy(factor_class['class']) 140 | del factor_class['class'] 141 | #print "del",id(factor_class) 142 | 143 | '''实例化买入因子''' 144 | factor = class_fac(**factor_class) 145 | 146 | if not isinstance(factor, FactorBuyAverBreak) and not isinstance(factor, FactorBuyNdayBreak):#判断factor为基于FactorBuyBreak实例 147 | raise TypeError('factor must base FactorBuyBreak!!') 148 | self.buy_factors.append(factor) 149 | 150 | def init_sell_factors(self, sell_factors): 151 | """ 152 | 通过sell_factors实例化各个卖出因子 153 | :param sell_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 154 | :return: 155 | """ 156 | self.sell_factors = list() 157 | 158 | if sell_factors is None: 159 | return 160 | 161 | for factor_class in sell_factors: 162 | if factor_class is None: 163 | continue #执行下个循环 164 | if 'class' not in factor_class: 165 | raise ValueError('factor class key must name class!!') 166 | factor_class = copy.deepcopy(factor_class) 167 | class_fac = copy.deepcopy(factor_class['class']) 168 | del factor_class['class'] 169 | 170 | '''实例化卖出因子''' 171 | factor = class_fac(**factor_class) 172 | 173 | if not isinstance(factor, FactorSellAverBreak) and not isinstance(factor, FactorSellNdayBreak):#判断factor为基于FactorBuyBreak实例 174 | raise TypeError('factor must base FactorSellBreak!!') 175 | self.sell_factors.append(factor) 176 | 177 | 178 | def _day_task(self, kl_index, today): 179 | 180 | fact_buy,fact_sell,sell_buf,buy_buf = 0,0,0,0 181 | 182 | for index, buy_factor in enumerate(self.buy_factors): 183 | #遍历所有买入因子 184 | buy_buf += buy_factor.fit_day(kl_index, today, self.kl_pd) 185 | 186 | fact_buy = 1 if (buy_buf == (index+1)) else 0 187 | 188 | for index, sell_factor in enumerate(self.sell_factors): 189 | #遍历所有卖出因子 190 | sell_buf += sell_factor.fit_day(kl_index, today, self.kl_pd) 191 | 192 | fact_sell = -1 if (sell_buf > 0) else 0 193 | 194 | return fact_buy or fact_sell 195 | 196 | def run_factor_plot(self): 197 | list_signal = [] 198 | is_win = False 199 | self.kl_pd['Ma30'] = self.kl_pd.Close.rolling(window=30).mean()#pd.rolling_mean(self.kl_pd.Close,window=30)#增加M30移动平均线 200 | #print self.kl_pd.loc['2017-12-27':'2018-02-09'].filter(['Close','Ma30']) 201 | 202 | #self.kl_pd.apply(self._day_task, axis=1) 203 | p1 = plt.subplot(3,1,1) 204 | self.kl_pd.Close.plot() 205 | self.kl_pd.Ma30.plot(c='black') 206 | 207 | plt.title(u'浙大网新') 208 | plt.ylim(np.min(self.kl_pd.Close)-5,np.max(self.kl_pd.Close)+5)#设置Y轴范围 209 | plt.xticks([]) #去掉横坐标值 210 | #plt.yticks([]) #去掉纵坐标值 211 | plt.legend(['Close','30ave'],loc='best') 212 | 213 | for kl_index,today in self.kl_pd.iterrows(): 214 | 215 | signal = self._day_task(kl_index, today) 216 | 217 | if signal > 0:# 买入 218 | if is_win == False:#空仓则买 219 | start = self.kl_pd.index.get_loc(kl_index) 220 | is_win = True 221 | 222 | self.posit_num = int(self.cash_hold/today.Close) 223 | self.cash_hold = 0 224 | print("Start order",kl_index) 225 | plt.annotate('B',xy=(kl_index,self.kl_pd.Close.asof(kl_index)),xytext=(kl_index, self.kl_pd.Close.asof(kl_index)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 226 | 227 | elif signal < 0:# 卖出 228 | if is_win == True:#避免未买先卖 229 | end = self.kl_pd.index.get_loc(kl_index) 230 | is_win = False 231 | print("End order",kl_index) 232 | self.cash_hold = int(self.posit_num*today.Close) 233 | self.market_total = 0 234 | 235 | if self.kl_pd.Close[end] < self.kl_pd.Close[start]:#赔钱显示绿色 236 | plt.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='green',alpha=0.38) 237 | 238 | else:#赚钱显示绿色 239 | plt.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='red',alpha=0.38) 240 | list_signal.append(is_win) 241 | 242 | if is_win == True: 243 | self.market_total = int(self.posit_num*today.Close) 244 | self.profit_curve.append(self.market_total) 245 | else: 246 | self.profit_curve.append(self.cash_hold) 247 | 248 | self.kl_pd['keep'] = list_signal 249 | self.kl_pd['keep'].fillna(method = 'ffill',inplace = True) 250 | 251 | """ 计算基准收益 """ 252 | self.kl_pd['benchmark_profit'] = np.log(self.kl_pd.Close/self.kl_pd.Close.shift(1)) 253 | """ 计算趋势突破策略收益 """ 254 | self.kl_pd['trend_profit'] = self.kl_pd.keep*self.kl_pd.benchmark_profit 255 | """ 可视化收益情况对比 """ 256 | p2 = plt.subplot(3,1,2) 257 | self.kl_pd[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = p2) 258 | plt.legend(['benchmark_profit','trend_profit'],loc='best') 259 | plt.xticks([]) #去掉横坐标值 260 | p3 = plt.subplot(3,1,3) 261 | p3.xaxis.set_minor_locator(mdates.WeekdayLocator(byweekday=(1),interval=1)) 262 | p3.xaxis.set_minor_formatter(mdates.DateFormatter('%d\n%a')) 263 | self.kl_pd['profit'] = self.profit_curve 264 | self.kl_pd.profit.plot() 265 | plt.legend(['profit'],loc='best') 266 | plt.xticks([]) #去掉横坐标值 267 | #plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0.25) 268 | plt.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.95, wspace=0, hspace=0.25) 269 | 270 | plt.show() 271 | 272 | buy_factors = [{'xd': 20,'class': FactorBuyNdayBreak}, 273 | {'xd': 30,'class': FactorBuyAverBreak}] 274 | sell_factors = [{'xd': 5,'class': FactorSellNdayBreak}, 275 | {'xd': 30,'class': FactorSellAverBreak}] 276 | 277 | stock = web.DataReader("600797.SS", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 278 | examp_trade= QuantPickTimeSys(stock,buy_factors,sell_factors) 279 | examp_trade.run_factor_plot() 280 | 281 | 282 | -------------------------------------------------------------------------------- /Chapter21/chapter21-Strategy_AtrStopSell.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import matplotlib 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.dates as mdates 9 | import pandas as pd 10 | import pandas_datareader.data as web 11 | import datetime 12 | import copy 13 | import talib 14 | 15 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 16 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 17 | 18 | class FactorBuyAverBreak: 19 | def __init__(self,**kwargs): 20 | self.xd = kwargs['xd'] 21 | 22 | def make_buy_order(self): 23 | buy_signal = True 24 | return buy_signal 25 | 26 | def fit_day(self,kl_index, stock_df): 27 | 28 | today = stock_df.ix[kl_index]#获取当天数据 29 | day_ind = stock_df.index.get_loc(kl_index) 30 | 31 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 32 | return False 33 | 34 | if today.Close > stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 35 | #print 'FactorBuyAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean() 36 | return self.make_buy_order() 37 | return False 38 | 39 | class FactorSellAverBreak: 40 | def __init__(self,**kwargs): 41 | self.xd = kwargs['xd'] 42 | 43 | def fit_sell_order(self): 44 | sell_signal = True 45 | return sell_signal 46 | 47 | def fit_day(self,kl_index, stock_df, *args): 48 | 49 | today = stock_df.ix[kl_index]#获取当天数据 50 | day_ind = stock_df.index.get_loc(kl_index) 51 | 52 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 53 | return False 54 | 55 | if today.Close < stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 56 | #print 'FactorSellAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean() 57 | #print 'FactorSellAverBreak for data',stock_df.Close[day_ind-self.xd+1:day_ind+1] 58 | return self.fit_sell_order() 59 | return False 60 | 61 | class FactorBuyNdayBreak: 62 | def __init__(self,**kwargs): 63 | self.xd = kwargs['xd'] 64 | 65 | def make_buy_order(self): 66 | buy_signal = True 67 | return buy_signal 68 | 69 | def fit_day(self,kl_index, stock_df): 70 | 71 | today = stock_df.ix[kl_index]#获取当天数据 72 | day_ind = stock_df.index.get_loc(kl_index) 73 | 74 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 75 | return False 76 | 77 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].max(): 78 | #print 'FactorBuyNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].max() 79 | return self.make_buy_order() 80 | return False 81 | 82 | class FactorSellNdayBreak: 83 | def __init__(self,**kwargs): 84 | self.xd = kwargs['xd'] 85 | 86 | def fit_sell_order(self): 87 | sell_signal = True 88 | return sell_signal 89 | 90 | def fit_day(self,kl_index, stock_df, *args): 91 | 92 | today = stock_df.ix[kl_index]#获取当天数据 93 | day_ind = stock_df.index.get_loc(kl_index) 94 | 95 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 96 | return False 97 | 98 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].min(): 99 | #print 'FactorSellNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].min() 100 | return self.fit_sell_order() 101 | return False 102 | 103 | class FactorSellAtrStop: 104 | def __init__(self,**kwargs): 105 | if 'stop_loss_n' in kwargs: 106 | #设置止损ATR倍数 107 | self.stop_loss_n = kwargs['stop_loss_n'] 108 | 109 | if 'stop_win_n' in kwargs: 110 | #设置止盈ATR倍数 111 | self.stop_win_n = kwargs['stop_win_n'] 112 | 113 | def fit_sell_order(self): 114 | sell_signal = True 115 | return sell_signal 116 | 117 | def fit_day(self,kl_index, stock_df, Buy_Price): 118 | 119 | today = stock_df.ix[kl_index]#获取当天数据 120 | 121 | if Buy_Price == 0:#无成交价格 122 | return False 123 | 124 | if (Buy_Price > today.Close) and ((Buy_Price - today.Close) > self.stop_loss_n * today.atr14): 125 | print('stop_loss_n',kl_index,today.Close,Buy_Price) 126 | return self.fit_sell_order() 127 | 128 | elif (Buy_Price < today.Close) and ((today.Close - Buy_Price) > self.stop_win_n * today.atr14): 129 | print('stop_win_n',kl_index,today.Close,Buy_Price) 130 | return self.fit_sell_order() 131 | else: 132 | return False 133 | 134 | class QuantPickTimeSys: 135 | def __init__(self, kl_pd, buy_factors, sell_factors): 136 | """ 137 | :param cap: 初始资金 138 | :param kl_pd: 择时时间段交易数据 139 | :param buy_factors: 买入因子序列,序列中的对象为dict,每一个dict针对一个具体因子 140 | :param sell_factors: 卖出因子序列,序列中的对象为dict,每一个dict针对一个具体因子 141 | """ 142 | 143 | # 回测阶段kl 144 | self.kl_pd = kl_pd 145 | 146 | # 初始化买入因子列表 147 | self.init_buy_factors(buy_factors) 148 | # 初始化卖出因子列表 149 | self.init_sell_factors(sell_factors) 150 | self.buy_price = 0#买入价格 151 | self.cash_hold = 100000#初始资金 152 | self.posit_num = 0#持股数目 153 | self.market_total = 0#持股市值 154 | self.profit_curve = [] 155 | plt.figure(figsize=(25,12), dpi=80, facecolor="white") 156 | 157 | 158 | def init_buy_factors(self, buy_factors): 159 | 160 | """ 161 | 通过buy_factors实例化各个买入因子 162 | :param buy_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 163 | :return: 164 | """ 165 | self.buy_factors = list() 166 | 167 | if buy_factors is None: 168 | return 169 | 170 | for factor_class in buy_factors: 171 | if factor_class is None: 172 | continue #执行下个循环 173 | if 'class' not in factor_class: 174 | raise ValueError('factor class key must name class!!') 175 | #print "before copy",id(factor_class) 176 | factor_class = copy.deepcopy(factor_class) 177 | #print "after copy",id(factor_class) 178 | class_fac = copy.deepcopy(factor_class['class']) 179 | del factor_class['class'] 180 | #print "del",id(factor_class) 181 | 182 | '''实例化买入因子''' 183 | factor = class_fac(**factor_class) 184 | 185 | if not isinstance(factor, FactorBuyAverBreak) and not isinstance(factor, FactorBuyNdayBreak): #判断factor为基于FactorBuyBreak实例 186 | raise TypeError('factor must base FactorBuyBreak!!') 187 | self.buy_factors.append(factor) 188 | 189 | def init_sell_factors(self, sell_factors): 190 | """ 191 | 通过sell_factors实例化各个卖出因子 192 | :param sell_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 193 | :return: 194 | """ 195 | self.sell_factors = list() 196 | 197 | if sell_factors is None: 198 | return 199 | 200 | for factor_class in sell_factors: 201 | if factor_class is None: 202 | continue #执行下个循环 203 | if 'class' not in factor_class: 204 | raise ValueError('factor class key must name class!!') 205 | factor_class = copy.deepcopy(factor_class) 206 | class_fac = copy.deepcopy(factor_class['class']) 207 | del factor_class['class'] 208 | 209 | '''实例化卖出因子''' 210 | factor = class_fac(**factor_class) 211 | 212 | if not isinstance(factor, FactorSellAverBreak) and not isinstance(factor, FactorSellNdayBreak) and not isinstance(factor, FactorSellAtrStop):#判断factor为基于FactorBuyBreak实例 213 | raise TypeError('factor must base FactorSellBreak!!') 214 | self.sell_factors.append(factor) 215 | 216 | 217 | def _day_task(self, kl_index, Buy_Price): 218 | 219 | fact_buy,fact_sell,sell_buf,buy_buf = 0,0,0,0 220 | 221 | for index, buy_factor in enumerate(self.buy_factors): 222 | #遍历所有买入因子 223 | buy_buf += buy_factor.fit_day(kl_index, self.kl_pd) 224 | 225 | fact_buy = 1 if (buy_buf == (index+1)) else 0 226 | 227 | for index, sell_factor in enumerate(self.sell_factors): 228 | #遍历所有卖出因子 229 | sell_buf += sell_factor.fit_day(kl_index, self.kl_pd, Buy_Price) 230 | 231 | fact_sell = -1 if (sell_buf > 0) else 0 232 | 233 | return fact_buy or fact_sell 234 | 235 | def run_factor_plot(self): 236 | 237 | list_signal = [] 238 | is_win = False 239 | self.kl_pd['Ma30'] = self.kl_pd.Close.rolling(window=30).mean()#pd.rolling_mean(self.kl_pd.Close,window=30)#增加M30移动平均线 240 | 241 | #print self.kl_pd.loc['2017-12-27':'2018-02-09'].filter(['Close','Ma30']) 242 | 243 | #self.kl_pd.apply(self._day_task, axis=1) 244 | 245 | self.kl_pd['atr14'] = talib.ATR(self.kl_pd.High.values,self.kl_pd.Low.values,self.kl_pd.Close.values,timeperiod=14)#计算ATR14 246 | self.kl_pd['atr21'] = talib.ATR(self.kl_pd.High.values,self.kl_pd.Low.values,self.kl_pd.Close.values,timeperiod=21)#计算ATR21 247 | 248 | #pd.DataFrame({'close':self.kl_pd.Close, 'atr14':self.kl_pd.atr14,'art21':self.kl_pd.art21,}) 249 | self.kl_pd['artwin'] = self.kl_pd.Close - self.kl_pd['atr14']*3#止盈对应的买入价位 250 | self.kl_pd['artloss'] = self.kl_pd.Close + self.kl_pd['atr14']*1#止损对应的买入价位 251 | 252 | p1 = plt.subplot(3,1,1) 253 | self.kl_pd.Close.plot() 254 | self.kl_pd.Ma30.plot(c='black') 255 | self.kl_pd.artwin.plot() 256 | self.kl_pd.artloss.plot() 257 | 258 | plt.title(u'浙大网新') 259 | plt.ylim(np.min(self.kl_pd.Close)-5,np.max(self.kl_pd.Close)+5)#设置Y轴范围 260 | plt.xticks([]) #去掉纵坐标值 261 | plt.legend(['Close','30ave','atrwin','artloss'],loc='best') 262 | 263 | 264 | for kl_index,today in self.kl_pd.iterrows(): 265 | 266 | signal = self._day_task(kl_index, self.buy_price) 267 | 268 | if signal > 0:# 买入 269 | if is_win == False:#空仓则买 270 | start = self.kl_pd.index.get_loc(kl_index) 271 | is_win = True 272 | self.buy_price = today.Close 273 | self.posit_num = int(self.cash_hold/today.Close) 274 | self.cash_hold = 0 275 | print("Start order",kl_index,today.Close) 276 | plt.annotate('B',xy=(kl_index,self.kl_pd.Close.asof(kl_index)),xytext=(kl_index, self.kl_pd.Close.asof(kl_index)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 277 | 278 | elif signal < 0:# 卖出 279 | if is_win == True:#避免未买先卖 280 | end = self.kl_pd.index.get_loc(kl_index) 281 | is_win = False 282 | self.buy_price = 0 283 | print("End order",kl_index,today.Close) 284 | self.cash_hold = int(self.posit_num*today.Close) 285 | self.market_total = 0 286 | 287 | if self.kl_pd.Close[end] < self.kl_pd.Close[start]:#赔钱显示绿色 288 | plt.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='green',alpha=0.38) 289 | 290 | else:#赚钱显示绿色 291 | plt.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='red',alpha=0.38) 292 | list_signal.append(is_win) 293 | 294 | if is_win == True: 295 | self.market_total = int(self.posit_num*today.Close) 296 | self.profit_curve.append(self.market_total) 297 | else: 298 | self.profit_curve.append(self.cash_hold) 299 | 300 | self.kl_pd['keep'] = list_signal 301 | self.kl_pd['keep'].fillna(method = 'ffill',inplace = True) 302 | 303 | """ 计算基准收益 """ 304 | self.kl_pd['benchmark_profit'] = np.log(self.kl_pd.Close/self.kl_pd.Close.shift(1)) 305 | """ 计算趋势突破策略收益 """ 306 | self.kl_pd['trend_profit'] = self.kl_pd.keep*self.kl_pd.benchmark_profit 307 | """ 可视化收益情况对比 """ 308 | p2 = plt.subplot(3,1,2) 309 | self.kl_pd[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = p2) 310 | plt.xticks([]) #去掉纵坐标值 311 | plt.legend(['benchmark_profit','trend_profit'],loc='best') 312 | 313 | p3 = plt.subplot(3,1,3) 314 | p3.xaxis.set_minor_locator(mdates.WeekdayLocator(byweekday=(1),interval=1)) 315 | p3.xaxis.set_minor_formatter(mdates.DateFormatter('%d\n%a')) 316 | self.kl_pd['profit'] = self.profit_curve 317 | self.kl_pd.profit.plot() 318 | plt.legend(['profit'],loc='best') 319 | plt.xticks([]) #去掉纵坐标值 320 | #plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0.25) 321 | plt.subplots_adjust(left=0.09,bottom=0.20, right=0.94,top=0.95, wspace=0, hspace=0.25) 322 | plt.show() 323 | 324 | """ 多因子策略测试 """ 325 | buy_factors = [{'xd': 20,'class': FactorBuyNdayBreak}, 326 | {'xd': 30,'class': FactorBuyAverBreak}] 327 | 328 | sell_factors = [{'xd': 5,'class': FactorSellNdayBreak}, 329 | {'xd': 30,'class': FactorSellAverBreak}] 330 | {'stop_loss_n': 0.8,'stop_win_n': 2,'class': FactorSellAtrStop}] 331 | 332 | stock = web.DataReader("600797.SS", "yahoo", datetime.datetime(2018,1,1), datetime.date.today()) 333 | examp_trade= QuantPickTimeSys(stock,buy_factors,sell_factors) 334 | examp_trade.run_factor_plot() 335 | 336 | 337 | -------------------------------------------------------------------------------- /Chapter22/chapter22-Strategy_CloLinearReg.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | 5 | import matplotlib 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.dates as mdates 9 | import pandas as pd 10 | import pandas_datareader.data as web 11 | import datetime 12 | import copy 13 | import statsmodels.api as sm 14 | from statsmodels import regression 15 | 16 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 17 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 18 | 19 | class FactorPickStockAng: 20 | def __init__(self,**kwargs): 21 | self.threshold_ang_min = -np.inf 22 | if 'threshold_ang_min' in kwargs: 23 | #设置最小角度阈值 24 | self.threshold_ang_min = kwargs['threshold_ang_min'] 25 | 26 | self.threshold_ang_max = np.inf 27 | if 'threshold_ang_max' in kwargs: 28 | #设置最大角度阈值 29 | self.threshold_ang_max = kwargs['threshold_ang_max'] 30 | 31 | plt.figure(figsize=(25,12), dpi=80, facecolor="white") 32 | 33 | def calc_regress_deg(self, y_arr): 34 | x= np.arange(0, len(y_arr)) 35 | #y值缩放到与x一个级别 36 | #zoom_factor =x.max()/y_arr.max() 37 | #y_arr = zoom_factor * y_arr 38 | 39 | x = sm.add_constant(x)#添加常数列1 40 | 41 | model = regression.linear_model.OLS(y_arr, x).fit()#使用OLS做拟合 42 | rad = model.params[1]#y = kx + b :params[1] = k 43 | deg = np.rad2deg(rad)#弧度转换为角度 44 | 45 | intercept = model.params[0]##y = kx + b :params[0] = b 46 | reg_y_fit = x * rad + intercept 47 | 48 | return deg, x, reg_y_fit, y_arr 49 | 50 | def fit_pick(self, symbols,show=True): 51 | 52 | for index, stockName in enumerate(symbols.keys()) : 53 | plt.subplot(len(symbols),1,index+1) 54 | #print "stockName",stockName,symbols[stockName] 55 | kl_pd = web.DataReader(symbols[stockName], "yahoo", datetime.datetime(2018,6,1), datetime.date.today()) 56 | kl_pd.fillna(method='bfill',inplace=True)#后一个数据填充NAN1 57 | print(kl_pd.head()) 58 | 59 | ang, x, reg_y_fit, y_arr = self.calc_regress_deg(kl_pd.Close)#计算走势角度 60 | 61 | if self.threshold_ang_min < ang < self.threshold_ang_max: 62 | if show: 63 | plt.plot(x, y_arr) 64 | plt.plot(x, reg_y_fit,'r') 65 | plt.xticks([]) #去掉纵坐标值 66 | plt.title(stockName + 'deg = ' + str(ang)) 67 | plt.legend(['close','linear'],loc='best') 68 | plt.show() 69 | 70 | """ 选股策略测试 """ 71 | pick_stocks = {u"浙大网新": "600797.SS",u"高鸿股份": "000851.SZ",u"开山股份": "300257.SZ",u"水晶光电": "002273.SZ"} 72 | 73 | examp_trade= FactorPickStockAng() 74 | examp_trade.fit_pick(pick_stocks) 75 | """ 选股策略测试 """ 76 | 77 | 78 | -------------------------------------------------------------------------------- /Chapter23-Python3.6+/GUI_QuantTradeSys.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import wx 5 | import wx.adv 6 | import numpy as np 7 | import pandas as pd 8 | import pandas_datareader.data as web 9 | import matplotlib 10 | import matplotlib.pyplot as plt 11 | from matplotlib.figure import Figure 12 | import matplotlib.dates as mdates 13 | import mpl_finance as mpf #替换 import matplotlib.finance as mpf 14 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 15 | import matplotlib.gridspec as gridspec#分割子图 16 | import datetime 17 | import talib 18 | import csv,os 19 | import codecs 20 | 21 | from RedefPanelMod import MPL_Panel_Base,Loop_Panel_Base 22 | from StockDataMod import GetStockDatPro 23 | from IndicatStrateMod import Excave_Indic_Base, QuantPickTimeSys,FactorPickStockAng 24 | 25 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 26 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 27 | 28 | class UserDialog(wx.Dialog):# user-defined 29 | 30 | def __init__(self,parent,text): 31 | wx.Dialog.__init__(self,parent,-1,u"选股提示",size=(400,500),style=wx.CAPTION|wx.CLOSE_BOX|wx.MAXIMIZE_BOX|wx.MINIMIZE_BOX) 32 | 33 | sizer = wx.BoxSizer(wx.VERTICAL) 34 | 35 | pstock_Text = wx.StaticText(self, -1, u'选股策略筛选结果') 36 | pstock_Text.SetFont(wx.Font(18,wx.DEFAULT,wx.NORMAL,wx.BOLD)) 37 | pstock_sure = wx.TextCtrl(self, -1, "角度值:\n",size=(350,300),style = wx.TE_MULTILINE|wx.TE_READONLY)#多行|只读 38 | pstock_sure.SetFont(wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD)) 39 | 40 | okbtn = wx.Button(self,wx.ID_OK,u"确认") 41 | okbtn.SetDefault() 42 | 43 | sizer.Add(pstock_Text,flag=wx.ALIGN_CENTER) 44 | sizer.Add(pstock_sure,flag=wx.ALIGN_CENTER) 45 | sizer.Add(okbtn,flag=wx.ALIGN_CENTER) 46 | self.SetSizer(sizer) 47 | for i in text:pstock_sure.AppendText(i) 48 | 49 | 50 | class Frame(wx.Frame): 51 | def __init__(self): 52 | wx.Frame.__init__(self, parent = None, title = u'量化软件', size=(1500,800), 53 | style=wx.DEFAULT_FRAME_STYLE^wx.MAXIMIZE_BOX) 54 | #创建显示区面板 55 | self.DispPanel = MPL_Panel_Base(self) 56 | self.BackPanel = Loop_Panel_Base(self) 57 | self.am = self.DispPanel.am 58 | self.vol = self.DispPanel.vol 59 | self.devol = self.DispPanel.devol 60 | self.macd = self.DispPanel.macd 61 | 62 | #创建参数区面板 63 | self.ParaPanel = wx.Panel(self,-1) 64 | 65 | paraInput_Box = wx.StaticBox(self.ParaPanel, -1, u'参数输入') 66 | paraInput_Sizer = wx.StaticBoxSizer(paraInput_Box, wx.VERTICAL) 67 | self.StNameCodedict = {u"开山股份":"300257.SZ",u"浙大网新":"600797.SS",u"水晶光电":"002273.SZ", u"高鸿股份":"000851.SZ"} 68 | 69 | #初始化股票代码变量 70 | self.stockName_Val = u"开山股份" 71 | self.stockCode_Val = self.StNameCodedict[self.stockName_Val] 72 | 73 | self.stockName_CMBO = wx.ComboBox(self.ParaPanel, -1,self.stockName_Val, choices = list(self.StNameCodedict.keys()), style = wx.CB_READONLY|wx.CB_DROPDOWN) #股票名称 74 | stockCode_Text = wx.StaticText(self.ParaPanel, -1, u'股票名称') 75 | 76 | #策略选取 77 | strate_Text = wx.StaticText(self.ParaPanel, -1, u'策略名称') 78 | strate_Combo_Val = [u"双趋势融合", u"阿尔法", u"布林带"] 79 | self.pickstrate_Val = u"双趋势融合" 80 | self.pickstrate_CMBO = wx.ComboBox(self.ParaPanel, -1, self.pickstrate_Val, choices = strate_Combo_Val, style = wx.CB_READONLY|wx.CB_DROPDOWN) #策略名称 81 | 82 | #日历控件选择数据周期 83 | #self.dpcEndTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_SHOWCENTURY|wx.DP_ALLOWNONE)#结束时间 84 | #self.dpcStartTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_SHOWCENTURY|wx.DP_ALLOWNONE)#起始时间 85 | self.dpcEndTime = wx.adv.DatePickerCtrl(self.ParaPanel, -1,style = wx.adv.DP_DROPDOWN|wx.adv.DP_SHOWCENTURY|wx.adv.DP_ALLOWNONE)#结束时间 86 | self.dpcStartTime = wx.adv.DatePickerCtrl(self.ParaPanel, -1,style = wx.adv.DP_DROPDOWN|wx.adv.DP_SHOWCENTURY|wx.adv.DP_ALLOWNONE)#起始时间 87 | 88 | DateTimeNow = wx.DateTime.Now()#wx.DateTime格式"03/03/18 00:00:00" 89 | 90 | self.dpcEndTime.SetValue(DateTimeNow) 91 | #DateTimeNow.SetYear(DateTimeNow.Year-1) 92 | DateTimeNow.SetYear(DateTimeNow.year - 1) 93 | self.dpcStartTime.SetValue(DateTimeNow) 94 | stockData_Text = wx.StaticText(self.ParaPanel, -1, u'日期(Start-End)') 95 | 96 | #初始化时间变量 97 | dateVal = self.dpcStartTime.GetValue() 98 | self.stockSdate_Val = datetime.datetime(dateVal.year,dateVal.month+1,dateVal.day) 99 | dateVal = self.dpcEndTime.GetValue() 100 | self.stockEdate_Val = datetime.datetime(dateVal.year,dateVal.month+1,dateVal.day) 101 | 102 | paraInput_Sizer.Add(stockCode_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 103 | paraInput_Sizer.Add(self.stockName_CMBO, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 104 | paraInput_Sizer.Add(stockData_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 105 | paraInput_Sizer.Add(self.dpcStartTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 106 | paraInput_Sizer.Add(self.dpcEndTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 107 | paraInput_Sizer.Add(strate_Text, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 108 | paraInput_Sizer.Add(self.pickstrate_CMBO, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 109 | 110 | RadioList = ["不显示","跳空缺口", "金叉/死叉", "N日突破"] 111 | self.StratInputBox = wx.RadioBox(self.ParaPanel, -1, label=u'指标提示', choices=RadioList,majorDimension = 4, style = wx.RA_SPECIFY_ROWS) 112 | self.StratInputBox.Bind(wx.EVT_RADIOBOX,self.OnRadioBox_Indicator) 113 | #初始化指标变量 114 | self.IndicatInput_Val = self.StratInputBox.GetStringSelection() 115 | 116 | self.TextAInput = wx.TextCtrl(self.ParaPanel, -1, "交易信息提示:", style = wx.TE_MULTILINE|wx.TE_READONLY)#多行|只读 117 | 118 | vboxnetA = wx.BoxSizer(wx.VERTICAL)#纵向box 119 | vboxnetA.Add(paraInput_Sizer,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) #proportion参数控制容器尺寸比例 120 | vboxnetA.Add(self.StratInputBox,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) 121 | vboxnetA.Add(self.TextAInput,proportion=1,flag=wx.EXPAND|wx.ALL,border=2) 122 | self.ParaPanel.SetSizer(vboxnetA) 123 | 124 | #创建Right面板 125 | self.CtrlPanel = wx.Panel(self,-1) 126 | #创建FlexGridSizer布局网格 127 | self.FlexGridSizer=wx.FlexGridSizer(rows=3, cols=1, vgap=3, hgap=3) 128 | 129 | #行情按钮 130 | self.Firmoffer = wx.Button(self.CtrlPanel,-1,"行情") 131 | self.Firmoffer.Bind(wx.EVT_BUTTON,self.FirmEvent)#绑定行情按钮事件 132 | #选股按钮 133 | self.Stockpick = wx.Button(self.CtrlPanel,-1,"选股") 134 | self.Stockpick.Bind(wx.EVT_BUTTON,self.PstockpEvent)#绑定选股按钮事件 135 | #回测按钮 136 | self.Backtrace = wx.Button(self.CtrlPanel,-1,"回测") 137 | self.Backtrace.Bind(wx.EVT_BUTTON,self.BackEvent)#绑定回测按钮事件 138 | 139 | #加入Sizer中 140 | self.FlexGridSizer.Add(self.Firmoffer,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 141 | self.FlexGridSizer.Add(self.Stockpick,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 142 | self.FlexGridSizer.Add(self.Backtrace,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 143 | self.FlexGridSizer.SetFlexibleDirection(wx.BOTH) 144 | 145 | self.CtrlPanel.SetSizer(self.FlexGridSizer) 146 | 147 | self.HBoxPanel = wx.BoxSizer(wx.HORIZONTAL) 148 | self.HBoxPanel.Add(self.ParaPanel,proportion = 1.5, border = 2,flag = wx.EXPAND|wx.ALL) 149 | self.HBoxPanel.Add(self.DispPanel,proportion = 8, border = 2,flag = wx.EXPAND|wx.ALL ) 150 | self.HBoxPanel.Add(self.CtrlPanel,proportion = 1, border = 2,flag = wx.EXPAND|wx.ALL ) 151 | self.SetSizer(self.HBoxPanel) 152 | 153 | def ProcessStock(self): 154 | #df_stockload = web.DataReader("600797.SS", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 155 | df_stockload = GetStockDatPro(self.stockCode_Val,self.stockSdate_Val, self.stockEdate_Val) 156 | 157 | """ 绘制移动平均线图 """ 158 | #self.am.plot(self.numic[0:self.butNum],self.close[0:self.butNum],'#0f0ff0',linewidth=1.0) 159 | 160 | dispCont_List = [] 161 | 162 | examp_trade= Excave_Indic_Base() 163 | if self.IndicatInput_Val == u"金叉/死叉": 164 | dispCont_pd,dispCont_List = examp_trade.plot_Aver_Cross(df_stockload) 165 | self.DispPanel.draw_avercross(df_stockload,dispCont_pd) 166 | elif self.IndicatInput_Val == u"跳空缺口": 167 | dispCont_pd,dispCont_List = examp_trade.plot_Jump_Thrd(df_stockload) 168 | self.DispPanel.draw_jumpgap(df_stockload,dispCont_pd) 169 | elif self.IndicatInput_Val == u"N日突破": 170 | dispCont_pd,dispCont_List = examp_trade.plot_Ndays_Break(df_stockload) 171 | self.DispPanel.draw_ndaysbreak(df_stockload,dispCont_pd) 172 | else: 173 | dispCont_List = dispCont_List 174 | 175 | self.TextAInput.SetValue(u"指标提示信息如下:"+'\n') 176 | for i in dispCont_List:self.TextAInput.AppendText(i) 177 | 178 | numic = np.arange(0,len(df_stockload.index)) 179 | butNum = len(df_stockload.index) 180 | self.DispPanel.xylabel_tick_lim(self.stockName_Val,df_stockload.index) 181 | self.DispPanel.draw_subgraph(df_stockload,numic) 182 | 183 | def ProcessLoop(self): 184 | 185 | df_stockload = GetStockDatPro(self.stockCode_Val,self.stockSdate_Val, self.stockEdate_Val) 186 | dispCont_List = [] 187 | if self.pickstrate_Val == u"双趋势融合": 188 | #多趋势融合策略执行 """ 189 | examp_trade= QuantPickTimeSys(df_stockload) 190 | dispCont_List = examp_trade.run_factor_plot(self.BackPanel.trade,self.BackPanel.total,self.BackPanel.profit) 191 | else: 192 | #执行其他策略 193 | pass 194 | 195 | self.TextAInput.SetValue(u"策略提示信息如下:"+'\n') 196 | for i in dispCont_List:self.TextAInput.AppendText(i) 197 | self.BackPanel.xylabel_tick_lim(self.stockName_Val) 198 | 199 | def reFlashLoop(self): 200 | self.BackPanel.clear_subgraph()#必须清理图形才能显示下一幅图 201 | self.ProcessLoop() 202 | self.BackPanel.update_subgraph()#必须刷新才能显示下一幅图 203 | 204 | def reFlashFrame(self): 205 | self.DispPanel.clear_subgraph()#必须清理图形才能显示下一幅图 206 | self.ProcessStock() 207 | self.DispPanel.update_subgraph()#必须刷新才能显示下一幅图 208 | 209 | def FirmEvent(self,event): 210 | #显示行情面板 211 | self.HBoxPanel.Hide(self.BackPanel) 212 | self.HBoxPanel.Replace(self.BackPanel,self.DispPanel) 213 | self.HBoxPanel.Show(self.DispPanel) 214 | #self.HBoxPanel.Remove(self.BackPanel) 215 | self.SetSizer(self.HBoxPanel) 216 | self.HBoxPanel.Layout() 217 | 218 | #获取列表股票代码和起始时间 219 | self.stockName_Val = self.stockName_CMBO.GetString(self.stockName_CMBO.GetSelection()) 220 | self.stockCode_Val = self.StNameCodedict[self.stockName_Val] 221 | 222 | dateVal = self.dpcStartTime.GetValue() 223 | self.stockSdate_Val = datetime.datetime(dateVal.year,dateVal.month+1,dateVal.day) 224 | dateVal = self.dpcEndTime.GetValue() 225 | self.stockEdate_Val = datetime.datetime(dateVal.year,dateVal.month+1,dateVal.day) 226 | 227 | self.reFlashFrame() 228 | 229 | def BackEvent(self,event): 230 | #显示回测面板 231 | self.HBoxPanel.Hide(self.DispPanel) 232 | self.HBoxPanel.Replace(self.DispPanel,self.BackPanel) 233 | self.HBoxPanel.Show(self.BackPanel) 234 | #self.HBoxPanel.Remove(self.DispPanel) 235 | self.SetSizer(self.HBoxPanel) 236 | self.HBoxPanel.Layout() 237 | 238 | #获取策略名称 239 | self.pickstrate_Val = self.pickstrate_CMBO.GetString(self.pickstrate_CMBO.GetSelection()) 240 | self.reFlashLoop() 241 | 242 | def PstockpEvent(self,event): 243 | dispCont_List = [] 244 | """ 选股策略执行 """ 245 | examp_trade= FactorPickStockAng() 246 | for index, stockName in enumerate(self.StNameCodedict.keys()) : 247 | print("stockName",stockName,self.StNameCodedict[stockName]) 248 | df_stockload = GetStockDatPro(self.StNameCodedict[stockName],self.stockSdate_Val, self.stockEdate_Val) 249 | df_stockload.fillna(method='bfill',inplace=True)#后一个数据填充NAN1 250 | dispCont_List.append(stockName+'\n'+examp_trade.fit_pick(df_stockload.Close)+'\n') 251 | print(dispCont_List) 252 | """ 选股策略执行 """ 253 | """ 自定义提示框 """ 254 | myPickStock = UserDialog(self,dispCont_List) 255 | if myPickStock.ShowModal() == wx.ID_OK: 256 | pass 257 | else: 258 | pass 259 | """ 自定义提示框 """ 260 | 261 | def OnRadioBox_Indicator(self,event): 262 | self.IndicatInput_Val = self.StratInputBox.GetStringSelection() 263 | 264 | 265 | class App(wx.App): 266 | def OnInit(self): 267 | self.frame = Frame() 268 | self.frame.ProcessStock() 269 | self.frame.Show() 270 | self.SetTopWindow(self.frame) 271 | return True 272 | 273 | if __name__ == '__main__': 274 | app = App() 275 | app.MainLoop() 276 | 277 | -------------------------------------------------------------------------------- /Chapter23-Python3.6+/IndicatStrateMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import pandas as pd 6 | import pandas_datareader.data as web 7 | import statsmodels.api as sm 8 | from statsmodels import regression 9 | import datetime 10 | import csv,os 11 | import codecs 12 | import talib 13 | import copy 14 | 15 | class Excave_Indic_Base: 16 | def __init__(self): 17 | #挖掘衍生技术指标 18 | pass 19 | 20 | def plot_Aver_Cross(self, stock_df): 21 | #显示均线金叉/死叉提示符 22 | list_diff = np.sign(stock_df['Ma20']-stock_df['Ma60']) 23 | list_signal = np.sign(list_diff-list_diff.shift(1)) 24 | #print "list_diff",list_diff 25 | list_signal = list_signal[list_signal !=0] 26 | list_signal = list_signal.dropna(axis=0,how='any')#去除NA值 27 | #print "list_signal",list_signal 28 | 29 | dispCont_List = ["M20&M60 金叉:\n"+"日期:"+list_signal.index[x].strftime('%Y-%m-%d')+'\n' if list_signal[x] > 0 else "M20&M60 死叉:\n"+"日期:"+list_signal.index[x].strftime('%Y-%m-%d')+'\n' for x in range(0,len(list_signal.index))]#金叉时间 30 | 31 | return list_signal,dispCont_List 32 | 33 | def plot_Jump_Thrd(self, stock_df): 34 | 35 | stock_df['changeRatio'] = stock_df.Close.pct_change()*100#计算涨/跌幅 (今收-昨收)/昨收*100% 判断向上跳空缺口/向下跳空缺口 36 | stock_df['preClose'] = stock_df.Close.shift(1) #增加昨收序列 37 | 38 | jump_threshold = stock_df.Close.median()*0.01 #跳空阈值 收盘价中位数*0.03 39 | #print "jump_threshold",jump_threshold 40 | jump_pd = pd.DataFrame() 41 | 42 | for kl_index in np.arange(0, stock_df.shape[0]): 43 | today = stock_df.ix[kl_index] 44 | """ 检测跳空缺口 """ 45 | if (today.changeRatio > 0) and ((today.Low-today.preClose) > jump_threshold): 46 | #向上跳空 (今最低-昨收)/阈值 47 | today['jump_power'] = (today.Low-today.preClose)/jump_threshold 48 | jump_pd = jump_pd.append(today) 49 | 50 | elif (today.changeRatio < 0) and ((today.preClose-today.High) > jump_threshold): 51 | #向下跳空 (昨收-今最高)/阈值 52 | today['jump_power'] = (today.High-today.preClose)/jump_threshold 53 | jump_pd = jump_pd.append(today) 54 | 55 | jump_pd = jump_pd[(np.abs(jump_pd.changeRatio) > 2)&(jump_pd.Volume > 20000000)]#abs取绝对值 56 | 57 | dispCont_List = ["向上跳空:\n"+"日期:"+jump_pd.index[x].strftime('%Y-%m-%d')+'\n'+"缺口值:"+str('%.2f'%jump_pd.jump_power[x])+'\n' if jump_pd.jump_power[x] > 0 else "向下跳空:\n"+"日期:"+jump_pd.index[x].strftime('%Y-%m-%d')+'\n'+"缺口值:"+str('%.2f'%jump_pd.jump_power[x])+'\n' for x in range(0,len(jump_pd.index))] 58 | 59 | print(jump_pd.filter(['jump_power','preClose','changeRatio','Close','Volume']))#按顺序只显示该列 60 | return jump_pd, dispCont_List 61 | 62 | 63 | def plot_Ndays_Break(self, stock_df): 64 | N1 = 42 65 | N2 = 30 66 | #stock_df['N1_High'] = pd.rolling_max(stock_df.High,window=N1)#计算最近N1个交易日最高价 67 | stock_df['N1_High'] = stock_df.High.rolling(window=N1).max() #计算最近N1个交易日最高价 68 | stock_df['N1_High'] = stock_df['N1_High'].shift(1) 69 | #expan_max = pd.expanding_max(stock_df.Close) 70 | expan_max = stock_df.Close.expanding().max() 71 | stock_df['N1_High'].fillna(value=expan_max,inplace=True)#目前出现过的最大值填充前N1个nan 72 | 73 | #stock_df['N2_Low'] = pd.rolling_min(stock_df.Low,window=N2)#计算最近N2个交易日最低价 74 | stock_df['N2_Low'] = stock_df.Low.rolling(window=N2).min() # 计算最近N2个交易日最低价 75 | stock_df['N2_Low'] = stock_df['N2_Low'].shift(1) 76 | #expan_min = pd.expanding_min(stock_df.Close) 77 | expan_min = stock_df.Close.expanding().min() 78 | stock_df['N2_Low'].fillna(value=expan_min,inplace=True)#目前出现过的最小值填充前N2个nan 79 | 80 | dispCont_List = [] 81 | break_pd = pd.DataFrame() 82 | 83 | for kl_index in np.arange(0, stock_df.shape[0]): 84 | today = stock_df.ix[kl_index] 85 | """ 收盘价超过N2最低价 卖出股票持有""" 86 | if today['Close'] < today['N2_Low']: 87 | break_pd = break_pd.append(today) 88 | dispCont_List.append("向下突破:"+stock_df.index[kl_index].strftime('%Y-%m-%d')+','+str(today['Close'])+'\n')#向下突破和价格 89 | """ 收盘价超过N1最高价 买入股票持有""" 90 | if today['Close'] > today['N1_High']: 91 | break_pd = break_pd.append(today) 92 | dispCont_List.append("向上突破:"+stock_df.index[kl_index].strftime('%Y-%m-%d')+','+str(today['Close'])+'\n')#向上突破和价格 93 | 94 | return break_pd, dispCont_List 95 | 96 | 97 | class FactorBuyAverBreak: 98 | def __init__(self,**kwargs): 99 | self.xd = kwargs['xd'] 100 | 101 | def make_buy_order(self): 102 | buy_signal = True 103 | return buy_signal 104 | 105 | def fit_day(self,kl_index, today, stock_df): 106 | day_ind = stock_df.index.get_loc(kl_index) 107 | 108 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 109 | return False 110 | 111 | if today.Close > stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 112 | print('FactorBuyAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 113 | return self.make_buy_order() 114 | return False 115 | 116 | class FactorSellAverBreak: 117 | def __init__(self,**kwargs): 118 | self.xd = kwargs['xd'] 119 | 120 | def fit_sell_order(self): 121 | sell_signal = True 122 | return sell_signal 123 | 124 | def fit_day(self,kl_index, today, stock_df): 125 | day_ind = stock_df.index.get_loc(kl_index) 126 | 127 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 128 | return False 129 | 130 | if today.Close < stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 131 | print('FactorSellAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 132 | #print 'FactorSellAverBreak for data',stock_df.Close[day_ind-self.xd+1:day_ind+1] 133 | return self.fit_sell_order() 134 | return False 135 | 136 | class FactorBuyNdayBreak: 137 | def __init__(self,**kwargs): 138 | self.xd = kwargs['xd'] 139 | 140 | def make_buy_order(self): 141 | buy_signal = True 142 | return buy_signal 143 | 144 | def fit_day(self,kl_index, today, stock_df): 145 | day_ind = stock_df.index.get_loc(kl_index) 146 | 147 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 148 | return False 149 | 150 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].max(): 151 | print('FactorBuyNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].max()) 152 | return self.make_buy_order() 153 | return False 154 | 155 | class FactorSellNdayBreak: 156 | def __init__(self,**kwargs): 157 | self.xd = kwargs['xd'] 158 | 159 | def fit_sell_order(self): 160 | sell_signal = True 161 | return sell_signal 162 | 163 | def fit_day(self,kl_index, today, stock_df): 164 | day_ind = stock_df.index.get_loc(kl_index) 165 | 166 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 167 | return False 168 | 169 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].min(): 170 | print('FactorSellNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].min()) 171 | return self.fit_sell_order() 172 | return False 173 | 174 | 175 | buy_factors = [{'xd': 20,'class': FactorBuyNdayBreak}, 176 | {'xd': 30,'class': FactorBuyAverBreak}] 177 | sell_factors = [{'xd': 5,'class': FactorSellNdayBreak}, 178 | {'xd': 30,'class': FactorSellAverBreak}] 179 | 180 | class QuantPickTimeSys: 181 | def __init__(self, kl_pd): 182 | """ 183 | :param cap: 初始资金 184 | :param kl_pd: 择时时间段交易数据 185 | :param buy_factors: 买入因子序列,序列中的对象为dict,每一个dict针对一个具体因子 186 | :param sell_factors: 卖出因子序列,序列中的对象为dict,每一个dict针对一个具体因子 187 | """ 188 | 189 | # 回测阶段kl 190 | self.kl_pd = kl_pd 191 | 192 | # 初始化买入因子列表 193 | self.init_buy_factors(buy_factors) 194 | # 初始化卖出因子列表 195 | self.init_sell_factors(sell_factors) 196 | 197 | self.cash_hold = 100000#初始资金 198 | self.posit_num = 0#持股数目 199 | self.market_total = 0#持股市值 200 | self.profit_curve = [] 201 | 202 | def init_buy_factors(self, buy_factors): 203 | 204 | """ 205 | 通过buy_factors实例化各个买入因子 206 | :param buy_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 207 | :return: 208 | """ 209 | self.buy_factors = list() 210 | 211 | if buy_factors is None: 212 | return 213 | 214 | for factor_class in buy_factors: 215 | if factor_class is None: 216 | continue #执行下个循环 217 | if 'class' not in factor_class: 218 | raise ValueError('factor class key must name class!!') 219 | #print "before copy",id(factor_class) 220 | factor_class = copy.deepcopy(factor_class) 221 | #print "after copy",id(factor_class) 222 | class_fac = copy.deepcopy(factor_class['class']) 223 | del factor_class['class'] 224 | #print "del",id(factor_class) 225 | 226 | '''实例化买入因子''' 227 | factor = class_fac(**factor_class) 228 | 229 | if not isinstance(factor, FactorBuyAverBreak) and not isinstance(factor, FactorBuyNdayBreak):#判断factor为基于FactorBuyBreak实例 230 | raise TypeError('factor must base FactorBuyBreak!!') 231 | self.buy_factors.append(factor) 232 | 233 | def init_sell_factors(self, sell_factors): 234 | """ 235 | 通过sell_factors实例化各个卖出因子 236 | :param sell_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 237 | :return: 238 | """ 239 | self.sell_factors = list() 240 | 241 | if sell_factors is None: 242 | return 243 | 244 | for factor_class in sell_factors: 245 | if factor_class is None: 246 | continue #执行下个循环 247 | if 'class' not in factor_class: 248 | raise ValueError('factor class key must name class!!') 249 | factor_class = copy.deepcopy(factor_class) 250 | class_fac = copy.deepcopy(factor_class['class']) 251 | del factor_class['class'] 252 | 253 | '''实例化卖出因子''' 254 | factor = class_fac(**factor_class) 255 | 256 | if not isinstance(factor, FactorSellAverBreak) and not isinstance(factor, FactorSellNdayBreak):#判断factor为基于FactorBuyBreak实例 257 | raise TypeError('factor must base FactorSellBreak!!') 258 | self.sell_factors.append(factor) 259 | 260 | 261 | def _day_task(self, kl_index, today): 262 | 263 | fact_buy,fact_sell,sell_buf,buy_buf = 0,0,0,0 264 | 265 | for index, buy_factor in enumerate(self.buy_factors): 266 | #遍历所有买入因子 267 | buy_buf += buy_factor.fit_day(kl_index, today, self.kl_pd) 268 | 269 | fact_buy = 1 if (buy_buf == (index+1)) else 0 270 | 271 | for index, sell_factor in enumerate(self.sell_factors): 272 | #遍历所有卖出因子 273 | sell_buf += sell_factor.fit_day(kl_index, today, self.kl_pd) 274 | 275 | fact_sell = -1 if (sell_buf > 0) else 0 276 | 277 | return fact_buy or fact_sell 278 | 279 | def run_factor_plot(self,subplotP0,subplotP1, subplotP2): 280 | 281 | dispCont_List = [] 282 | list_signal = [] 283 | is_win = False 284 | self.kl_pd['Ma30'] = self.kl_pd.Close.rolling(window=30).mean()#pd.rolling_mean(self.kl_pd.Close,window=30)#增加M30移动平均线 285 | 286 | self.kl_pd.Close.plot(ax=subplotP0) 287 | self.kl_pd.Ma30.plot(c='black',ax=subplotP0) 288 | subplotP0.set_ylim(np.min(self.kl_pd.Close)-5,np.max(self.kl_pd.Close)+5)#设置Y轴范围 289 | subplotP0.set_xticks([]) #去掉横坐标值 290 | subplotP0.legend(['Close','30ave'],loc='best') 291 | 292 | for kl_index,today in self.kl_pd.iterrows(): 293 | 294 | signal = self._day_task(kl_index, today) 295 | 296 | if signal > 0:# 买入 297 | if is_win == False:#空仓则买 298 | start = self.kl_pd.index.get_loc(kl_index) 299 | is_win = True 300 | 301 | self.posit_num = int(self.cash_hold/today.Close) 302 | self.cash_hold = 0 303 | dispCont_List.append("Start order:\n"+self.kl_pd.index[start].strftime('%Y-%m-%d')+'\n'+str(today.Close)+'\n') 304 | 305 | subplotP0.annotate('B',xy=(kl_index,self.kl_pd.Close.asof(kl_index)),xytext=(kl_index, self.kl_pd.Close.asof(kl_index)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 306 | 307 | elif signal < 0:# 卖出 308 | if is_win == True:#避免未买先卖 309 | end = self.kl_pd.index.get_loc(kl_index) 310 | is_win = False 311 | dispCont_List.append("End order:\n"+self.kl_pd.index[end].strftime('%Y-%m-%d')+'\n'+str(today.Close)+'\n') 312 | self.cash_hold = int(self.posit_num*today.Close) 313 | self.market_total = 0 314 | 315 | if self.kl_pd.Close[end] < self.kl_pd.Close[start]:#赔钱显示绿色 316 | subplotP0.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='green',alpha=0.38) 317 | 318 | else:#赚钱显示绿色 319 | subplotP0.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='red',alpha=0.38) 320 | list_signal.append(is_win) 321 | 322 | if is_win == True: 323 | self.market_total = int(self.posit_num*today.Close) 324 | self.profit_curve.append(self.market_total) 325 | else: 326 | self.profit_curve.append(self.cash_hold) 327 | 328 | self.kl_pd['keep'] = list_signal 329 | self.kl_pd['keep'].fillna(method = 'ffill',inplace = True) 330 | 331 | """ 计算基准收益 """ 332 | self.kl_pd['benchmark_profit'] = np.log(self.kl_pd.Close/self.kl_pd.Close.shift(1)) 333 | """ 计算趋势突破策略收益 """ 334 | self.kl_pd['trend_profit'] = self.kl_pd.keep*self.kl_pd.benchmark_profit 335 | """ 可视化收益情况对比 """ 336 | self.kl_pd[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = subplotP2) 337 | subplotP2.legend(['benchmark_profit','trend_profit'],loc='best') 338 | subplotP2.set_xticks([]) #去掉横坐标值 339 | 340 | self.kl_pd['profit'] = self.profit_curve 341 | self.kl_pd.profit.plot(ax = subplotP1) 342 | subplotP1.legend(['profit'],loc='best') 343 | subplotP1.set_xticks([]) #去掉横坐标值 344 | 345 | return dispCont_List 346 | 347 | class FactorPickStockAng: 348 | def __init__(self,**kwargs): 349 | self.threshold_ang_min = -np.inf 350 | if 'threshold_ang_min' in kwargs: 351 | #设置最小角度阈值 352 | self.threshold_ang_min = kwargs['threshold_ang_min'] 353 | 354 | self.threshold_ang_max = np.inf 355 | if 'threshold_ang_max' in kwargs: 356 | #设置最大角度阈值 357 | self.threshold_ang_max = kwargs['threshold_ang_max'] 358 | 359 | def calc_regress_deg(self, y_arr): 360 | x= np.arange(0, len(y_arr)) 361 | x = sm.add_constant(x)#添加常数列1 362 | 363 | model = regression.linear_model.OLS(y_arr, x).fit()#使用OLS做拟合 364 | rad = model.params[1]#y = kx + b :params[1] = k 365 | deg = np.rad2deg(rad)#弧度转换为角度 366 | 367 | intercept = model.params[0]##y = kx + b :params[0] = b 368 | reg_y_fit = x * rad + intercept 369 | 370 | return deg, x, reg_y_fit, y_arr 371 | 372 | def fit_pick(self, Close): 373 | 374 | ang, x, reg_y_fit, y_arr = self.calc_regress_deg(Close)#计算走势角度 375 | return 'deg = '+str(ang) 376 | 377 | 378 | -------------------------------------------------------------------------------- /Chapter23-Python3.6+/RedefPanelMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import wx 5 | import numpy as np 6 | import pandas as pd 7 | import pandas_datareader.data as web 8 | import matplotlib 9 | import matplotlib.pyplot as plt 10 | from matplotlib.figure import Figure 11 | import matplotlib.dates as mdates 12 | import mpl_finance as mpf #替换 import matplotlib.finance as mpf 13 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 14 | import matplotlib.gridspec as gridspec#分割子图 15 | import datetime 16 | 17 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 18 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 19 | 20 | class MPL_Panel_Base(wx.Panel): 21 | def __init__(self, parent): 22 | wx.Panel.__init__(self,parent=parent, id=-1) 23 | 24 | self.figure = Figure() 25 | gs = gridspec.GridSpec(4, 1, left=0.05, bottom=0.10, right=0.96, top=0.96, wspace=None, hspace=0.1, height_ratios=[3.5,1,1,1]) 26 | self.am = self.figure.add_subplot(gs[0,:]) 27 | self.vol = self.figure.add_subplot(gs[1,:]) 28 | self.macd = self.figure.add_subplot(gs[2,:]) 29 | self.devol = self.figure.add_subplot(gs[3,:]) 30 | 31 | self.FigureCanvas = FigureCanvas(self, -1, self.figure)#figure加到FigureCanvas 32 | self.TopBoxSizer = wx.BoxSizer(wx.VERTICAL) 33 | self.TopBoxSizer.Add(self.FigureCanvas,proportion = -1, border = 2,flag = wx.ALL | wx.EXPAND) 34 | self.SetSizer(self.TopBoxSizer) 35 | 36 | def draw_subgraph(self,stockdat,numt): 37 | 38 | """ 绘制K线图 """ 39 | ohlc = list(zip(np.arange(0,len(stockdat.index)),stockdat.Open,stockdat.Close,stockdat.High,stockdat.Low))#使用zip方法生成数据列表 40 | #mpf.candlestick(self.am, ohlc, width=0.5, colorup='r', colordown='g')#绘制K线走势 41 | mpf.candlestick_ochl(self.am, ohlc, width=0.5, colorup='r', colordown='g')#绘制K线走势 py3.7 42 | ''' 绘制均线 ''' 43 | self.am.plot(numt, stockdat['Ma20'],'black', label='M20',lw=1.0) 44 | self.am.plot(numt, stockdat['Ma60'],'green',label='M60', lw=1.0) 45 | self.am.plot(numt, stockdat['Ma120'],'blue',label='M120', lw=1.0) 46 | self.am.legend(loc='best',shadow=True, fontsize ='10') 47 | ''' 绘制成交量 ''' 48 | self.vol.bar(numt, stockdat.Volume,color=['g' if stockdat.Open[x] > stockdat.Close[x] else 'r' for x in range(0,len(stockdat.index))]) 49 | ''' 绘制MACD ''' 50 | #绘制BAR>0 柱状图 51 | bar_red = np.where(stockdat['macd_bar']>0, 2*stockdat['macd_bar'], 0) 52 | #绘制BAR<0 柱状图 53 | bar_green = np.where(stockdat['macd_bar']<0, 2*stockdat['macd_bar'], 0) 54 | 55 | self.macd.plot(numt, stockdat['macd_dif'], 'red', label='macd dif') #dif 56 | self.macd.plot(numt, stockdat['macd_dea'], 'blue', label='macd dea') #dea 57 | self.macd.bar(numt, bar_red, facecolor='red',label='hist bar') 58 | self.macd.bar(numt, bar_green, facecolor='green',label='hist bar') 59 | self.macd.legend(loc='best',shadow=True, fontsize ='10') 60 | #legend = self.macd.legend(loc='best',shadow=True, fontsize ='10') 61 | #legend.get_frame().set_facecolor('#00FFCC')# Put a nicer background color on the legend. 62 | #legend.get_title().set_fontsize(fontsize = 20) 63 | ''' 绘制KDJ ''' 64 | self.devol.plot(numt, stockdat['K'], 'blue', label='K') #K 65 | self.devol.plot(numt, stockdat['D'], 'g--', label='D') #D 66 | self.devol.plot(numt, stockdat['J'], 'r-', label='J') #J 67 | self.devol.legend(loc='best',shadow=True, fontsize ='10') 68 | 69 | def draw_jumpgap(self,stockdat,jump_pd): 70 | ''' 绘制跳空缺口 ''' 71 | for kl_index in np.arange(0, jump_pd.shape[0]): 72 | today = jump_pd.ix[kl_index]#若版本提示已经弃用 可使用loc或iloc替换 73 | inday = stockdat.index.get_loc(jump_pd.index[kl_index]) 74 | if today['jump_power'] > 0: 75 | self.am.annotate('up',xy=(inday,today.Low*0.95),xytext=(inday, today.Low*0.9),arrowprops=dict(facecolor='red',shrink=0.1),horizontalalignment='left',verticalalignment='top') 76 | elif today['jump_power'] < 0: 77 | self.am.annotate('down',xy=(inday,today.High*1.05),xytext=(inday, today.High*1.1),arrowprops=dict(facecolor='green',shrink=0.1),horizontalalignment='left',verticalalignment='top') 78 | 79 | def draw_avercross(self,stockdat,list_signal): 80 | ''' 绘制金叉死叉 ''' 81 | for kl_index,signal_dat in enumerate(list_signal): 82 | inday = stockdat.index.get_loc(list_signal.index[kl_index]) 83 | if signal_dat < 0: 84 | self.am.annotate(u"死叉", xy=(inday, stockdat['Ma20'][inday]), xytext=(inday, stockdat['Ma20'][inday]+1.5),arrowprops=dict(facecolor='green', shrink=0.2)) 85 | elif signal_dat > 0: 86 | self.am.annotate(u"金叉", xy=(inday, stockdat['Ma60'][inday]), xytext=(inday, stockdat['Ma60'][inday]-1.5),arrowprops=dict(facecolor='red', shrink=0.2)) 87 | 88 | def draw_ndaysbreak(self,stockdat,list_signal): 89 | ''' 绘制N日突破 ''' 90 | for kl_index in np.arange(0, stockdat.shape[0]): 91 | today = stockdat.ix[kl_index] 92 | """ 收盘价超过N2最低价 卖出股票持有""" 93 | if today['Close'] < today['N2_Low']: 94 | self.am.annotate(u"下突破", xy=(kl_index, stockdat['High'][kl_index]), xytext=(kl_index, stockdat['High'][kl_index]+1.5),arrowprops=dict(facecolor='green', shrink=0.2)) 95 | 96 | """ 收盘价超过N1最高价 买入股票持有""" 97 | if today['Close'] > today['N1_High']: 98 | self.am.annotate(u"上突破", xy=(kl_index, stockdat['Low'][kl_index]), xytext=(kl_index, stockdat['Low'][kl_index]-1.5),arrowprops=dict(facecolor='red', shrink=0.2)) 99 | 100 | def update_subgraph(self): 101 | #修改图形的任何属性后都必须使用self.updatePlot()更新GUI界面 102 | self.FigureCanvas.draw() 103 | 104 | def clear_subgraph(self): 105 | #再次画图前,必须调用该命令清空原来的图形 106 | self.am.clear() 107 | self.vol.clear() 108 | self.devol.clear() 109 | self.macd.clear() 110 | #self.figure.set_canvas(self.FigureCanvas) 111 | #self.updatePlot() 112 | 113 | def xylabel_tick_lim(self,title,dates): 114 | # 设置X轴 Y轴的标签 115 | # 给图像添加一个标题 116 | # 设置X轴的刻度大小 117 | # 设置x轴的显示范围 118 | 119 | self.devol.set_xlabel(u"时间") 120 | self.am.set_ylabel(u"日K线") 121 | self.vol.set_ylabel(u"成交量") 122 | self.devol.set_ylabel(u"KDJ") 123 | self.macd.set_ylabel(u"MACD") 124 | self.am.set_title(title) 125 | dir(self.figure) 126 | 127 | major_tick = len(dates) 128 | self.am.set_xlim(0,major_tick) #设置一下x轴的范围 129 | self.vol.set_xlim(0,major_tick) #设置一下x轴的范围 130 | self.devol.set_xlim(0,major_tick) #设置一下x轴的范围 131 | self.macd.set_xlim(0,major_tick) #设置一下x轴的范围 132 | 133 | self.am.set_xticks(range(0,major_tick,15))#每五天标一个日期 134 | self.vol.set_xticks(range(0,major_tick,15))#每五天标一个日期 135 | self.devol.set_xticks(range(0,major_tick,15))#每五天标一个日期 136 | self.macd.set_xticks(range(0,major_tick,15))#每五天标一个日期 137 | self.devol.set_xticklabels([dates.strftime('%Y-%m-%d')[index] for index in self.devol.get_xticks()])#标签设置为日期 138 | 139 | for line in self.am.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 140 | line.set_visible(False) 141 | for line in self.vol.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 142 | line.set_visible(False) 143 | for line in self.macd.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 144 | line.set_visible(False) 145 | for label in self.devol.xaxis.get_ticklabels(): 146 | label.set_rotation(45)#X-轴每个ticker标签都向右倾斜45度 147 | label.set_fontsize(10)#设置标签字体 148 | 149 | self.am.grid(True,color='k') 150 | self.vol.grid(True,color='k') 151 | self.macd.grid(True,color='k') 152 | self.devol.grid(True,color='k') 153 | 154 | class Loop_Panel_Base(wx.Panel): 155 | def __init__(self, parent): 156 | wx.Panel.__init__(self, parent=parent, id=-1) 157 | self.figure = Figure() 158 | 159 | gs = gridspec.GridSpec(3, 1, left=0.05, bottom=0.10, right=0.96, top=0.96, wspace=None, hspace=0.1, height_ratios=[1.5,1,1]) 160 | 161 | self.trade = self.figure.add_subplot(gs[0,:]) 162 | self.total = self.figure.add_subplot(gs[1,:]) 163 | self.profit = self.figure.add_subplot(gs[2,:]) 164 | 165 | self.FigureCanvas = FigureCanvas(self, -1, self.figure)#figure加到FigureCanvas 166 | self.TopBoxSizer = wx.BoxSizer(wx.VERTICAL) 167 | self.TopBoxSizer.Add(self.FigureCanvas,proportion = -1, border = 2,flag = wx.ALL | wx.EXPAND) 168 | self.SetSizer(self.TopBoxSizer) 169 | 170 | def update_subgraph(self): 171 | #修改图形的任何属性后都必须使用self.updatePlot()更新GUI界面 172 | self.FigureCanvas.draw() 173 | 174 | def clear_subgraph(self): 175 | #再次画图前,必须调用该命令清空原来的图形 176 | self.trade.clear() 177 | self.total.clear() 178 | self.profit.clear() 179 | 180 | def xylabel_tick_lim(self,title): 181 | # 设置X轴 Y轴的标签 182 | # 给图像添加一个标题 183 | # 设置X轴的刻度大小 184 | # 设置x轴的显示范围 185 | 186 | self.profit.set_xlabel(u"时间") 187 | self.trade.set_ylabel(u"交易区间") 188 | self.total.set_ylabel(u"资金收益") 189 | self.profit.set_ylabel(u"收益率对比") 190 | self.trade.set_title(title) 191 | 192 | #self.trade.xticks([]) #去掉横坐标值 193 | #self.total.xticks([]) #去掉横坐标值 194 | self.profit.xaxis.set_minor_locator(mdates.WeekdayLocator(byweekday=(1),interval=1)) 195 | self.profit.xaxis.set_minor_formatter(mdates.DateFormatter('%d\n%a'))#标签设置为日期 196 | 197 | -------------------------------------------------------------------------------- /Chapter23-Python3.6+/StockDataMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import pandas as pd 6 | import pandas_datareader.data as web 7 | import datetime 8 | import csv,os 9 | import codecs 10 | import talib 11 | 12 | #获取股票数据接口 13 | def GetStockDatApi(stockName=None,stockTimeS=None,stockTimeE=None): 14 | 15 | #path = 'C:\programPY\GUI_Inter_StoMPL\StockData' 16 | path = '.' 17 | str_stockTimeS = stockTimeS.strftime('%Y-%m-%d') 18 | str_stockTimeE = stockTimeE.strftime('%Y-%m-%d') 19 | newname = stockName+'+'+str_stockTimeS+'+'+str_stockTimeE+'.csv' 20 | newpath = os.path.join(path,newname) 21 | 22 | #path=os.path.abspath('.')#获取当前脚本所在的路径 C:\Program Files\Notepad++ 23 | print(u"当前:%s" % os.getcwd())#当前工作目录 24 | os.chdir(path) 25 | print(u"修改为:%s" % os.getcwd())#修改后工作目录 26 | 27 | for filename in os.listdir(path):#遍历路径下所有文件 28 | #print(os.path.join(path,filename)) 29 | 30 | if stockName in filename: 31 | if filename.count('+') == 2:#存在CSV文件 32 | str_dfLoadTimeS = filename.split('+')[1] 33 | str_dfLoadTimeE = filename.split('+')[2].split('.')[0] 34 | 35 | dtm_dfLoadTimeS = datetime.datetime.strptime(str_dfLoadTimeS,'%Y-%m-%d') 36 | dtm_dfLoadTimeE = datetime.datetime.strptime(str_dfLoadTimeE,'%Y-%m-%d') 37 | 38 | if((dtm_dfLoadTimeS - stockTimeS).days <= 0)and((dtm_dfLoadTimeE - stockTimeE).days >= 0):#起止日期在文件内则读取CSV文件获取数据 39 | print("123",(dtm_dfLoadTimeS - stockTimeS).days) 40 | print("345",(dtm_dfLoadTimeE - stockTimeE).days) 41 | stockDat = pd.read_csv(os.path.join(path,filename),parse_dates=True,index_col=0,encoding='gb2312') 42 | print(stockDat.head(),stockDat.tail()) 43 | stockDat = stockDat.loc[stockTimeS:stockTimeE] 44 | print(stockDat.head(),stockDat.tail()) 45 | else:#起止日期不相同则重新下载 46 | stockDat = web.DataReader(stockName, "yahoo", stockTimeS, stockTimeE) 47 | os.rename(filename, newname) 48 | stockDat.to_csv(newpath,columns=stockDat.columns,index=True) 49 | return stockDat 50 | else: 51 | break 52 | 53 | stockDat = web.DataReader(stockName, "yahoo", stockTimeS, stockTimeE) 54 | stockDat.to_csv(newpath,columns=stockDat.columns,index=True) 55 | 56 | return stockDat 57 | 58 | #处理股票数据接口 59 | def GetStockDatPro(stockName=None,stockTimeS=None,stockTimeE=None): 60 | stockPro = GetStockDatApi(stockName, stockTimeS, stockTimeE) 61 | 62 | #处理移动平均线 63 | stockPro['Ma20'] = stockPro.Close.rolling(window=20).mean() #pd.rolling_mean(stockPro.Close,window=20) 64 | stockPro['Ma60'] = stockPro.Close.rolling(window=60).mean() #pd.rolling_mean(stockPro.Close,window=60) 65 | stockPro['Ma120'] = stockPro.Close.rolling(window=120).mean() #pd.rolling_mean(stockPro.Close,window=120) 66 | 67 | #处理MACD 68 | stockPro['macd_dif'],stockPro['macd_dea'], stockPro['macd_bar'] = talib.MACD(stockPro['Close'].values, fastperiod=12, slowperiod=26, signalperiod=9) 69 | 70 | #处理KDJ 71 | xd = 9-1 72 | date = stockPro.index.to_series() 73 | RSV = pd.Series(np.zeros(len(date)-xd),index=date.index[xd:]) 74 | Kvalue = pd.Series(0.0,index=RSV.index) 75 | Dvalue = pd.Series(0.0,index=RSV.index) 76 | Kvalue[0],Dvalue[0] = 50,50 77 | 78 | for day_ind in range(xd, len(date)): 79 | RSV[date[day_ind]] = (stockPro.Close[day_ind] - stockPro.Low[day_ind-xd:day_ind+1].min())/(stockPro.High[day_ind-xd:day_ind+1].max()-stockPro.Low[day_ind-xd:day_ind+1].min())*100 80 | if day_ind > xd: 81 | index = day_ind-xd 82 | Kvalue[index] = 2.0/3*Kvalue[index-1]+RSV[date[day_ind]]/3 83 | Dvalue[index] = 2.0/3*Dvalue[index-1]+Kvalue[index]/3 84 | stockPro['RSV'] = RSV 85 | stockPro['K'] = Kvalue 86 | stockPro['D'] = Dvalue 87 | stockPro['J'] = 3*Kvalue-2*Dvalue 88 | return stockPro 89 | 90 | 91 | -------------------------------------------------------------------------------- /Chapter23/GUI_QuantTradeSys.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import wx 5 | import numpy as np 6 | import pandas as pd 7 | import pandas_datareader.data as web 8 | import matplotlib 9 | import matplotlib.pyplot as plt 10 | from matplotlib.figure import Figure 11 | import matplotlib.dates as mdates 12 | import matplotlib.finance as mpf 13 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 14 | import matplotlib.gridspec as gridspec#分割子图 15 | import datetime 16 | import talib 17 | import csv,os 18 | import codecs 19 | 20 | from RedefPanelMod import MPL_Panel_Base,Loop_Panel_Base 21 | from StockDataMod import GetStockDatPro 22 | from IndicatStrateMod import Excave_Indic_Base, QuantPickTimeSys,FactorPickStockAng 23 | 24 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 25 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 26 | 27 | class UserDialog(wx.Dialog):# user-defined 28 | 29 | def __init__(self,parent,text): 30 | wx.Dialog.__init__(self,parent,-1,u"选股提示",size=(400,500),style=wx.CAPTION|wx.CLOSE_BOX|wx.MAXIMIZE_BOX|wx.MINIMIZE_BOX) 31 | 32 | sizer = wx.BoxSizer(wx.VERTICAL) 33 | 34 | pstock_Text = wx.StaticText(self, -1, u'选股策略筛选结果') 35 | pstock_Text.SetFont(wx.Font(18,wx.DEFAULT,wx.NORMAL,wx.BOLD)) 36 | pstock_sure = wx.TextCtrl(self, -1, "角度值:\n",size=(350,300),style = wx.TE_MULTILINE|wx.TE_READONLY)#多行|只读 37 | pstock_sure.SetFont(wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD)) 38 | 39 | okbtn = wx.Button(self,wx.ID_OK,u"确认") 40 | okbtn.SetDefault() 41 | 42 | sizer.Add(pstock_Text,flag=wx.ALIGN_CENTER) 43 | sizer.Add(pstock_sure,flag=wx.ALIGN_CENTER) 44 | sizer.Add(okbtn,flag=wx.ALIGN_CENTER) 45 | self.SetSizer(sizer) 46 | for i in text:pstock_sure.AppendText(i) 47 | 48 | 49 | class Frame(wx.Frame): 50 | def __init__(self): 51 | wx.Frame.__init__(self, parent = None, title = u'量化软件', size=(1500,800), 52 | style=wx.DEFAULT_FRAME_STYLE^wx.MAXIMIZE_BOX) 53 | #创建显示区面板 54 | self.DispPanel = MPL_Panel_Base(self) 55 | self.BackPanel = Loop_Panel_Base(self) 56 | self.am = self.DispPanel.am 57 | self.vol = self.DispPanel.vol 58 | self.devol = self.DispPanel.devol 59 | self.macd = self.DispPanel.macd 60 | 61 | #创建参数区面板 62 | self.ParaPanel = wx.Panel(self,-1) 63 | 64 | paraInput_Box = wx.StaticBox(self.ParaPanel, -1, u'参数输入') 65 | paraInput_Sizer = wx.StaticBoxSizer(paraInput_Box, wx.VERTICAL) 66 | self.StNameCodedict = {u"开山股份":"300257.SZ",u"浙大网新":"600797.SS",u"水晶光电":"002273.SZ", u"高鸿股份":"000851.SZ"} 67 | 68 | #初始化股票代码变量 69 | self.stockName_Val = u"开山股份" 70 | self.stockCode_Val = self.StNameCodedict[self.stockName_Val] 71 | 72 | self.stockName_CMBO = wx.ComboBox(self.ParaPanel, -1,self.stockName_Val, choices = list(self.StNameCodedict.keys()), style = wx.CB_READONLY|wx.CB_DROPDOWN) #股票名称 73 | stockCode_Text = wx.StaticText(self.ParaPanel, -1, u'股票名称') 74 | 75 | #策略选取 76 | strate_Text = wx.StaticText(self.ParaPanel, -1, u'策略名称') 77 | strate_Combo_Val = [u"双趋势融合", u"阿尔法", u"布林带"] 78 | self.pickstrate_Val = u"双趋势融合" 79 | self.pickstrate_CMBO = wx.ComboBox(self.ParaPanel, -1, self.pickstrate_Val, choices = strate_Combo_Val, style = wx.CB_READONLY|wx.CB_DROPDOWN) #策略名称 80 | 81 | #日历控件选择数据周期 82 | self.dpcEndTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_SHOWCENTURY|wx.DP_ALLOWNONE)#结束时间 83 | self.dpcStartTime = wx.DatePickerCtrl(self.ParaPanel, -1,style = wx.DP_DROPDOWN|wx.DP_SHOWCENTURY|wx.DP_ALLOWNONE)#起始时间 84 | DateTimeNow = wx.DateTime.Now()#wx.DateTime格式"03/03/18 00:00:00" 85 | self.dpcEndTime.SetValue(DateTimeNow) 86 | DateTimeNow.SetYear(DateTimeNow.Year-1) 87 | self.dpcStartTime.SetValue(DateTimeNow) 88 | stockData_Text = wx.StaticText(self.ParaPanel, -1, u'日期(Start-End)') 89 | 90 | #初始化时间变量 91 | dateVal = self.dpcStartTime.GetValue() 92 | self.stockSdate_Val = datetime.datetime(dateVal.Year,dateVal.Month+1,dateVal.Day) 93 | dateVal = self.dpcEndTime.GetValue() 94 | self.stockEdate_Val = datetime.datetime(dateVal.Year,dateVal.Month+1,dateVal.Day) 95 | 96 | paraInput_Sizer.Add(stockCode_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 97 | paraInput_Sizer.Add(self.stockName_CMBO, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 98 | paraInput_Sizer.Add(stockData_Text,proportion=0,flag=wx.EXPAND|wx.ALL,border=2) 99 | paraInput_Sizer.Add(self.dpcStartTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 100 | paraInput_Sizer.Add(self.dpcEndTime, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 101 | paraInput_Sizer.Add(strate_Text, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 102 | paraInput_Sizer.Add(self.pickstrate_CMBO, 0, wx.EXPAND|wx.ALL|wx.CENTER, 2) 103 | 104 | RadioList = ["不显示","跳空缺口", "金叉/死叉", "N日突破"] 105 | self.StratInputBox = wx.RadioBox(self.ParaPanel, -1, label=u'指标提示', choices=RadioList,majorDimension = 4, style = wx.RA_SPECIFY_ROWS) 106 | self.StratInputBox.Bind(wx.EVT_RADIOBOX,self.OnRadioBox_Indicator) 107 | #初始化指标变量 108 | self.IndicatInput_Val = self.StratInputBox.GetStringSelection() 109 | 110 | self.TextAInput = wx.TextCtrl(self.ParaPanel, -1, "交易信息提示:", style = wx.TE_MULTILINE|wx.TE_READONLY)#多行|只读 111 | 112 | vboxnetA = wx.BoxSizer(wx.VERTICAL)#纵向box 113 | vboxnetA.Add(paraInput_Sizer,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) #proportion参数控制容器尺寸比例 114 | vboxnetA.Add(self.StratInputBox,proportion=0,flag=wx.EXPAND|wx.BOTTOM,border=2) 115 | vboxnetA.Add(self.TextAInput,proportion=1,flag=wx.EXPAND|wx.ALL,border=2) 116 | self.ParaPanel.SetSizer(vboxnetA) 117 | 118 | #创建Right面板 119 | self.CtrlPanel = wx.Panel(self,-1) 120 | #创建FlexGridSizer布局网格 121 | self.FlexGridSizer=wx.FlexGridSizer(rows=3, cols=1, vgap=3, hgap=3) 122 | 123 | #行情按钮 124 | self.Firmoffer = wx.Button(self.CtrlPanel,-1,"行情") 125 | self.Firmoffer.Bind(wx.EVT_BUTTON,self.FirmEvent)#绑定行情按钮事件 126 | #选股按钮 127 | self.Stockpick = wx.Button(self.CtrlPanel,-1,"选股") 128 | self.Stockpick.Bind(wx.EVT_BUTTON,self.PstockpEvent)#绑定选股按钮事件 129 | #回测按钮 130 | self.Backtrace = wx.Button(self.CtrlPanel,-1,"回测") 131 | self.Backtrace.Bind(wx.EVT_BUTTON,self.BackEvent)#绑定回测按钮事件 132 | 133 | #加入Sizer中 134 | self.FlexGridSizer.Add(self.Firmoffer,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 135 | self.FlexGridSizer.Add(self.Stockpick,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 136 | self.FlexGridSizer.Add(self.Backtrace,proportion = 1, border = 5,flag = wx.ALL | wx.EXPAND) 137 | self.FlexGridSizer.SetFlexibleDirection(wx.BOTH) 138 | 139 | self.CtrlPanel.SetSizer(self.FlexGridSizer) 140 | 141 | self.HBoxPanel = wx.BoxSizer(wx.HORIZONTAL) 142 | self.HBoxPanel.Add(self.ParaPanel,proportion = 1.5, border = 2,flag = wx.EXPAND|wx.ALL) 143 | self.HBoxPanel.Add(self.DispPanel,proportion = 8, border = 2,flag = wx.EXPAND|wx.ALL ) 144 | self.HBoxPanel.Add(self.CtrlPanel,proportion = 1, border = 2,flag = wx.EXPAND|wx.ALL ) 145 | self.SetSizer(self.HBoxPanel) 146 | 147 | def ProcessStock(self): 148 | #df_stockload = web.DataReader("600797.SS", "yahoo", datetime.datetime(2017,1,1), datetime.date.today()) 149 | df_stockload = GetStockDatPro(self.stockCode_Val,self.stockSdate_Val, self.stockEdate_Val) 150 | 151 | """ 绘制移动平均线图 """ 152 | #self.am.plot(self.numic[0:self.butNum],self.close[0:self.butNum],'#0f0ff0',linewidth=1.0) 153 | 154 | dispCont_List = [] 155 | 156 | examp_trade= Excave_Indic_Base() 157 | if self.IndicatInput_Val == u"金叉/死叉": 158 | dispCont_pd,dispCont_List = examp_trade.plot_Aver_Cross(df_stockload) 159 | self.DispPanel.draw_avercross(df_stockload,dispCont_pd) 160 | elif self.IndicatInput_Val == u"跳空缺口": 161 | dispCont_pd,dispCont_List = examp_trade.plot_Jump_Thrd(df_stockload) 162 | self.DispPanel.draw_jumpgap(df_stockload,dispCont_pd) 163 | elif self.IndicatInput_Val == u"N日突破": 164 | dispCont_pd,dispCont_List = examp_trade.plot_Ndays_Break(df_stockload) 165 | self.DispPanel.draw_ndaysbreak(df_stockload,dispCont_pd) 166 | else: 167 | dispCont_List = dispCont_List 168 | 169 | self.TextAInput.SetValue(u"指标提示信息如下:"+'\n') 170 | for i in dispCont_List:self.TextAInput.AppendText(i) 171 | 172 | numic = np.arange(0,len(df_stockload.index)) 173 | butNum = len(df_stockload.index) 174 | self.DispPanel.xylabel_tick_lim(self.stockName_Val,df_stockload.index) 175 | self.DispPanel.draw_subgraph(df_stockload,numic) 176 | 177 | def ProcessLoop(self): 178 | 179 | df_stockload = GetStockDatPro(self.stockCode_Val,self.stockSdate_Val, self.stockEdate_Val) 180 | dispCont_List = [] 181 | if self.pickstrate_Val == u"双趋势融合": 182 | #多趋势融合策略执行 """ 183 | examp_trade= QuantPickTimeSys(df_stockload) 184 | dispCont_List = examp_trade.run_factor_plot(self.BackPanel.trade,self.BackPanel.total,self.BackPanel.profit) 185 | else: 186 | #执行其他策略 187 | pass 188 | 189 | self.TextAInput.SetValue(u"策略提示信息如下:"+'\n') 190 | for i in dispCont_List:self.TextAInput.AppendText(i) 191 | self.BackPanel.xylabel_tick_lim(self.stockName_Val) 192 | 193 | def reFlashLoop(self): 194 | self.BackPanel.clear_subgraph()#必须清理图形才能显示下一幅图 195 | self.ProcessLoop() 196 | self.BackPanel.update_subgraph()#必须刷新才能显示下一幅图 197 | 198 | def reFlashFrame(self): 199 | self.DispPanel.clear_subgraph()#必须清理图形才能显示下一幅图 200 | self.ProcessStock() 201 | self.DispPanel.update_subgraph()#必须刷新才能显示下一幅图 202 | 203 | def FirmEvent(self,event): 204 | #显示行情面板 205 | self.HBoxPanel.Hide(self.BackPanel) 206 | self.HBoxPanel.Replace(self.BackPanel,self.DispPanel) 207 | self.HBoxPanel.Show(self.DispPanel) 208 | self.HBoxPanel.Remove(self.BackPanel) 209 | self.SetSizer(self.HBoxPanel) 210 | self.HBoxPanel.Layout() 211 | 212 | #获取列表股票代码和起始时间 213 | self.stockName_Val = self.stockName_CMBO.GetString(self.stockName_CMBO.GetSelection()) 214 | self.stockCode_Val = self.StNameCodedict[self.stockName_Val] 215 | 216 | dateVal = self.dpcStartTime.GetValue() 217 | self.stockSdate_Val = datetime.datetime(dateVal.Year,dateVal.Month+1,dateVal.Day) 218 | dateVal = self.dpcEndTime.GetValue() 219 | self.stockEdate_Val = datetime.datetime(dateVal.Year,dateVal.Month+1,dateVal.Day) 220 | 221 | self.reFlashFrame() 222 | 223 | def BackEvent(self,event): 224 | #显示回测面板 225 | self.HBoxPanel.Hide(self.DispPanel) 226 | self.HBoxPanel.Replace(self.DispPanel,self.BackPanel) 227 | self.HBoxPanel.Show(self.BackPanel) 228 | self.HBoxPanel.Remove(self.DispPanel) 229 | self.SetSizer(self.HBoxPanel) 230 | self.HBoxPanel.Layout() 231 | 232 | #获取策略名称 233 | self.pickstrate_Val = self.pickstrate_CMBO.GetString(self.pickstrate_CMBO.GetSelection()) 234 | self.reFlashLoop() 235 | 236 | def PstockpEvent(self,event): 237 | dispCont_List = [] 238 | """ 选股策略执行 """ 239 | examp_trade= FactorPickStockAng() 240 | for index, stockName in enumerate(self.StNameCodedict.keys()) : 241 | print "stockName",stockName,self.StNameCodedict[stockName] 242 | df_stockload = GetStockDatPro(self.StNameCodedict[stockName],self.stockSdate_Val, self.stockEdate_Val) 243 | df_stockload.fillna(method='bfill',inplace=True)#后一个数据填充NAN1 244 | dispCont_List.append(stockName+'\n'+examp_trade.fit_pick(df_stockload.Close)+'\n') 245 | print dispCont_List 246 | """ 选股策略执行 """ 247 | """ 自定义提示框 """ 248 | myPickStock = UserDialog(self,dispCont_List) 249 | if myPickStock.ShowModal() == wx.ID_OK: 250 | pass 251 | else: 252 | pass 253 | """ 自定义提示框 """ 254 | 255 | def OnRadioBox_Indicator(self,event): 256 | self.IndicatInput_Val = self.StratInputBox.GetStringSelection() 257 | 258 | 259 | class App(wx.App): 260 | def OnInit(self): 261 | self.frame = Frame() 262 | self.frame.ProcessStock() 263 | self.frame.Show() 264 | self.SetTopWindow(self.frame) 265 | return True 266 | 267 | if __name__ == '__main__': 268 | app = App() 269 | app.MainLoop() 270 | 271 | -------------------------------------------------------------------------------- /Chapter23/IndicatStrateMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import pandas as pd 6 | import pandas_datareader.data as web 7 | import statsmodels.api as sm 8 | from statsmodels import regression 9 | import datetime 10 | import csv,os 11 | import codecs 12 | import talib 13 | import copy 14 | 15 | class Excave_Indic_Base: 16 | def __init__(self): 17 | #挖掘衍生技术指标 18 | pass 19 | 20 | def plot_Aver_Cross(self, stock_df): 21 | #显示均线金叉/死叉提示符 22 | list_diff = np.sign(stock_df['Ma20']-stock_df['Ma60']) 23 | list_signal = np.sign(list_diff-list_diff.shift(1)) 24 | #print "list_diff",list_diff 25 | list_signal = list_signal[list_signal !=0] 26 | list_signal = list_signal.dropna(axis=0,how='any')#去除NA值 27 | #print "list_signal",list_signal 28 | 29 | dispCont_List = ["M20&M60 金叉:\n"+"日期:"+list_signal.index[x].strftime('%Y-%m-%d')+'\n' if list_signal[x] > 0 else "M20&M60 死叉:\n"+"日期:"+list_signal.index[x].strftime('%Y-%m-%d')+'\n' for x in range(0,len(list_signal.index))]#金叉时间 30 | 31 | return list_signal,dispCont_List 32 | 33 | def plot_Jump_Thrd(self, stock_df): 34 | 35 | stock_df['changeRatio'] = stock_df.Close.pct_change()*100#计算涨/跌幅 (今收-昨收)/昨收*100% 判断向上跳空缺口/向下跳空缺口 36 | stock_df['preClose'] = stock_df.Close.shift(1) #增加昨收序列 37 | 38 | jump_threshold = stock_df.Close.median()*0.01 #跳空阈值 收盘价中位数*0.03 39 | #print "jump_threshold",jump_threshold 40 | jump_pd = pd.DataFrame() 41 | 42 | for kl_index in np.arange(0, stock_df.shape[0]): 43 | today = stock_df.ix[kl_index] 44 | """ 检测跳空缺口 """ 45 | if (today.changeRatio > 0) and ((today.Low-today.preClose) > jump_threshold): 46 | #向上跳空 (今最低-昨收)/阈值 47 | today['jump_power'] = (today.Low-today.preClose)/jump_threshold 48 | jump_pd = jump_pd.append(today) 49 | 50 | elif (today.changeRatio < 0) and ((today.preClose-today.High) > jump_threshold): 51 | #向下跳空 (昨收-今最高)/阈值 52 | today['jump_power'] = (today.High-today.preClose)/jump_threshold 53 | jump_pd = jump_pd.append(today) 54 | 55 | jump_pd = jump_pd[(np.abs(jump_pd.changeRatio) > 2)&(jump_pd.Volume > 20000000)]#abs取绝对值 56 | 57 | dispCont_List = ["向上跳空:\n"+"日期:"+jump_pd.index[x].strftime('%Y-%m-%d')+'\n'+"缺口值:"+str('%.2f'%jump_pd.jump_power[x])+'\n' if jump_pd.jump_power[x] > 0 else "向下跳空:\n"+"日期:"+jump_pd.index[x].strftime('%Y-%m-%d')+'\n'+"缺口值:"+str('%.2f'%jump_pd.jump_power[x])+'\n' for x in range(0,len(jump_pd.index))] 58 | 59 | print jump_pd.filter(['jump_power','preClose','changeRatio','Close','Volume'])#按顺序只显示该列 60 | return jump_pd, dispCont_List 61 | 62 | 63 | def plot_Ndays_Break(self, stock_df): 64 | N1 = 42 65 | N2 = 30 66 | stock_df['N1_High'] = pd.rolling_max(stock_df.High,window=N1)#计算最近N1个交易日最高价 67 | stock_df['N1_High'] = stock_df['N1_High'].shift(1) 68 | expan_max = pd.expanding_max(stock_df.Close) 69 | stock_df['N1_High'].fillna(value=expan_max,inplace=True)#目前出现过的最大值填充前N1个nan 70 | 71 | stock_df['N2_Low'] = pd.rolling_min(stock_df.Low,window=N2)#计算最近N2个交易日最低价 72 | stock_df['N2_Low'] = stock_df['N2_Low'].shift(1) 73 | expan_min = pd.expanding_min(stock_df.Close) 74 | stock_df['N2_Low'].fillna(value=expan_min,inplace=True)#目前出现过的最小值填充前N2个nan 75 | 76 | dispCont_List = [] 77 | break_pd = pd.DataFrame() 78 | 79 | for kl_index in np.arange(0, stock_df.shape[0]): 80 | today = stock_df.ix[kl_index] 81 | """ 收盘价超过N2最低价 卖出股票持有""" 82 | if today['Close'] < today['N2_Low']: 83 | break_pd = break_pd.append(today) 84 | dispCont_List.append("向下突破:"+stock_df.index[kl_index].strftime('%Y-%m-%d')+','+str(today['Close'])+'\n')#向下突破和价格 85 | """ 收盘价超过N1最高价 买入股票持有""" 86 | if today['Close'] > today['N1_High']: 87 | break_pd = break_pd.append(today) 88 | dispCont_List.append("向上突破:"+stock_df.index[kl_index].strftime('%Y-%m-%d')+','+str(today['Close'])+'\n')#向上突破和价格 89 | 90 | return break_pd, dispCont_List 91 | 92 | 93 | class FactorBuyAverBreak: 94 | def __init__(self,**kwargs): 95 | self.xd = kwargs['xd'] 96 | 97 | def make_buy_order(self): 98 | buy_signal = True 99 | return buy_signal 100 | 101 | def fit_day(self,kl_index, today, stock_df): 102 | day_ind = stock_df.index.get_loc(kl_index) 103 | 104 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 105 | return False 106 | 107 | if today.Close > stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 108 | print('FactorBuyAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 109 | return self.make_buy_order() 110 | return False 111 | 112 | class FactorSellAverBreak: 113 | def __init__(self,**kwargs): 114 | self.xd = kwargs['xd'] 115 | 116 | def fit_sell_order(self): 117 | sell_signal = True 118 | return sell_signal 119 | 120 | def fit_day(self,kl_index, today, stock_df): 121 | day_ind = stock_df.index.get_loc(kl_index) 122 | 123 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 124 | return False 125 | 126 | if today.Close < stock_df.Close[day_ind-self.xd+1:day_ind+1].mean(): 127 | print('FactorSellAverBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].mean()) 128 | #print 'FactorSellAverBreak for data',stock_df.Close[day_ind-self.xd+1:day_ind+1] 129 | return self.fit_sell_order() 130 | return False 131 | 132 | class FactorBuyNdayBreak: 133 | def __init__(self,**kwargs): 134 | self.xd = kwargs['xd'] 135 | 136 | def make_buy_order(self): 137 | buy_signal = True 138 | return buy_signal 139 | 140 | def fit_day(self,kl_index, today, stock_df): 141 | day_ind = stock_df.index.get_loc(kl_index) 142 | 143 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 144 | return False 145 | 146 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].max(): 147 | print('FactorBuyNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].max()) 148 | return self.make_buy_order() 149 | return False 150 | 151 | class FactorSellNdayBreak: 152 | def __init__(self,**kwargs): 153 | self.xd = kwargs['xd'] 154 | 155 | def fit_sell_order(self): 156 | sell_signal = True 157 | return sell_signal 158 | 159 | def fit_day(self,kl_index, today, stock_df): 160 | day_ind = stock_df.index.get_loc(kl_index) 161 | 162 | if day_ind < self.xd - 1 or day_ind >= stock_df.shape[0] - 1: 163 | return False 164 | 165 | if today.Close == stock_df.Close[day_ind-self.xd+1:day_ind+1].min(): 166 | print('FactorSellNdayBreak for info',kl_index,today.Close,stock_df.Close[day_ind-self.xd+1:day_ind+1].min()) 167 | return self.fit_sell_order() 168 | return False 169 | 170 | 171 | buy_factors = [{'xd': 20,'class': FactorBuyNdayBreak}, 172 | {'xd': 30,'class': FactorBuyAverBreak}] 173 | sell_factors = [{'xd': 5,'class': FactorSellNdayBreak}, 174 | {'xd': 30,'class': FactorSellAverBreak}] 175 | 176 | class QuantPickTimeSys: 177 | def __init__(self, kl_pd): 178 | """ 179 | :param cap: 初始资金 180 | :param kl_pd: 择时时间段交易数据 181 | :param buy_factors: 买入因子序列,序列中的对象为dict,每一个dict针对一个具体因子 182 | :param sell_factors: 卖出因子序列,序列中的对象为dict,每一个dict针对一个具体因子 183 | """ 184 | 185 | # 回测阶段kl 186 | self.kl_pd = kl_pd 187 | 188 | # 初始化买入因子列表 189 | self.init_buy_factors(buy_factors) 190 | # 初始化卖出因子列表 191 | self.init_sell_factors(sell_factors) 192 | 193 | self.cash_hold = 100000#初始资金 194 | self.posit_num = 0#持股数目 195 | self.market_total = 0#持股市值 196 | self.profit_curve = [] 197 | 198 | def init_buy_factors(self, buy_factors): 199 | 200 | """ 201 | 通过buy_factors实例化各个买入因子 202 | :param buy_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 203 | :return: 204 | """ 205 | self.buy_factors = list() 206 | 207 | if buy_factors is None: 208 | return 209 | 210 | for factor_class in buy_factors: 211 | if factor_class is None: 212 | continue #执行下个循环 213 | if 'class' not in factor_class: 214 | raise ValueError('factor class key must name class!!') 215 | #print "before copy",id(factor_class) 216 | factor_class = copy.deepcopy(factor_class) 217 | #print "after copy",id(factor_class) 218 | class_fac = copy.deepcopy(factor_class['class']) 219 | del factor_class['class'] 220 | #print "del",id(factor_class) 221 | 222 | '''实例化买入因子''' 223 | factor = class_fac(**factor_class) 224 | 225 | if not isinstance(factor, FactorBuyAverBreak) and not isinstance(factor, FactorBuyNdayBreak):#判断factor为基于FactorBuyBreak实例 226 | raise TypeError('factor must base FactorBuyBreak!!') 227 | self.buy_factors.append(factor) 228 | 229 | def init_sell_factors(self, sell_factors): 230 | """ 231 | 通过sell_factors实例化各个卖出因子 232 | :param sell_factors: list中元素为dict,每个dict为因子的构造元素,如class,构造参数等 233 | :return: 234 | """ 235 | self.sell_factors = list() 236 | 237 | if sell_factors is None: 238 | return 239 | 240 | for factor_class in sell_factors: 241 | if factor_class is None: 242 | continue #执行下个循环 243 | if 'class' not in factor_class: 244 | raise ValueError('factor class key must name class!!') 245 | factor_class = copy.deepcopy(factor_class) 246 | class_fac = copy.deepcopy(factor_class['class']) 247 | del factor_class['class'] 248 | 249 | '''实例化卖出因子''' 250 | factor = class_fac(**factor_class) 251 | 252 | if not isinstance(factor, FactorSellAverBreak) and not isinstance(factor, FactorSellNdayBreak):#判断factor为基于FactorBuyBreak实例 253 | raise TypeError('factor must base FactorSellBreak!!') 254 | self.sell_factors.append(factor) 255 | 256 | 257 | def _day_task(self, kl_index, today): 258 | 259 | fact_buy,fact_sell,sell_buf,buy_buf = 0,0,0,0 260 | 261 | for index, buy_factor in enumerate(self.buy_factors): 262 | #遍历所有买入因子 263 | buy_buf += buy_factor.fit_day(kl_index, today, self.kl_pd) 264 | 265 | fact_buy = 1 if (buy_buf == (index+1)) else 0 266 | 267 | for index, sell_factor in enumerate(self.sell_factors): 268 | #遍历所有卖出因子 269 | sell_buf += sell_factor.fit_day(kl_index, today, self.kl_pd) 270 | 271 | fact_sell = -1 if (sell_buf > 0) else 0 272 | 273 | return fact_buy or fact_sell 274 | 275 | def run_factor_plot(self,subplotP0,subplotP1, subplotP2): 276 | 277 | dispCont_List = [] 278 | list_signal = [] 279 | is_win = False 280 | self.kl_pd['Ma30'] = self.kl_pd.Close.rolling(window=30).mean()#pd.rolling_mean(self.kl_pd.Close,window=30)#增加M30移动平均线 281 | 282 | self.kl_pd.Close.plot(ax=subplotP0) 283 | self.kl_pd.Ma30.plot(c='black',ax=subplotP0) 284 | subplotP0.set_ylim(np.min(self.kl_pd.Close)-5,np.max(self.kl_pd.Close)+5)#设置Y轴范围 285 | subplotP0.set_xticks([]) #去掉横坐标值 286 | subplotP0.legend(['Close','30ave'],loc='best') 287 | 288 | for kl_index,today in self.kl_pd.iterrows(): 289 | 290 | signal = self._day_task(kl_index, today) 291 | 292 | if signal > 0:# 买入 293 | if is_win == False:#空仓则买 294 | start = self.kl_pd.index.get_loc(kl_index) 295 | is_win = True 296 | 297 | self.posit_num = int(self.cash_hold/today.Close) 298 | self.cash_hold = 0 299 | dispCont_List.append("Start order:\n"+self.kl_pd.index[start].strftime('%Y-%m-%d')+'\n'+str(today.Close)+'\n') 300 | 301 | subplotP0.annotate('B',xy=(kl_index,self.kl_pd.Close.asof(kl_index)),xytext=(kl_index, self.kl_pd.Close.asof(kl_index)+4),arrowprops=dict(facecolor='yellow',shrink=0.1),horizontalalignment='left',verticalalignment='top') 302 | 303 | elif signal < 0:# 卖出 304 | if is_win == True:#避免未买先卖 305 | end = self.kl_pd.index.get_loc(kl_index) 306 | is_win = False 307 | dispCont_List.append("End order:\n"+self.kl_pd.index[end].strftime('%Y-%m-%d')+'\n'+str(today.Close)+'\n') 308 | self.cash_hold = int(self.posit_num*today.Close) 309 | self.market_total = 0 310 | 311 | if self.kl_pd.Close[end] < self.kl_pd.Close[start]:#赔钱显示绿色 312 | subplotP0.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='green',alpha=0.38) 313 | 314 | else:#赚钱显示绿色 315 | subplotP0.fill_between(self.kl_pd.index[start:end],0,self.kl_pd.Close[start:end],color='red',alpha=0.38) 316 | list_signal.append(is_win) 317 | 318 | if is_win == True: 319 | self.market_total = int(self.posit_num*today.Close) 320 | self.profit_curve.append(self.market_total) 321 | else: 322 | self.profit_curve.append(self.cash_hold) 323 | 324 | self.kl_pd['keep'] = list_signal 325 | self.kl_pd['keep'].fillna(method = 'ffill',inplace = True) 326 | 327 | """ 计算基准收益 """ 328 | self.kl_pd['benchmark_profit'] = np.log(self.kl_pd.Close/self.kl_pd.Close.shift(1)) 329 | """ 计算趋势突破策略收益 """ 330 | self.kl_pd['trend_profit'] = self.kl_pd.keep*self.kl_pd.benchmark_profit 331 | """ 可视化收益情况对比 """ 332 | self.kl_pd[['benchmark_profit','trend_profit']].cumsum().plot(grid=True,ax = subplotP2) 333 | subplotP2.legend(['benchmark_profit','trend_profit'],loc='best') 334 | subplotP2.set_xticks([]) #去掉横坐标值 335 | 336 | self.kl_pd['profit'] = self.profit_curve 337 | self.kl_pd.profit.plot(ax = subplotP1) 338 | subplotP1.legend(['profit'],loc='best') 339 | subplotP1.set_xticks([]) #去掉横坐标值 340 | 341 | return dispCont_List 342 | 343 | class FactorPickStockAng: 344 | def __init__(self,**kwargs): 345 | self.threshold_ang_min = -np.inf 346 | if 'threshold_ang_min' in kwargs: 347 | #设置最小角度阈值 348 | self.threshold_ang_min = kwargs['threshold_ang_min'] 349 | 350 | self.threshold_ang_max = np.inf 351 | if 'threshold_ang_max' in kwargs: 352 | #设置最大角度阈值 353 | self.threshold_ang_max = kwargs['threshold_ang_max'] 354 | 355 | def calc_regress_deg(self, y_arr): 356 | x= np.arange(0, len(y_arr)) 357 | x = sm.add_constant(x)#添加常数列1 358 | 359 | model = regression.linear_model.OLS(y_arr, x).fit()#使用OLS做拟合 360 | rad = model.params[1]#y = kx + b :params[1] = k 361 | deg = np.rad2deg(rad)#弧度转换为角度 362 | 363 | intercept = model.params[0]##y = kx + b :params[0] = b 364 | reg_y_fit = x * rad + intercept 365 | 366 | return deg, x, reg_y_fit, y_arr 367 | 368 | def fit_pick(self, Close): 369 | 370 | ang, x, reg_y_fit, y_arr = self.calc_regress_deg(Close)#计算走势角度 371 | return 'deg = '+str(ang) 372 | 373 | 374 | -------------------------------------------------------------------------------- /Chapter23/RedefPanelMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import wx 5 | import numpy as np 6 | import pandas as pd 7 | import pandas_datareader.data as web 8 | import matplotlib 9 | import matplotlib.pyplot as plt 10 | from matplotlib.figure import Figure 11 | import matplotlib.dates as mdates 12 | import matplotlib.finance as mpf 13 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 14 | import matplotlib.gridspec as gridspec#分割子图 15 | import datetime 16 | 17 | plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 18 | plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 19 | 20 | class MPL_Panel_Base(wx.Panel): 21 | def __init__(self,parent): 22 | wx.Panel.__init__(self,parent=parent, id=-1) 23 | 24 | self.figure = Figure() 25 | gs = gridspec.GridSpec(4, 1, left=0.05, bottom=0.10, right=0.96, top=0.96, wspace=None, hspace=0.1, height_ratios=[3.5,1,1,1]) 26 | self.am = self.figure.add_subplot(gs[0,:]) 27 | self.vol = self.figure.add_subplot(gs[1,:]) 28 | self.macd = self.figure.add_subplot(gs[2,:]) 29 | self.devol = self.figure.add_subplot(gs[3,:]) 30 | 31 | self.FigureCanvas = FigureCanvas(self, -1, self.figure)#figure加到FigureCanvas 32 | self.TopBoxSizer = wx.BoxSizer(wx.VERTICAL) 33 | self.TopBoxSizer.Add(self.FigureCanvas,proportion = -1, border = 2,flag = wx.ALL | wx.EXPAND) 34 | self.SetSizer(self.TopBoxSizer) 35 | 36 | def draw_subgraph(self,stockdat,numt): 37 | 38 | """ 绘制K线图 """ 39 | ohlc = list(zip(np.arange(0,len(stockdat.index)),stockdat.Open,stockdat.Close,stockdat.High,stockdat.Low))#使用zip方法生成数据列表 40 | mpf.candlestick(self.am, ohlc, width=0.5, colorup='r', colordown='g')#绘制K线走势 41 | ''' 绘制均线 ''' 42 | self.am.plot(numt, stockdat['Ma20'],'black', label='M20',lw=1.0) 43 | self.am.plot(numt, stockdat['Ma60'],'green',label='M60', lw=1.0) 44 | self.am.plot(numt, stockdat['Ma120'],'blue',label='M120', lw=1.0) 45 | self.am.legend(loc='best',shadow=True, fontsize ='10') 46 | ''' 绘制成交量 ''' 47 | self.vol.bar(numt, stockdat.Volume,color=['g' if stockdat.Open[x] > stockdat.Close[x] else 'r' for x in range(0,len(stockdat.index))]) 48 | ''' 绘制MACD ''' 49 | #绘制BAR>0 柱状图 50 | bar_red = np.where(stockdat['macd_bar']>0, 2*stockdat['macd_bar'], 0) 51 | #绘制BAR<0 柱状图 52 | bar_green = np.where(stockdat['macd_bar']<0, 2*stockdat['macd_bar'], 0) 53 | 54 | self.macd.plot(numt, stockdat['macd_dif'], 'red', label='macd dif') #dif 55 | self.macd.plot(numt, stockdat['macd_dea'], 'blue', label='macd dea') #dea 56 | self.macd.bar(numt, bar_red, facecolor='red',label='hist bar') 57 | self.macd.bar(numt, bar_green, facecolor='green',label='hist bar') 58 | self.macd.legend(loc='best',shadow=True, fontsize ='10') 59 | #legend = self.macd.legend(loc='best',shadow=True, fontsize ='10') 60 | #legend.get_frame().set_facecolor('#00FFCC')# Put a nicer background color on the legend. 61 | #legend.get_title().set_fontsize(fontsize = 20) 62 | ''' 绘制KDJ ''' 63 | self.devol.plot(numt, stockdat['K'], 'blue', label='K') #K 64 | self.devol.plot(numt, stockdat['D'], 'g--', label='D') #D 65 | self.devol.plot(numt, stockdat['J'], 'r-', label='J') #J 66 | self.devol.legend(loc='best',shadow=True, fontsize ='10') 67 | 68 | def draw_jumpgap(self,stockdat,jump_pd): 69 | ''' 绘制跳空缺口 ''' 70 | for kl_index in np.arange(0, jump_pd.shape[0]): 71 | today = jump_pd.ix[kl_index]#若版本提示已经弃用 可使用loc或iloc替换 72 | inday = stockdat.index.get_loc(jump_pd.index[kl_index]) 73 | if today['jump_power'] > 0: 74 | self.am.annotate('up',xy=(inday,today.Low*0.95),xytext=(inday, today.Low*0.9),arrowprops=dict(facecolor='red',shrink=0.1),horizontalalignment='left',verticalalignment='top') 75 | elif today['jump_power'] < 0: 76 | self.am.annotate('down',xy=(inday,today.High*1.05),xytext=(inday, today.High*1.1),arrowprops=dict(facecolor='green',shrink=0.1),horizontalalignment='left',verticalalignment='top') 77 | 78 | def draw_avercross(self,stockdat,list_signal): 79 | ''' 绘制金叉死叉 ''' 80 | for kl_index,signal_dat in enumerate(list_signal): 81 | inday = stockdat.index.get_loc(list_signal.index[kl_index]) 82 | if signal_dat < 0: 83 | self.am.annotate(u"死叉", xy=(inday, stockdat['Ma20'][inday]), xytext=(inday, stockdat['Ma20'][inday]+1.5),arrowprops=dict(facecolor='green', shrink=0.2)) 84 | elif signal_dat > 0: 85 | self.am.annotate(u"金叉", xy=(inday, stockdat['Ma60'][inday]), xytext=(inday, stockdat['Ma60'][inday]-1.5),arrowprops=dict(facecolor='red', shrink=0.2)) 86 | 87 | def draw_ndaysbreak(self,stockdat,list_signal): 88 | ''' 绘制N日突破 ''' 89 | for kl_index in np.arange(0, stockdat.shape[0]): 90 | today = stockdat.ix[kl_index] 91 | """ 收盘价超过N2最低价 卖出股票持有""" 92 | if today['Close'] < today['N2_Low']: 93 | self.am.annotate(u"下突破", xy=(kl_index, stockdat['High'][kl_index]), xytext=(kl_index, stockdat['High'][kl_index]+1.5),arrowprops=dict(facecolor='green', shrink=0.2)) 94 | 95 | """ 收盘价超过N1最高价 买入股票持有""" 96 | if today['Close'] > today['N1_High']: 97 | self.am.annotate(u"上突破", xy=(kl_index, stockdat['Low'][kl_index]), xytext=(kl_index, stockdat['Low'][kl_index]-1.5),arrowprops=dict(facecolor='red', shrink=0.2)) 98 | 99 | def update_subgraph(self): 100 | #修改图形的任何属性后都必须使用self.updatePlot()更新GUI界面 101 | self.FigureCanvas.draw() 102 | 103 | def clear_subgraph(self): 104 | #再次画图前,必须调用该命令清空原来的图形 105 | self.am.clear() 106 | self.vol.clear() 107 | self.devol.clear() 108 | self.macd.clear() 109 | #self.figure.set_canvas(self.FigureCanvas) 110 | #self.updatePlot() 111 | 112 | def xylabel_tick_lim(self,title,dates): 113 | # 设置X轴 Y轴的标签 114 | # 给图像添加一个标题 115 | # 设置X轴的刻度大小 116 | # 设置x轴的显示范围 117 | 118 | self.devol.set_xlabel(u"时间") 119 | self.am.set_ylabel(u"日K线") 120 | self.vol.set_ylabel(u"成交量") 121 | self.devol.set_ylabel(u"KDJ") 122 | self.macd.set_ylabel(u"MACD") 123 | self.am.set_title(title) 124 | dir(self.figure) 125 | 126 | major_tick = len(dates) 127 | self.am.set_xlim(0,major_tick) #设置一下x轴的范围 128 | self.vol.set_xlim(0,major_tick) #设置一下x轴的范围 129 | self.devol.set_xlim(0,major_tick) #设置一下x轴的范围 130 | self.macd.set_xlim(0,major_tick) #设置一下x轴的范围 131 | 132 | self.am.set_xticks(range(0,major_tick,15))#每五天标一个日期 133 | self.vol.set_xticks(range(0,major_tick,15))#每五天标一个日期 134 | self.devol.set_xticks(range(0,major_tick,15))#每五天标一个日期 135 | self.macd.set_xticks(range(0,major_tick,15))#每五天标一个日期 136 | self.devol.set_xticklabels([dates.strftime('%Y-%m-%d')[index] for index in self.devol.get_xticks()])#标签设置为日期 137 | 138 | for line in self.am.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 139 | line.set_visible(False) 140 | for line in self.vol.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 141 | line.set_visible(False) 142 | for line in self.macd.xaxis.get_ticklabels():#X-轴每个ticker标签隐藏 143 | line.set_visible(False) 144 | for label in self.devol.xaxis.get_ticklabels(): 145 | label.set_rotation(45)#X-轴每个ticker标签都向右倾斜45度 146 | label.set_fontsize(10)#设置标签字体 147 | 148 | self.am.grid(True,color='k') 149 | self.vol.grid(True,color='k') 150 | self.macd.grid(True,color='k') 151 | self.devol.grid(True,color='k') 152 | 153 | class Loop_Panel_Base(wx.Panel): 154 | def __init__(self,parent): 155 | wx.Panel.__init__(self,parent=parent, id=-1) 156 | self.figure = Figure() 157 | 158 | gs = gridspec.GridSpec(3, 1, left=0.05, bottom=0.10, right=0.96, top=0.96, wspace=None, hspace=0.1, height_ratios=[1.5,1,1]) 159 | 160 | self.trade = self.figure.add_subplot(gs[0,:]) 161 | self.total = self.figure.add_subplot(gs[1,:]) 162 | self.profit = self.figure.add_subplot(gs[2,:]) 163 | 164 | self.FigureCanvas = FigureCanvas(self, -1, self.figure)#figure加到FigureCanvas 165 | self.TopBoxSizer = wx.BoxSizer(wx.VERTICAL) 166 | self.TopBoxSizer.Add(self.FigureCanvas,proportion = -1, border = 2,flag = wx.ALL | wx.EXPAND) 167 | self.SetSizer(self.TopBoxSizer) 168 | 169 | def update_subgraph(self): 170 | #修改图形的任何属性后都必须使用self.updatePlot()更新GUI界面 171 | self.FigureCanvas.draw() 172 | 173 | def clear_subgraph(self): 174 | #再次画图前,必须调用该命令清空原来的图形 175 | self.trade.clear() 176 | self.total.clear() 177 | self.profit.clear() 178 | 179 | def xylabel_tick_lim(self,title): 180 | # 设置X轴 Y轴的标签 181 | # 给图像添加一个标题 182 | # 设置X轴的刻度大小 183 | # 设置x轴的显示范围 184 | 185 | self.profit.set_xlabel(u"时间") 186 | self.trade.set_ylabel(u"交易区间") 187 | self.total.set_ylabel(u"资金收益") 188 | self.profit.set_ylabel(u"收益率对比") 189 | self.trade.set_title(title) 190 | 191 | #self.trade.xticks([]) #去掉横坐标值 192 | #self.total.xticks([]) #去掉横坐标值 193 | self.profit.xaxis.set_minor_locator(mdates.WeekdayLocator(byweekday=(1),interval=1)) 194 | self.profit.xaxis.set_minor_formatter(mdates.DateFormatter('%d\n%a'))#标签设置为日期 195 | 196 | -------------------------------------------------------------------------------- /Chapter23/StockDataMod.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | #-*- encoding: utf-8 -*- 3 | #author pythontab.com 4 | import numpy as np 5 | import pandas as pd 6 | import pandas_datareader.data as web 7 | import datetime 8 | import csv,os 9 | import codecs 10 | import talib 11 | 12 | #获取股票数据接口 13 | def GetStockDatApi(stockName=None,stockTimeS=None,stockTimeE=None): 14 | 15 | path = 'C:\programPY\GUI_Inter_StoMPL\StockData' 16 | 17 | str_stockTimeS = stockTimeS.strftime('%Y-%m-%d') 18 | str_stockTimeE = stockTimeE.strftime('%Y-%m-%d') 19 | newname = stockName+'+'+str_stockTimeS+'+'+str_stockTimeE+'.csv' 20 | newpath = os.path.join(path,newname) 21 | 22 | #path=os.path.abspath('.')#获取当前脚本所在的路径 C:\Program Files\Notepad++ 23 | print(u"当前:%s" % os.getcwd())#当前工作目录 24 | os.chdir(path) 25 | print(u"修改为:%s" % os.getcwd())#修改后工作目录 26 | 27 | for filename in os.listdir(path):#遍历路径下所有文件 28 | #print(os.path.join(path,filename)) 29 | 30 | if stockName in filename: 31 | if filename.count('+') == 2:#存在CSV文件 32 | str_dfLoadTimeS = filename.split('+')[1] 33 | str_dfLoadTimeE = filename.split('+')[2].split('.')[0] 34 | 35 | dtm_dfLoadTimeS = datetime.datetime.strptime(str_dfLoadTimeS,'%Y-%m-%d') 36 | dtm_dfLoadTimeE = datetime.datetime.strptime(str_dfLoadTimeE,'%Y-%m-%d') 37 | 38 | if((dtm_dfLoadTimeS - stockTimeS).days <= 0)and((dtm_dfLoadTimeE - stockTimeE).days >= 0):#起止日期在文件内则读取CSV文件获取数据 39 | print("123",(dtm_dfLoadTimeS - stockTimeS).days) 40 | print("345",(dtm_dfLoadTimeE - stockTimeE).days) 41 | stockDat = pd.read_csv(os.path.join(path,filename),parse_dates=True,index_col=0,encoding='gb2312') 42 | print(stockDat.head(),stockDat.tail()) 43 | stockDat = stockDat.loc[stockTimeS:stockTimeE] 44 | print(stockDat.head(),stockDat.tail()) 45 | else:#起止日期不相同则重新下载 46 | stockDat = web.DataReader(stockName, "yahoo", stockTimeS, stockTimeE) 47 | os.rename(filename, newname) 48 | stockDat.to_csv(newpath,columns=stockDat.columns,index=True) 49 | return stockDat 50 | else: 51 | break 52 | 53 | stockDat = web.DataReader(stockName, "yahoo", stockTimeS, stockTimeE) 54 | stockDat.to_csv(newpath,columns=stockDat.columns,index=True) 55 | 56 | return stockDat 57 | 58 | #处理股票数据接口 59 | def GetStockDatPro(stockName=None,stockTimeS=None,stockTimeE=None): 60 | stockPro = GetStockDatApi(stockName, stockTimeS, stockTimeE) 61 | 62 | #处理移动平均线 63 | stockPro['Ma20'] = pd.rolling_mean(stockPro.Close,window=20) 64 | stockPro['Ma60'] = pd.rolling_mean(stockPro.Close,window=60) 65 | stockPro['Ma120'] = pd.rolling_mean(stockPro.Close,window=120) 66 | 67 | #处理MACD 68 | stockPro['macd_dif'],stockPro['macd_dea'], stockPro['macd_bar'] = talib.MACD(stockPro['Close'].values, fastperiod=12, slowperiod=26, signalperiod=9) 69 | 70 | #处理KDJ 71 | xd = 9-1 72 | date = stockPro.index.to_series() 73 | RSV = pd.Series(np.zeros(len(date)-xd),index=date.index[xd:]) 74 | Kvalue = pd.Series(0.0,index=RSV.index) 75 | Dvalue = pd.Series(0.0,index=RSV.index) 76 | Kvalue[0],Dvalue[0] = 50,50 77 | 78 | for day_ind in range(xd, len(date)): 79 | RSV[date[day_ind]] = (stockPro.Close[day_ind] - stockPro.Low[day_ind-xd:day_ind+1].min())/(stockPro.High[day_ind-xd:day_ind+1].max()-stockPro.Low[day_ind-xd:day_ind+1].min())*100 80 | if day_ind > xd: 81 | index = day_ind-xd 82 | Kvalue[index] = 2.0/3*Kvalue[index-1]+RSV[date[day_ind]]/3 83 | Dvalue[index] = 2.0/3*Dvalue[index-1]+Kvalue[index]/3 84 | stockPro['RSV'] = RSV 85 | stockPro['K'] = Kvalue 86 | stockPro['D'] = Dvalue 87 | stockPro['J'] = 3*Kvalue-2*Dvalue 88 | return stockPro 89 | 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-Quantitative-Trading 2 | 教你用Python进阶量化交易-专栏 3 | 4 | 微信公众号:【元宵大师带你用Python量化交易】 5 | --------------------------------------------------------------------------------