├── Config.py
├── DateTimeUtils.py
├── EmailService.py
├── FundHoldStrategy.py
├── HoldStocks.txt
├── Ma20Strategy.py
├── MacdStrategy.py
├── Main.py
├── MonitorStrategy.py
├── NewStockMonitor.py
├── README.md
├── RedisService.py
├── SchedulerService.py
├── Signal.py
├── SmsService.py
├── StockService.py
├── TestModule.py
├── nohup.out
├── requirements.txt
├── seleniumTest.py
└── stockMonitor.bat
/Config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | #coding:utf8
3 | #设置为自己的redis
4 | REDIS_HOST=''
5 | REDIS_PORT='6379'
6 | REDIS_PASSWD=''
7 | KEY_HOLD_STOCK='stock:hold'
8 | KEY_NEW_STOCK='stock:newstock'
9 | CHAR_SET='utf-8'
10 | FORMAT_STR="%Y-%m-%d"
11 | DATE_TIME_FORMAT_STR="%Y-%m-%d %H:%M:%S"
12 | HOLD_STOCKS_FILE_NAME='HoldStocks.txt'
--------------------------------------------------------------------------------
/DateTimeUtils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import time
4 | class DateTimeUtils():
5 | def __init__(self):
6 | pass
7 | def getCurrentQuarter(self):
8 | localtime=time.localtime()
9 | month=localtime.tm_mon
10 | return int(month/3+1)
11 |
12 | #获取需要计算的年份和季度数
13 | def getCalYearAndQuarter(self):
14 | localtime=time.localtime()
15 | #获取计算年份
16 | calc_year=localtime.tm_year
17 | #获取当前季度
18 | now_quarter=self.getCurrentQuarter()
19 | cal_quarter=now_quarter-1
20 | #如果计算季度为0,则计算年份减1,季度为4
21 | if 0==cal_quarter:
22 | cal_quarter=4
23 | calc_year=calc_year-1
24 | return (calc_year,cal_quarter)
25 |
26 | dateTimeUtils=DateTimeUtils()
27 |
28 | if __name__=='__main__':
29 | calc_year,cal_quarter=dateTimeUtils.getCalYearAndQuarter()
30 | print(calc_year)
31 | print(cal_quarter)
32 |
--------------------------------------------------------------------------------
/EmailService.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 |
3 | from email.mime.image import MIMEImage
4 | from email.mime.multipart import MIMEMultipart
5 | from email.mime.text import MIMEText
6 | from email.mime.base import MIMEBase
7 | import email
8 | from smtplib import SMTP
9 | import smtplib
10 | import os.path
11 | import mimetypes
12 | import time
13 | #设置为自己的邮箱服务器
14 | HOST="smtp.qq.com" #使用的邮箱的smtp服务器地址,这里是163的smtp地址,经过测试网易和搜狐的邮箱可用
15 | SENDER="" #用户名
16 | PASSWORD="" #密码
17 | POSTFIX="qq.com" #邮箱的后缀,网易就是163.com
18 | RECIPS=["guiyin@chinaexpressair.com"] #这里接收人也设置为自己
19 | # 一个包含文本和html的多部分邮件。多部分消息通常包含纯文本和html格式,客户端自行选择显示哪个。(web客户端显示html,命令行客户端显示纯文本)
20 | def make_mpa_msg(content):
21 | myemail = MIMEMultipart('alternative')
22 | text = MIMEText(content+'\r\n', 'plain',_charset="utf-8") #纯文本的邮件消息正文
23 | myemail.attach(text) #消息正文绑定到邮件对象
24 | html = MIMEText( #html邮件消息正文
25 | '
'+content+'
'
26 | '', 'html')
27 | myemail.attach(html) #消息正文绑定到邮件对象
28 | return myemail
29 |
30 | # 创建一个文本和图片的邮件
31 | def make_img_msg(imgfile):
32 | f = open(imgfile, 'rb') #创建文件指针,这里要以rb的模式取读
33 | data = f.read() #读取图片成字节流
34 | f.close() #文件关闭
35 | ctype, encoding = mimetypes.guess_type(imgfile) #ctype为根据文件获取的数据传输类型image/jpeg,encoding应该为None
36 | if ctype is None or encoding is not None:
37 | ctype = 'application/octet-stream'
38 | maintype, subtype = ctype.split('/', 1) #maintype为文件所属类image,subtype为具体文件类型jpeg
39 | myemail = MIMEImage(data, name=subtype) #生成图片邮件,name=文件类型jpeg
40 | basename = os.path.basename(imgfile) #basename为文件名,不包含路径
41 | myemail.add_header('Content-Disposition','attachment; filename="%s"' % basename) #添加邮件头
42 | return myemail
43 |
44 | # 创建一个文本和文件的邮件
45 | def make_file_msg(file_name):
46 | # 构造MIMEBase对象做为文件附件内容并附加到根容器
47 | ctype, encoding = mimetypes.guess_type(file_name) # ctype为根据文件获取的数据传输类型image/jpeg,encoding应该为None
48 | if ctype is None or encoding is not None:
49 | ctype = 'application/octet-stream'
50 | maintype, subtype = ctype.split('/', 1) # maintype为文件所属类image,subtype为具体文件类型jpeg
51 | print(maintype,subtype)
52 | ## 读入文件内容并格式化
53 | f = open(file_name, 'rb') # 创建文件指针,这里要以rb的模式取读
54 | myemail = MIMEBase(maintype, subtype)
55 | myemail.set_payload(f.read()) #设置负载数据
56 | f.close()
57 | email.encoders.encode_base64(myemail) #将邮件编码
58 | #设置附件头
59 | basename = os.path.basename(file_name) #basename为文件名,不包含路径
60 | myemail.add_header('Content-Disposition','attachment; filename="%s"' % basename) #添加邮件头
61 | return myemail
62 |
63 | def sendMsg(fr, to, message):
64 | sendSvr = smtplib.SMTP_SSL(HOST,465)
65 | #sendSvr.connect(HOST) # 连接服务器
66 | sendSvr.login(SENDER, PASSWORD) # 登录操作
67 | errs = sendSvr.sendmail(fr, to, message) #参数:发件人,收件人,消息正文
68 | sendSvr.quit()
69 |
70 | def sendTextOrHtml(subject,content,recips):
71 | msg = make_mpa_msg(content)
72 | msg['From'] = SENDER
73 | msg['To'] = ', '.join(recips)
74 | msg['Subject'] = subject
75 | sendMsg(SENDER,recips, msg.as_string())
76 |
77 | if __name__ == '__main__':
78 | recips=['test@qq.com','test@qq.com']
79 | sendTextOrHtml('人生苦短,我用python!','Life is short,you need Python!',recips)
80 | #time.sleep(1);
81 | #msg = make_mpa_msg('Life is short,you need Python!')
82 | #msg['From'] = SENDER
83 | #msg['To'] = ', '.join(RECIPS)
84 | #msg['Subject'] = '人生苦短,我用python!'
85 | #sendMsg(SENDER, RECIPS, msg.as_string())
86 | #print('发送图片消息体')
87 | #msg = make_img_msg(r'D:/test.jpg')
88 | #msg['From'] = SENDER
89 | #msg['To'] = ', '.join(RECIPS)
90 | #msg['Subject'] = '图片消息邮件'
91 | #sendMsg(SENDER, RECIPS, msg.as_string())
92 | #
93 | #print('发送文件消息体')
94 | #msg = make_file_msg(r'D:/test.mp4')
95 | #msg['From'] = SENDER
96 | #msg['To'] = ', '.join(RECIPS)
97 | #msg['Subject'] = '文件消息邮件'
98 | #sendMsg(SENDER, RECIPS, msg.as_string())
--------------------------------------------------------------------------------
/FundHoldStrategy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import sys
4 | import tushare as ts
5 | import time
6 | import datetime
7 | import Signal
8 | import Config
9 | import EmailService
10 | from RedisService import redisService
11 | from StockService import stockService
12 | from DateTimeUtils import dateTimeUtils
13 | class FundHoldStrategy():
14 | strategyName='fund_hold'
15 | buyKey='buy:fund_hold'
16 | sellKey='sell:fund_hold'
17 | compare_last_increment=30
18 | fund_hold_rate=30
19 | def __init__(self):
20 | pass
21 |
22 | def strategy(self):
23 | try:
24 | calc_year,cal_quarter=dateTimeUtils.getCalYearAndQuarter()
25 | df=ts.fund_holdings(calc_year,cal_quarter)
26 | for index,row in df.iterrows():
27 | code=row.code
28 | #同上期相比,基金在增仓,且增幅大于50%
29 | nlast=float(row.nlast)
30 | #基金持股流通占比大于30%
31 | ratio=float(row.ratio)
32 | if nlast > self.compare_last_increment and ratio > self.fund_hold_rate:
33 | redisService.sadd(self.buyKey,code)
34 | return None
35 | except:
36 | print(self.strategyName,"策略出现异常:", sys.exc_info()[0])
37 | finally:
38 | #让出线程
39 | time.sleep(1)
40 | return None
41 | return None
42 |
43 | def getEmailContent(self):
44 | content=None
45 | buyList=redisService.smembers(self.buyKey)
46 | sellList=redisService.smembers(self.sellKey)
47 | if len(buyList) > 0 or len(sellList) > 0 :
48 | content='策略名称:' + self.strategyName
49 | content=content+'
'
50 | content=content+'买入('+str(len(buyList))+'支):
'
51 | content = content + ','.join(buyList)
52 | content=content+'\r\n'
53 | content=content+'卖出('+str(len(sellList))+'支):
'
54 | content = content + ','.join(sellList)
55 | return content
56 |
57 | def clearRedis(self):
58 | redisService.delete(self.buyKey)
59 | redisService.delete(self.sellKey)
60 |
61 | fundHoldStrategy=FundHoldStrategy()
62 |
63 | if __name__=='__main__':
64 | print("基金持股策略开始时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
65 | fundHoldStrategy.clearRedis()
66 | fundHoldStrategy.strategy()
67 | emailContent=fundHoldStrategy.getEmailContent()
68 | if emailContent :
69 | #发送结果邮件
70 | EmailService.sendTextOrHtml('基金持股策略结果',emailContent,['yantaozhou@qq.com'])
71 | else:
72 | print('基金持股策略结束,无需发送邮件')
73 | #清理redis缓存
74 | fundHoldStrategy.clearRedis()
75 | print("基金持股策略结束时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
--------------------------------------------------------------------------------
/HoldStocks.txt:
--------------------------------------------------------------------------------
1 | 601618
2 | 601688
3 | 000157
4 | 002029
5 | 002269
6 | 300195
--------------------------------------------------------------------------------
/Ma20Strategy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import sys
4 | import tushare as ts
5 | import time
6 | import datetime
7 | import Signal
8 | import Config
9 | import EmailService
10 | from RedisService import redisService
11 | from StockService import stockService
12 | class Ma20Strategy():
13 | strategyName='ma20'
14 | buyKey='buy:ma20'
15 | sellKey='sell:ma20'
16 | dateDelta=7
17 | def __init__(self):
18 | pass
19 | def strategy(self,stockNo):
20 | if stockNo is not None:
21 | try:
22 | nowDate=datetime.datetime.now()
23 | endDateStr=nowDate.strftime(Config.FORMAT_STR)
24 | startDateStr=(nowDate-datetime.timedelta(days=self.dateDelta)).strftime(Config.FORMAT_STR)
25 | df = ts.get_hist_data(stockNo,start=startDateStr,end=endDateStr)
26 | if (df is not None) and (len(df.index) >= self.dateDelta):
27 | ma20 = df[u'ma20']
28 | close = df[u'close']
29 | underline=True
30 | for index in range(1,self.dateDelta-1):
31 | if close[index] > ma20[index]:
32 | underline=False
33 | break
34 | if underline and close[0] > ma20[0]:
35 | #存入买入股票编码
36 | redisService.sadd(self.buyKey,stockNo)
37 | return Signal.Signal(stockNo=stockNo,buy=True,dateStr=endDateStr)
38 | upline=True
39 | for index in range(1,self.dateDelta-1):
40 | if close[index] < ma20[index]:
41 | upline=False
42 | break
43 | if upline and close[0] < ma20[0]:
44 | #存入买入股票编码
45 | redisService.sadd(self.sellKey,stockNo)
46 | return Signal.Signal(stockNo=stockNo,sell=True,dateStr=dateStr)
47 | #如果前3天的收盘价在20天均线下方,而当前收盘价在均线上方,则发出买入信号
48 | # if close[1] < ma20[1] and close[2] < ma20[2] and close[3] < ma20[3] and close[0] > ma20[0] :
49 | #存入买入股票编码
50 | # redisService.sadd(self.buyKey,stockNo)
51 | # return Signal.Signal(stockNo=stockNo,buy=True,dateStr=dateStr)
52 | #如果前3天的收盘价在20天均线上方,而当前收盘价在均线上方,则发出买入信号
53 | # if close[1] > ma20[1] and close[2] > ma20[2] and close[3] > ma20[3] and close[0] < ma20[0] :
54 | # redisService.sadd(self.sellKey,stockNo)
55 | # return Signal.Signal(stockNo=stockNo,sell=True,dateStr=dateStr)
56 | return None
57 | except:
58 | print(self.strategyName,"策略出现异常:", sys.exc_info()[0])
59 | finally:
60 | #让出线程
61 | time.sleep(1)
62 | return None
63 | return None
64 |
65 | def getEmailContent(self):
66 | content=None
67 | buyList=redisService.smembers(self.buyKey)
68 | sellList=redisService.smembers(self.sellKey)
69 | if len(buyList) > 0 or len(sellList) > 0 :
70 | content='策略名称:' + self.strategyName
71 | content=content+'
'
72 | content=content+'买入('+str(len(buyList))+'支):
'
73 | content = content + ','.join(buyList)
74 | content=content+'\r\n'
75 | content=content+'卖出('+str(len(sellList))+'支):
'
76 | content = content + ','.join(sellList)
77 | return content
78 |
79 | def clearRedis(self):
80 | redisService.delete(self.buyKey)
81 | redisService.delete(self.sellKey)
82 |
83 | ma20Strategy=Ma20Strategy()
84 |
85 | if __name__=='__main__':
86 | print("Ma20策略开始时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
87 | ma20Strategy.clearRedis()
88 | stockNos=stockService.getStockCodes()
89 | for index in range(0,len(stockNos)):
90 | stockNo=stockNos[index]
91 | ma20Strategy.strategy(stockNo)
92 | emailContent=ma20Strategy.getEmailContent()
93 | if emailContent :
94 | #发送结果邮件
95 | EmailService.sendTextOrHtml('Ma20策略结果',emailContent,['yantaozhou@qq.com'])
96 | else:
97 | print('Ma20策略结束,无需发送邮件')
98 | #清理redis缓存
99 | ma20Strategy.clearRedis()
100 | print("Ma20策略结束时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
--------------------------------------------------------------------------------
/MacdStrategy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import sys
4 | import tushare as ts
5 | import time
6 | import datetime
7 | import Signal
8 | import Config
9 | import EmailService
10 | from RedisService import redisService
11 | from StockService import stockService
12 | class MacdStrategy():
13 | strategyName='MACD'
14 | buyKey='buy:MACD'
15 | sellKey='sell:MACD'
16 | dateDelta=52
17 | def __init__(self):
18 | pass
19 | #计算快速平滑移动均线
20 | def cal_EMA(self,df,EMA_days,days):
21 | #当前收盘价
22 | close=float(df.ix[EMA_days-days,:].close)
23 | #递归结束条件(如果计算天数为1时,则返回df的行数为12+2的收盘价(第一个EMA取前一日收盘价))
24 | if 1 >= days :
25 | return float(df.ix[EMA_days+2,:].close)
26 | return 2*close/(EMA_days+1) + (EMA_days-1)*self.cal_EMA(df,days-1)/(EMA_days+1)
27 |
28 | def cal_DEA(self,df):
29 | sum=0
30 | for num in range(0,9):
31 | EMA=cal_EMA(df,num+1,num+1)
32 | sum=sum+EMA
33 | return sum/9
34 |
35 | def strategy(self,stockNo):
36 | if stockNo is not None:
37 | try:
38 | nowDate=datetime.datetime.now()
39 | endDateStr=nowDate.strftime(Config.FORMAT_STR)
40 | startDateStr=(nowDate-datetime.timedelta(days=self.dateDelta)).strftime(Config.FORMAT_STR)
41 | df = ts.get_hist_data(stockNo,start=startDateStr,end=endDateStr)
42 | #最少需要28行数据
43 | if df and (len(df.index) >= 28) :
44 | front_EMA12=cal_EMA(df.ix[1:-1,:],12,12)
45 | front_EMA26=cal_EMA(df.ix[1:-1,:],26,26)
46 | #前日差离率
47 | front_DIF=EMA12-EMA26
48 | EMA12=cal_EMA(df,12,12)
49 | EMA26=cal_EMA(df,26,26)
50 | #今日差离率
51 | DIF=EMA12-EMA26
52 | DEA=cal_DEA(df)
53 | BAR=2*(DIF-DEA)
54 | #根据离差率判断是否属于上升趋势
55 | if front_DIF < DIF and DIF >= DEA :
56 | #如果离差率上穿DEA则为金叉,发出买入信号
57 | #存入买入股票编码
58 | redisService.sadd(self.buyKey,stockNo)
59 | #如果离差率下破DEA则为死叉,发出卖出信号
60 | if front_DIF > DIF and DIF <= DEA :
61 | #如果当前股票在持股里面,则发出卖出信号
62 | holdStocks=redisService.smembers(Config.KEY_HOLD_STOCK)
63 | for holdNo in holdStocks:
64 | if holdNo == stockNo :
65 | redisService.sadd(self.sellKey,stockNo)
66 | return None
67 | except:
68 | print(self.strategyName,"策略出现异常:", sys.exc_info()[0])
69 | finally:
70 | #让出线程
71 | time.sleep(1)
72 | return None
73 | return None
74 |
75 | def getEmailContent(self):
76 | content=None
77 | buyList=redisService.smembers(self.buyKey)
78 | sellList=redisService.smembers(self.sellKey)
79 | if len(buyList) > 0 or len(sellList) > 0 :
80 | content='策略名称:' + self.strategyName
81 | content=content+'
'
82 | content=content+'买入('+str(len(buyList))+'支):
'
83 | content = content + ','.join(buyList)
84 | content=content+'\r\n'
85 | content=content+'卖出('+str(len(sellList))+'支):
'
86 | content = content + ','.join(sellList)
87 | return content
88 |
89 | def clearRedis(self):
90 | redisService.delete(self.buyKey)
91 | redisService.delete(self.sellKey)
92 |
93 | macdStrategy=MacdStrategy()
94 |
95 | if __name__=='__main__':
96 | print('请确保当日收盘后再运行,否则需要调整历史行情数据的获取截止日期为前一天')
97 | print("MACD策略开始时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
98 | macdStrategy.clearRedis()
99 | stockNos=stockService.getStockCodes()
100 | for index in range(0,len(stockNos)):
101 | stockNo=stockNos[index]
102 | macdStrategy.strategy(stockNo)
103 | emailContent=macdStrategy.getEmailContent()
104 | if emailContent :
105 | #发送结果邮件
106 | EmailService.sendTextOrHtml('MACD策略结果',emailContent,['yantaozhou@qq.com'])
107 | else:
108 | print('MACD策略结束,无需发送邮件')
109 | #清理redis缓存
110 | macdStrategy.clearRedis()
111 | print("MACD策略结束时间:",datetime.datetime.now().strftime(Config.DATE_TIME_FORMAT_STR))
--------------------------------------------------------------------------------
/Main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import time
4 | import threading
5 | import EmailService
6 | from SchedulerService import schedulerService
7 | from Ma20Strategy import ma20Strategy
8 | from MonitorStrategy import monitorStrategy
9 | from StockService import stockService
10 | def getStockNos():
11 | # return ['600050']
12 | return stockService.getStockCodes()
13 |
14 | def getStrategies():
15 | return [ma20Strategy]
16 |
17 | def func():
18 | #如果是交易时间,则直接返回
19 | if stockService.isTradeTime():
20 | print('交易时间,不执行非实时策略')
21 | return None
22 | strategies=getStrategies()
23 | stockNos=getStockNos()
24 | emailContent=''
25 | for strategy in strategies :
26 | for index in range(0,len(stockNos)):
27 | stockNo=str(stockNos[index])
28 | strategy.strategy(stockNo)
29 | emailContent=emailContent+strategy.getEmailContent()
30 | strategy.clearRedis()
31 | #发送结果邮件
32 | EmailService.sendTextOrHtml('stock策略结果',emailContent,['yantaozhou@qq.com'])
33 |
34 | def monitor():
35 | needEmail=monitorStrategy.strategy()
36 | if needEmail:
37 | emailContent=monitorStrategy.getEmailContent()
38 | monitorStrategy.clearRedis()
39 | #发送结果邮件
40 | EmailService.sendTextOrHtml('stock监控结果',emailContent,['yantaozhou@qq.com'])
41 | print('监控一次',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))
42 | #非实时
43 | def strategyScheduler():
44 | schedulerService.timming_exe(func,after=5)
45 | #实时
46 | def monitorScheduler():
47 | schedulerService.timming_exe(monitor,after=6,interval=5*60)
48 |
49 |
50 | threads=[]
51 | strategyThread=threading.Thread(target=strategyScheduler)
52 | #monitorThread=threading.Thread(target=monitorScheduler)
53 | threads.append(strategyThread)
54 | #threads.append(monitorThread)
55 | for thread in threads :
56 | thread.setDaemon(True)
57 | thread.start()
58 | for thread in threads :
59 | thread.join()
60 | print("执行结束了。\n")
61 |
62 |
63 |
--------------------------------------------------------------------------------
/MonitorStrategy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import sys
4 | import tushare as ts
5 | import time
6 | import datetime
7 | import Signal
8 | import Config
9 | import EmailService
10 | from SchedulerService import schedulerService
11 | from RedisService import redisService
12 | from StockService import stockService
13 |
14 | class MonitorStrategy():
15 | strategyName='monitor'
16 | buyKey='buy:monitor'
17 | sellKey='sell:monitor'
18 | rate=0.05
19 | def __init__(self):
20 | pass
21 | def strategy(self):
22 | needEmail=False
23 | #非交易时间,直接返回
24 | if not stockService.isTradeTime():
25 | return needEmail
26 | #获取持有的股票
27 | try:
28 | holdStocks=redisService.smembers(Config.KEY_HOLD_STOCK)
29 | for stockNo in holdStocks:
30 | #获取当前股票的实时分笔
31 | df=ts.get_realtime_quotes(stockNo)
32 | column_open=df['open']
33 | open=float(column_open[0])
34 | column_price=df['price']
35 | price=float(column_price[0])
36 | percent=abs(open-price)/open
37 | #如果波动幅度超过5%,则需要给出卖出提示
38 | #涨幅超过5%的情况
39 | if price>open and percent>=self.rate :
40 | needEmail=True
41 | redisService.sadd(self.buyKey,stockNo)
42 | if price=self.rate :
43 | needEmail=True
44 | redisService.sadd(self.sellKey,stockNo)
45 | except:
46 | print(self.strategyName,"策略出现异常:", sys.exc_info()[0])
47 | finally:
48 | #让出线程
49 | time.sleep(1)
50 | return needEmail
51 | return needEmail
52 |
53 | def getEmailContent(self):
54 | content='策略名称:' + self.strategyName
55 | content=content+'\r\n'
56 | content=content+'买入:\r\n'
57 | content = content + ','.join(redisService.smembers(self.buyKey))
58 | content=content+'\r\n'
59 | content=content+'卖出:\r\n'
60 | content = content + ','.join(redisService.smembers(self.sellKey))
61 | return content
62 |
63 | def clearRedis(self):
64 | redisService.delete(self.buyKey)
65 | redisService.delete(self.sellKey)
66 |
67 | def monitor(self):
68 | needEmail=self.strategy()
69 | if needEmail:
70 | emailContent=self.getEmailContent()
71 | self.clearRedis()
72 | #发送结果邮件
73 | EmailService.sendTextOrHtml('stock监控结果',emailContent,['yantaozhou@qq.com'])
74 | print('持股监控一次',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()),',需要邮件:',needEmail)
75 |
76 | monitorStrategy=MonitorStrategy()
77 |
78 | if __name__=="__main__" :
79 | #每5分钟跑一遍
80 | schedulerService.timming_exe(monitorStrategy.monitor,after=6,interval=5*60)
81 |
82 |
83 |
--------------------------------------------------------------------------------
/NewStockMonitor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | import sys
4 | import tushare as ts
5 | import time
6 | import datetime
7 | import Signal
8 | import Config
9 | import EmailService
10 | from SchedulerService import schedulerService
11 | from RedisService import redisService
12 | from StockService import stockService
13 |
14 | class NewStockMonitor():
15 | strategyName='newstock'
16 | buyKey='buy:newstock'
17 | sellKey='sell:newstock'
18 | rate=1
19 | def __init__(self):
20 | pass
21 | def strategy(self):
22 | needEmail=False
23 | #非交易时间,直接返回
24 | if not stockService.isTradeTime():
25 | return needEmail
26 | #获取持有的股票
27 | try:
28 | holdStocks=redisService.smembers(Config.KEY_NEW_STOCK)
29 | for stockNo in holdStocks:
30 | #获取当前股票的实时分笔
31 | df=ts.get_realtime_quotes(stockNo)
32 | column_open=df['open']
33 | open=float(column_open[0])
34 | column_price=df['price']
35 | price=float(column_price[0])
36 | percent=abs(open-price)/open
37 | #如果当前价格低于开盘价(打开涨停板),则需要给出卖出提示
38 | if price使用python的tushare库编写的股票相关的应用,存储使用redis,策略结果和监控结果
4 | 通过邮件进行发送,需邮件服务方提供相应的smtp服务器地址及密码(非登录密码,腾讯的为imop授权码) 使用前请先修改Config.py和EmailService中的相关的配置,各个文件作用如下:
5 | ## Config.py
6 | >配置文件,控制redis访问配置,及各个Redis中的set的key(如:持有的股票,新股开板监控股票)
7 | ## DateTimeUtils.py
8 | >日期时间操作工具类(如:判断是否属于交易时间)
9 | ## EmailService.py
10 | >用于发送邮件(请修改相应配置,写的匆忙,没有提取到config中)
11 | ## FundHoldStrategy.py
12 | >基金持股选股策略
13 | ## HoldStocks.txt
14 | >持有的股票列表,用于持股多时,初始化redis中的持股set
15 | ## Ma20Strategy.py
16 | >20日均线策略,遍历市场所有股票,挑选出突破20日均线的为买入股票,否则且为持有股票则为卖出股票
17 | ## MacdStrategy.py
18 | >MACD策略
19 | ## Main.py
20 | >多策略组合入口
21 | ## MonitorStrategy.py
22 | >监控策略,监控持有股票,如果涨幅或跌幅达到5%,则发出邮件提示
23 | ## NewStockMonitor.py
24 | >监控持有的新股,如果当前价格低于涨停价(破板)则给出提示
25 | ## RedisService.py
26 | >redis操作service
27 | ## SchedulerService.py
28 | >python 计划调度工具类
29 | ## seleniumTest.py
30 | >python中selenium使用的例子
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/RedisService.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | #coding:utf8
3 | import redis
4 | import Config
5 |
6 | class RedisService():
7 | __POOL=None
8 | __DB=None
9 | def __init__(self):
10 | if self.__DB is None:
11 | self.__POOL=redis.ConnectionPool(host=Config.REDIS_HOST,port=Config.REDIS_PORT,db=0,password=Config.REDIS_PASSWD)
12 | self.__DB=redis.StrictRedis(connection_pool=self.__POOL)
13 |
14 | #获取字符串
15 | def to_str(self,bytes_or_str):
16 | if isinstance(bytes_or_str,bytes):
17 | value=bytes_or_str.decode(Config.CHAR_SET)
18 | else:
19 | value=bytes_or_str
20 | return value
21 | #获取bytes
22 | def to_bytes(self,bytes_or_str):
23 | if isinstance(bytes_or_str,str):
24 | value=bytes_or_str.encode(Config.CHAR_SET)
25 | else:
26 | value=bytes_or_str
27 | return bytes_or_str
28 | #
29 | ############
30 | #key 操作
31 | ############
32 | #
33 | #
34 | #是否存在name这个key True
35 | def exists(self,key):
36 | return self.__DB.exists(key)
37 | #删除name这个key 1
38 | def delete(self,key):
39 | return self.__DB.delete(key)
40 | #判断name这个key类型 str
41 | def type(self,key):
42 | return self.to_str(self.__DB.type(key))
43 | #获取所有以n开头的key ['name']
44 | def keys(self,key):
45 | byteKeys=self.__DB.keys(key)
46 | keys=[]
47 | for bKey in byteKeys :
48 | keys.append(self.to_str(bKey))
49 | return keys
50 | #获取随机的一个key 'name'
51 | def randomkey(self):
52 | return self.to_str(self.__DB.randomkey())
53 | #将name重命名为nickname
54 | def rename(self,name,nickname):
55 | return self.__DB.rename(name,nickname)
56 | #获取当前数据库中key的数目 100
57 | def dbsize(self):
58 | return self.__DB.dbsize()
59 | #将name这key的过期时间设置2秒 True
60 | def expire(self,key,time):
61 | return self.__DB.expire(key,time)
62 | #获取name这key的过期时间 -1
63 | def ttl(self,key):
64 | return self.__DB.ttl(key)
65 | #将name移动到2号数据库 True
66 | def move(self,key,no):
67 | return self.__DB.move(key,no)
68 | #删除当前选择数据库中的所有key True
69 | def flushdb(self):
70 | return self.__DB.flushdb()
71 | #删除所有数据库中的所有key True
72 | def flushall(self):
73 | return self.__DB.flushall()
74 | #
75 | ############
76 | #String 操作
77 | ############
78 | #
79 | #
80 | #给name这个key的value赋值为Bob True
81 | def set(self,key,value):
82 | return self.__DB.set(key,value)
83 | #将name移动到2号数据库 True
84 | def get(self,key):
85 | return self.to_str(self.__DB.get(key))
86 | #赋值name为Mike并得到上次的value 'Bob'
87 | def getset(self,key,value):
88 | return self.to_str(self.__DB.getset(key,value))
89 | #返回name和nickname的value ['Mike','Miker']
90 | def mget(self,*keys):
91 | bValues=self.__DB.mget(keys)
92 | values=[]
93 | for bValue in bValues:
94 | values.append(self.to_str(bValue))
95 | return values
96 | #如果newname这key不存在则设置值为James 第一次运行True,第二次False
97 | def setnx(self,key,value):
98 | return self.__DB.setnx(key,value)
99 | #将name这key的值设为James,有效期1秒 True
100 | def setex(self,key,time,value):
101 | return self.__DB.setex(key,time,value)
102 | #设置name为Hello字符串,并在index为6的位置补World 11,修改后的字符串长度
103 | def setrange(self,key,index,insert):
104 | return self.__DB.setrange(key,index,insert)
105 | #将name1设为Durant,name2设为James True
106 | def mset(self,dict):
107 | return self.__DB.mset(dict)
108 | #在name3和name4均不存在的情况下才设置二者值 True
109 | def msetnx(self,dict):
110 | return self.__DB.msetnx(dict)
111 | #age对应的值增1,若不存在则会创建并设置为1 1,即修改后的值
112 | def incr(self,key,index):
113 | return self.__DB.incr(key,index)
114 | #age对应的值减1,若不存在则会创建并设置为-1 -1,即修改后的值
115 | def decr(self,key,index):
116 | return self.__DB.decr(key,index)
117 | #向key为nickname的值后追加OK,即修改后的字符串长度
118 | def append(self,key,insert):
119 | return self.__DB.append(key,insert)
120 | #4) 返回key为name的值的字符串,截取索引为1-4的字符 'ello'
121 | def substr(self,key,start,end):
122 | return self.to_str(self.__DB.substr(key,start,end))
123 | #返回key为name的值的字符串,截取索引为1-4的字符 'ello'
124 | def getrange(self,key,start,end):
125 | return self.to_str(self.__DB.getrange(key,start,end))
126 | #
127 | ############
128 | #List 操作
129 | ############
130 | #
131 | #
132 | #给list这个key的list尾添加元素 list 大小
133 | def rpush(self,key,*values):
134 | return self.__DB.rpush(key,*values)
135 | #给list这个key的list头添加 list 大小
136 | def lpush(self,key,value):
137 | return self.__DB.lpush(key,value)
138 | #返回key为list的列表的长度 4
139 | def llen(self,key):
140 | return self.__DB.llen(key)
141 | #返回起始为1终止为3的索引范围对应的list
142 | def lrange(self,key,start,end):
143 | bKeys=self.__DB.lrange(key,start,end)
144 | keys=[]
145 | for bKey in bKeys:
146 | keys.append(self.to_str(bKey))
147 | return keys
148 | #返回并删除名为list的list第一个元素
149 | def lpop(self,key):
150 | return self.__DB.lpop(key)
151 | #返回并删除名为list的list最后一个元素
152 | def rpop(self,key):
153 | return self.__DB.rpop(key)
154 | #
155 | ############
156 | #Set 操作
157 | ############
158 | #
159 | #
160 | #向key为name的set中添加元素 添加元素的个数
161 | def sadd(self,key,*values):
162 | return self.__DB.sadd(key,*values)
163 | #从key为name的set中删除元素
164 | def srem(self,key,*values):
165 | return self.__DB.srem(key,*values)
166 | #返回key为name的set的所有元素
167 | def smembers(self,key):
168 | bValues=self.__DB.smembers(key)
169 | values=[]
170 | for bValue in bValues:
171 | values.append(self.to_str(bValue))
172 | return values
173 | #
174 | ############
175 | #Hash 操作
176 | ############
177 | #
178 | #
179 | #向key为name的hash中添加映射 添加元素的个数
180 | def hset(self,key,field,value):
181 | return self.__DB.hset(key,field,value)
182 | #返回key为name的hash中field对应的value 添加元素的个数
183 | def hget(self,key,field):
184 | return self.to_str(self.__DB.hget(key,field))
185 | #key为namehash中是否存在键名为key的映射
186 | def hexists(self,key,field):
187 | return self.__DB.hexists(key,field)
188 | #key为namehash中删除键名为key的映射
189 | def hdel(self,key,*fields):
190 | return self.__DB.hdel(key,*fields)
191 | #从key为name的hash中获取映射个数
192 | def hlen(self,key):
193 | return self.__DB.hlen(key,*fields)
194 | #从key为name的hash中获取所有映射键名
195 | def hkeys(self,key):
196 | bFields=self.__DB.hkeys(key)
197 | fields=[]
198 | for bField in bFields:
199 | fields.append(self.to_str(bField))
200 | return fields
201 | #从key为name的hash中获取所有映射键值对
202 | def hgetall(self,key):
203 | return self.__DB.hgetall(key)
204 |
205 | redisService=RedisService()
--------------------------------------------------------------------------------
/SchedulerService.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 | #coding=utf-8
3 | import time
4 | import os
5 | import sched
6 |
7 | class SchedulerService():
8 | # 第一个参数确定任务的时间,返回从某个特定的时间到现在经历的秒数
9 | # 第二个参数以某种人为的方式衡量时间
10 | schedule = None
11 | def __init__(self):
12 | if self.schedule is None:
13 | self.schedule=sched.scheduler(time.time, time.sleep)
14 |
15 | def perform_command(self,func,after,interval):
16 | # 安排inc秒后再次运行自己,即周期运行
17 | self.schedule.enter(interval,0,self.perform_command,(func,after,interval))
18 | func()
19 |
20 | def timming_exe(self,func,after=60,interval=24*60*60):
21 | # enter用来安排某事件的发生时间,从现在起第n秒开始启动
22 | self.schedule.enter(after,0,self.perform_command,(func,after,interval))
23 | # 持续运行,直到计划时间队列变成空为止
24 | self.schedule.run()
25 |
26 | schedulerService=SchedulerService()
27 | if __name__=='__main__':
28 | def printTime():
29 | print(time.time())
30 | #每一秒打印一次时间
31 | schedulerService.timming_exe(printTime,1,1)
--------------------------------------------------------------------------------
/Signal.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding: UTF-8
3 | class Signal():
4 | stockNo=None
5 | buy=None
6 | sell=None
7 | dateStr=None
8 | def __init__(self,stockNo=None,buy=None,sell=None,dateStr=None):
9 | self.stockNo=stockNo
10 | self.buy=buy
11 | self.sell=sell
12 | self.dateStr=dateStr
--------------------------------------------------------------------------------
/SmsService.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | from twilio.rest import Client
3 | #认证信息在 twilio 账户里可以找到
4 | def sendMessage(phone,content):
5 | if phone is not None and content is not None :
6 | account_sid="ACe4abe0a807da292b15e725ef559a7d24"
7 | auth_token="1ea0c9fea15531ddd3290f8d8c3a3ec7"
8 | twilio_phone="+12523022221"
9 | client=Client(account_sid,auth_token)
10 | message=client.api.account.messages.create(to="+86"+phone,from_=twilio_phone,body=content)
11 | if message is not None :
12 | print("短信发送编号:",message.sid,"发送内容:",content)
13 |
14 |
15 | if __name__=="__main__" :
16 | sendMessage('123456','人生苦短,我用python!')
--------------------------------------------------------------------------------
/StockService.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # coding: UTF-8
3 | import os
4 | import time
5 | import tushare as ts
6 | import Config
7 | from RedisService import redisService
8 | class StockService():
9 | def __init__(self):
10 | pass
11 | def getStockDicts():
12 | # code,代码
13 | # name,名称
14 | # industry,所属行业
15 | # area,地区
16 | # pe,市盈率
17 | # outstanding,流通股本(亿)
18 | # totals,总股本(亿)
19 | # totalAssets,总资产(万)
20 | # liquidAssets,流动资产
21 | # fixedAssets,固定资产
22 | # reserved,公积金
23 | # reservedPerShare,每股公积金
24 | # esp,每股收益
25 | # bvps,每股净资
26 | # pb,市净率
27 | # timeToMarket,上市日期
28 | # undp,未分利润
29 | # perundp, 每股未分配
30 | # rev,收入同比(%)
31 | # profit,利润同比(%)
32 | # gpr,毛利率(%)
33 | # npr,净利润率(%)
34 | # holders,股东人数
35 | stocks=[]
36 | df=ts.get_stock_basics()
37 | column_code=df[u'code']
38 | column_name=df[u'name']
39 | column_industry=df[u'industry']
40 | column_area=df[u'area']
41 | column_pe=df[u'pe']
42 | column_outstanding=df[u'outstanding']
43 | column_totals=df[u'totals']
44 | column_totalAssets=df[u'totalAssets']
45 | return stocks
46 | def getStockCodes(self):
47 | df=ts.get_stock_basics()
48 | return df.index
49 |
50 | def isTradeTime(self):
51 | struct_time=time.localtime()
52 | tm_wday=struct_time[6]
53 | tm_hour=struct_time[3]
54 | tm_min=struct_time[4]
55 | #周一到周五
56 | if 0<=tm_wday and tm_wday<=4 :
57 | if 9=13 and tm_hour<=15 :
60 | return True
61 | return False
62 |
63 | def initHoldStocksToRedis(self):
64 | filePath=os.path.join(os.getcwd(),Config.HOLD_STOCKS_FILE_NAME)
65 | with open(filePath) as somefile:
66 | lines=somefile.readlines()
67 | for line in lines :
68 | stockNo=line.strip()
69 | if len(stockNo)>0:
70 | try:
71 | redisService.sadd(Config.KEY_HOLD_STOCK,stockNo)
72 | except:
73 | pass
74 |
75 | stockService=StockService()
76 | if __name__=='__main__':
77 | stockService.initHoldStocksToRedis()
78 | print(redisService.smembers(Config.KEY_HOLD_STOCK))
79 |
--------------------------------------------------------------------------------
/TestModule.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | #coding:utf8
3 | from RedisService import redisService
4 | def testRedis():
5 | print('str')
6 | redisService.set('string','str')
7 | print(redisService.keys('s*'))
8 | print(redisService.get('string'))
9 | print('list')
10 | redisService.rpush('list',1)
11 | redisService.rpush('list',2)
12 | redisService.rpush('list',3)
13 | redisService.rpush('list',3)
14 | print(redisService.lrange('list',0,-1))
15 | print('set')
16 | redisService.sadd('set',1)
17 | redisService.sadd('set',2)
18 | redisService.sadd('set',3)
19 | redisService.sadd('set',3)
20 | print(redisService.smembers('set'))
21 |
22 | if __name__=='__main__':
23 | #testRedis()
--------------------------------------------------------------------------------
/nohup.out:
--------------------------------------------------------------------------------
1 | 监控一次 2017-12-28 16:24:00
2 | 监控一次 2017-12-28 16:29:01
3 | 监控一次 2017-12-28 16:34:01
4 | 监控一次 2017-12-28 16:39:01
5 | 监控一次 2017-12-28 16:44:01
6 | 监控一次 2017-12-28 16:49:01
7 | 监控一次 2017-12-28 16:54:02
8 | 监控一次 2017-12-28 16:59:01
9 | 监控一次 2017-12-28 17:04:01
10 | 监控一次 2017-12-28 17:09:01
11 | 监控一次 2017-12-28 17:14:01
12 | 监控一次 2017-12-28 17:19:01
13 | 交易时间,不执行非实时策略
14 | 监控一次 2017-12-28 17:24:01
15 | 监控一次 2017-12-28 17:29:01
16 | 监控一次 2017-12-28 17:34:02
17 | 监控一次 2017-12-28 17:39:02
18 | 监控一次 2017-12-28 17:44:03
19 | 监控一次 2017-12-28 17:49:02
20 | 监控一次 2017-12-28 17:54:02
21 | 监控一次 2017-12-28 17:59:02
22 | 监控一次 2017-12-28 18:04:02
23 | 监控一次 2017-12-28 18:09:03
24 | 监控一次 2017-12-28 18:14:02
25 | 监控一次 2017-12-28 18:19:02
26 | 监控一次 2017-12-28 18:24:02
27 | 监控一次 2017-12-28 18:29:02
28 | 监控一次 2017-12-28 18:34:02
29 | 监控一次 2017-12-28 18:39:02
30 | 监控一次 2017-12-28 18:44:03
31 | 监控一次 2017-12-28 18:49:03
32 | 监控一次 2017-12-28 18:54:03
33 | 监控一次 2017-12-28 18:59:03
34 | 监控一次 2017-12-28 19:04:02
35 | 监控一次 2017-12-28 19:09:03
36 | 监控一次 2017-12-28 19:14:03
37 | 监控一次 2017-12-28 19:19:03
38 | 监控一次 2017-12-28 19:24:03
39 | 监控一次 2017-12-28 19:29:03
40 | 监控一次 2017-12-28 19:34:03
41 | 监控一次 2017-12-28 19:39:04
42 | 监控一次 2017-12-28 19:44:04
43 | 监控一次 2017-12-28 19:49:04
44 | 监控一次 2017-12-28 19:54:04
45 | 监控一次 2017-12-28 19:59:04
46 | 监控一次 2017-12-28 20:04:03
47 | 监控一次 2017-12-28 20:09:04
48 | 监控一次 2017-12-28 20:14:04
49 | 监控一次 2017-12-28 20:19:04
50 | 监控一次 2017-12-28 20:24:04
51 | 监控一次 2017-12-28 20:29:05
52 | 监控一次 2017-12-28 20:34:06
53 | 监控一次 2017-12-28 20:39:04
54 | 监控一次 2017-12-28 20:44:05
55 | 监控一次 2017-12-28 20:49:05
56 | 监控一次 2017-12-28 20:54:05
57 | 监控一次 2017-12-28 20:59:05
58 | 监控一次 2017-12-28 21:04:05
59 | 监控一次 2017-12-28 21:09:05
60 | 监控一次 2017-12-28 21:14:05
61 | 监控一次 2017-12-28 21:19:05
62 | 监控一次 2017-12-28 21:24:05
63 | 监控一次 2017-12-28 21:29:05
64 | 监控一次 2017-12-28 21:34:05
65 | 监控一次 2017-12-28 21:39:07
66 | 监控一次 2017-12-28 21:44:06
67 | 监控一次 2017-12-28 21:49:06
68 | 监控一次 2017-12-28 21:54:06
69 | 监控一次 2017-12-28 21:59:06
70 | 监控一次 2017-12-28 22:04:06
71 | 监控一次 2017-12-28 22:09:06
72 | 监控一次 2017-12-28 22:14:06
73 | 监控一次 2017-12-28 22:19:06
74 | 监控一次 2017-12-28 22:24:07
75 | 监控一次 2017-12-28 22:29:06
76 | 监控一次 2017-12-28 22:34:06
77 | 监控一次 2017-12-28 22:39:08
78 | 监控一次 2017-12-28 22:44:06
79 | 监控一次 2017-12-28 22:49:06
80 | 监控一次 2017-12-28 22:54:07
81 | 监控一次 2017-12-28 22:59:09
82 | 监控一次 2017-12-28 23:04:07
83 | 监控一次 2017-12-28 23:09:09
84 | 监控一次 2017-12-28 23:14:07
85 | 监控一次 2017-12-28 23:19:07
86 | 监控一次 2017-12-28 23:24:07
87 | 监控一次 2017-12-28 23:29:07
88 | 监控一次 2017-12-28 23:34:07
89 | 监控一次 2017-12-28 23:39:07
90 | 监控一次 2017-12-28 23:44:07
91 | 监控一次 2017-12-28 23:49:08
92 | 监控一次 2017-12-28 23:54:09
93 | 监控一次 2017-12-28 23:59:08
94 | 监控一次 2017-12-29 00:04:06
95 | 监控一次 2017-12-29 00:09:06
96 | 监控一次 2017-12-29 00:14:06
97 | 监控一次 2017-12-29 00:19:06
98 | 监控一次 2017-12-29 00:24:06
99 | 监控一次 2017-12-29 00:29:06
100 | 监控一次 2017-12-29 00:34:06
101 | 监控一次 2017-12-29 00:39:06
102 | 监控一次 2017-12-29 00:44:06
103 | 监控一次 2017-12-29 00:49:06
104 | 监控一次 2017-12-29 00:54:06
105 | 监控一次 2017-12-29 00:59:07
106 | 监控一次 2017-12-29 01:04:07
107 | 监控一次 2017-12-29 01:09:07
108 | 监控一次 2017-12-29 01:14:07
109 | 监控一次 2017-12-29 01:19:07
110 | 监控一次 2017-12-29 01:24:07
111 | 监控一次 2017-12-29 01:29:07
112 | 监控一次 2017-12-29 01:34:07
113 | 监控一次 2017-12-29 01:39:07
114 | 监控一次 2017-12-29 01:44:07
115 | 监控一次 2017-12-29 01:49:07
116 | 监控一次 2017-12-29 01:54:07
117 | 监控一次 2017-12-29 01:59:08
118 | 监控一次 2017-12-29 02:04:08
119 | 监控一次 2017-12-29 02:09:08
120 | 监控一次 2017-12-29 02:14:08
121 | 监控一次 2017-12-29 02:19:08
122 | 监控一次 2017-12-29 02:24:08
123 | 监控一次 2017-12-29 02:29:08
124 | 监控一次 2017-12-29 02:34:08
125 | 监控一次 2017-12-29 02:39:08
126 | 监控一次 2017-12-29 02:44:08
127 | 监控一次 2017-12-29 02:49:08
128 | 监控一次 2017-12-29 02:54:08
129 | 监控一次 2017-12-29 02:59:09
130 | 监控一次 2017-12-29 03:04:09
131 | 监控一次 2017-12-29 03:09:09
132 | 监控一次 2017-12-29 03:14:09
133 | 监控一次 2017-12-29 03:19:09
134 | 监控一次 2017-12-29 03:24:09
135 | 监控一次 2017-12-29 03:29:09
136 | 监控一次 2017-12-29 03:34:09
137 | 监控一次 2017-12-29 03:39:09
138 | 监控一次 2017-12-29 03:44:09
139 | 监控一次 2017-12-29 03:49:09
140 | 监控一次 2017-12-29 03:54:09
141 | 监控一次 2017-12-29 03:59:10
142 | 监控一次 2017-12-29 04:04:10
143 | 监控一次 2017-12-29 04:09:10
144 | 监控一次 2017-12-29 04:14:10
145 | 监控一次 2017-12-29 04:19:10
146 | 监控一次 2017-12-29 04:24:10
147 | 监控一次 2017-12-29 04:29:10
148 | 监控一次 2017-12-29 04:34:10
149 | 监控一次 2017-12-29 04:39:10
150 | 监控一次 2017-12-29 04:44:10
151 | 监控一次 2017-12-29 04:49:10
152 | 监控一次 2017-12-29 04:54:10
153 | 监控一次 2017-12-29 04:59:10
154 | 监控一次 2017-12-29 05:04:11
155 | 监控一次 2017-12-29 05:09:11
156 | 监控一次 2017-12-29 05:14:11
157 | 监控一次 2017-12-29 05:19:11
158 | 监控一次 2017-12-29 05:24:11
159 | 监控一次 2017-12-29 05:29:11
160 | 监控一次 2017-12-29 05:34:11
161 | 监控一次 2017-12-29 05:39:11
162 | 监控一次 2017-12-29 05:44:11
163 | 监控一次 2017-12-29 05:49:11
164 | 监控一次 2017-12-29 05:54:11
165 | 监控一次 2017-12-29 05:59:12
166 | 监控一次 2017-12-29 06:04:12
167 | 监控一次 2017-12-29 06:09:12
168 | 监控一次 2017-12-29 06:14:12
169 | 监控一次 2017-12-29 06:19:12
170 | 监控一次 2017-12-29 06:24:12
171 | 监控一次 2017-12-29 06:29:12
172 | 监控一次 2017-12-29 06:34:12
173 | 监控一次 2017-12-29 06:39:12
174 | 监控一次 2017-12-29 06:44:12
175 | 监控一次 2017-12-29 06:49:12
176 | 监控一次 2017-12-29 06:54:12
177 | 监控一次 2017-12-29 06:59:12
178 | 监控一次 2017-12-29 07:04:13
179 | 监控一次 2017-12-29 07:09:13
180 | 监控一次 2017-12-29 07:14:13
181 | 监控一次 2017-12-29 07:19:13
182 | 监控一次 2017-12-29 07:24:13
183 | 监控一次 2017-12-29 07:29:13
184 | 监控一次 2017-12-29 07:34:13
185 | 监控一次 2017-12-29 07:39:13
186 | 监控一次 2017-12-29 07:44:13
187 | 监控一次 2017-12-29 07:49:13
188 | 监控一次 2017-12-29 07:54:13
189 | 监控一次 2017-12-29 07:59:13
190 | 监控一次 2017-12-29 08:04:13
191 | 监控一次 2017-12-29 08:09:13
192 | 监控一次 2017-12-29 08:14:14
193 | 监控一次 2017-12-29 08:19:14
194 | 监控一次 2017-12-29 08:24:14
195 | 监控一次 2017-12-29 08:29:14
196 | 监控一次 2017-12-29 08:34:14
197 | 监控一次 2017-12-29 08:39:14
198 | 监控一次 2017-12-29 08:44:14
199 | 监控一次 2017-12-29 08:49:14
200 | 监控一次 2017-12-29 08:54:14
201 | 监控一次 2017-12-29 08:59:14
202 | 监控一次 2017-12-29 09:04:14
203 | 监控一次 2017-12-29 09:09:14
204 | 监控一次 2017-12-29 09:14:14
205 | 监控一次 2017-12-29 09:19:15
206 | 监控一次 2017-12-29 09:24:15
207 | 监控一次 2017-12-29 09:29:15
208 | 监控一次 2017-12-29 09:34:17
209 | 监控一次 2017-12-29 09:39:16
210 | 监控一次 2017-12-29 09:44:17
211 | 监控一次 2017-12-29 09:49:17
212 | 监控一次 2017-12-29 09:54:17
213 | 监控一次 2017-12-29 09:59:17
214 | 监控一次 2017-12-29 10:04:17
215 | 监控一次 2017-12-29 10:09:17
216 | 监控一次 2017-12-29 10:14:17
217 | 监控一次 2017-12-29 10:19:17
218 | 监控一次 2017-12-29 10:24:18
219 | 监控一次 2017-12-29 10:29:18
220 | 监控一次 2017-12-29 10:34:18
221 | 监控一次 2017-12-29 10:39:18
222 | 监控一次 2017-12-29 10:44:18
223 | 监控一次 2017-12-29 10:49:18
224 | 监控一次 2017-12-29 10:54:18
225 | 监控一次 2017-12-29 10:59:18
226 | 监控一次 2017-12-29 11:04:18
227 | 监控一次 2017-12-29 11:09:18
228 | 监控一次 2017-12-29 11:14:18
229 | 监控一次 2017-12-29 11:19:18
230 | 监控一次 2017-12-29 11:24:18
231 | 监控一次 2017-12-29 11:29:18
232 | 监控一次 2017-12-29 11:34:18
233 | 监控一次 2017-12-29 11:39:19
234 | 监控一次 2017-12-29 11:44:19
235 | 监控一次 2017-12-29 11:49:20
236 | 监控一次 2017-12-29 11:54:20
237 | 监控一次 2017-12-29 11:59:19
238 | 监控一次 2017-12-29 12:04:19
239 | 监控一次 2017-12-29 12:09:19
240 | 监控一次 2017-12-29 12:14:19
241 | 监控一次 2017-12-29 12:19:19
242 | 监控一次 2017-12-29 12:24:19
243 | 监控一次 2017-12-29 12:29:19
244 | 监控一次 2017-12-29 12:34:20
245 | 监控一次 2017-12-29 12:39:20
246 | 监控一次 2017-12-29 12:44:20
247 | 监控一次 2017-12-29 12:49:20
248 | 监控一次 2017-12-29 12:54:20
249 | 监控一次
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | redis
--------------------------------------------------------------------------------
/seleniumTest.py:
--------------------------------------------------------------------------------
1 | # __*__ code:utf-8 __*__
2 |
3 | from selenium import webdriver
4 | from selenium.webdriver.common.keys import Keys
5 | from selenium.webdriver.common.action_chains import ActionChains
6 |
7 | browser=webdriver.Chrome()
8 | browser.get('https://www.baidu.com')
9 | input=browser.find_element_by_css_selector('#kw')
10 | input.send_keys('selenium')
11 | submit=browser.find_element_by_css_selector('#su')
12 | actions=ActionChains(browser)
13 | actions.move_to_element(submit)
14 | actions.click(submit)
15 | actions.perform()
16 | #browser.quit()
17 |
18 |
--------------------------------------------------------------------------------
/stockMonitor.bat:
--------------------------------------------------------------------------------
1 | @echo on
2 | set filePath=E:\python\email\MonitorStrategy.py
3 | call python %filePath%
4 | cmd.exe
--------------------------------------------------------------------------------