├── 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 --------------------------------------------------------------------------------