diff --git a/README.md b/README.md index 3e321dd7..4ad62fd8 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,14 @@ github 链接: https://github.com/msincenselee/vnpy gitee 链接: https://gitee.com/vnpy2/vnpy ###Fork版本主要改进如下 + +18、CTA股票选股引擎 + + -vnpy.app.stock_screener + + 无界面选股引擎 + -prod.screener_d1 + + 每日执行一次选股所有选股策略脚本 + 17、东财股票接入 - vnpy.api.eastmoney_api, @@ -153,10 +161,15 @@ gitee 链接: https://gitee.com/vnpy2/vnpy 大佳 QQ/Wechat:28888502 -2021 股票CTA实战课程:http://www.uquant.org/course/49 -2021 期货缠论高级课程:http://www.uquant.org/course/48 -2021 数字货币CTA课程:http://www.uquant.org/course/46 -2020 期货套利课程:http://www.uquant.org/course/43 +系列在线课程 + + 2021 股票CTA实战课程:http://www.uquant.org/course/49 + + 2021 期货缠论高级课程:http://www.uquant.org/course/48 + + 2021 数字货币CTA课程:http://www.uquant.org/course/46 + + 2020 期货套利课程:http://www.uquant.org/course/43 -------------------------------------------------------------------------------------------- # 原版 vn.py - 基于python的开源交易平台开发框架 diff --git a/prod/jobs/daily_download_stock_adjust_factors.sh b/prod/jobs/daily_download_stock_adjust_factors.sh new file mode 100644 index 00000000..1404eea2 --- /dev/null +++ b/prod/jobs/daily_download_stock_adjust_factors.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +CONDA_HOME=~/anaconda3 +#$CONDA_HOME/bin/conda deactivate +#$CONDA_HOME/bin/conda activate py37 + +############ Added by Huang Jianwei at 2018-04-03 +# To solve the problem about Javascript runtime +export PATH=$PATH:/usr/local/bin +############ Ended + +BASE_PATH=$(cd `dirname $0`; pwd) +echo $BASE_PATH +cd `dirname $0` +PROGRAM_NAME=../../vnpy/data/stock/adjust_factor.py + +# 全量下载baostock得复权因子 +$CONDA_HOME/envs/py37/bin/python $PROGRAM_NAME + + + diff --git a/prod/jobs/daily_stock_adjust_grids.py b/prod/jobs/daily_stock_adjust_grids.py index 14a96ca7..41cd40a3 100644 --- a/prod/jobs/daily_stock_adjust_grids.py +++ b/prod/jobs/daily_stock_adjust_grids.py @@ -29,7 +29,7 @@ if VNPY_ROOT not in sys.path: os.environ["VNPY_TESTING"] = "1" from vnpy.trader.utility import load_json, save_json, append_data -from vnpy.data.stock.adjust_factor import get_adjust_factor, get_stock_base +from vnpy.data.stock.adjust_factor import download_adjust_factor, get_adjust_factor, get_stock_base from vnpy.trader.util_wechat import send_wx_msg if __name__ == "__main__": @@ -38,6 +38,9 @@ if __name__ == "__main__": print(u'请输入:{}目录下的子目录作为参数1'.format(os.path.abspath(os.path.join(VNPY_ROOT, 'prod')))) exit() + print('下载更新所有复权因子') + download_adjust_factor() + # 进行报告的账号目录 account_folder = sys.argv[1] account_folder = os.path.abspath(os.path.join(VNPY_ROOT, 'prod', account_folder)) diff --git a/prod/screener_d1/readme.md b/prod/screener_d1/readme.md new file mode 100644 index 00000000..0ccb155f --- /dev/null +++ b/prod/screener_d1/readme.md @@ -0,0 +1,5 @@ +日线选股 + +配置选股策略文件 screenr_setting.json + +定时任务运行 run_screener.py diff --git a/prod/screener_d1/run_screener.py b/prod/screener_d1/run_screener.py new file mode 100644 index 00000000..209cfc51 --- /dev/null +++ b/prod/screener_d1/run_screener.py @@ -0,0 +1,115 @@ +# flake8: noqa + +import os +import sys +import multiprocessing +from time import sleep +from datetime import datetime, time +from logging import DEBUG + +# 将repostory的目录i,作为根目录,添加到系统环境中。 +ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +if ROOT_PATH not in sys.path: + sys.path.append(ROOT_PATH) + print(f'append {ROOT_PATH} into sys.path') + +from vnpy.event import EventEngine, EVENT_TIMER +from vnpy.trader.setting import SETTINGS +from vnpy.trader.engine import MainEngine +from vnpy.trader.utility import load_json +# from vnpy.gateway.gj import GjGateway +from vnpy.app.stock_screener import ScreenerApp +# from vnpy.app.cta_stock import CtaStockApp +# from vnpy.app.cta_crypto.base import EVENT_CTA_LOG +from vnpy.app.rpc_service import RpcServiceApp +# from vnpy.app.algo_broker import AlgoBrokerApp +from vnpy.app.account_recorder import AccountRecorderApp +from vnpy.trader.util_pid import update_pid + +# from vnpy.trader.util_monitor import OrderMonitor, TradeMonitor, PositionMonitor, AccountMonitor, LogMonitor + +SETTINGS["log.active"] = True +SETTINGS["log.level"] = DEBUG +SETTINGS["log.console"] = True +SETTINGS["log.file"] = True + +screener_name = '日线选股' + +import types +import traceback + + +def excepthook(exctype: type, value: Exception, tb: types.TracebackType) -> None: + """ + Raise exception under debug mode + """ + sys.__excepthook__(exctype, value, tb) + + msg = "".join(traceback.format_exception(exctype, value, tb)) + + print(msg, file=sys.stderr) + + +class DaemonService(object): + + def __init__(self): + self.event_engine = EventEngine() + self.g_count = 0 + self.last_dt = datetime.now() + + # 创建主引擎 + self.main_engine = MainEngine(self.event_engine) + + self.save_data_time = None + self.save_snapshot_time = None + + # 注册定时器,用于判断重连 + self.event_engine.register(EVENT_TIMER, self.on_timer) + + def on_timer(self, event): + """定时器执行逻辑,每十秒执行一次""" + + # 60秒才执行一次检查 + self.g_count += 1 + if self.g_count <= 60: + return + + self.g_count = 0 + dt = datetime.now() + + # if dt.hour != self.last_dt.hour: + self.last_dt = dt + print(u'run_screener.py checkpoint:{0}'.format(dt)) + self.main_engine.write_log(u'run_screener.py checkpoint:{0}'.format(dt)) + if self.main_engine.get_all_completed_status(): + from vnpy.trader.util_wechat import send_wx_msg + msg = f'{screener_name}完成所有选股任务' + send_wx_msg(content=msg) + self.main_engine.write_log(msg) + sleep(10) + os._exit(0) + + def start(self): + """ + Running in the child process. + """ + SETTINGS["log.file"] = True + + # 添加选股引擎 + screen_engine = self.main_engine.add_app(ScreenerApp) + screen_engine.init_engine() + + self.main_engine.write_log("主引擎创建成功") + + while True: + sleep(1) + + + +if __name__ == "__main__": + # from vnpy.trader.ui import create_qapp + # qApp = create_qapp() + # sys.excepthook = excepthook + + s = DaemonService() + s.start() diff --git a/prod/screener_d1/screener_setting.json b/prod/screener_d1/screener_setting.json new file mode 100644 index 00000000..e072e344 --- /dev/null +++ b/prod/screener_d1/screener_setting.json @@ -0,0 +1,14 @@ +{ + "stock_screener_ThreeBuy_D1": { + "class_name": "StrategyChanlunThreeBuy", + "auto_init": true, + "auto_start": true, + "setting": { + "vt_symbols": [ + "600410.SSE" + ], + "all_stocks": false, + "bar_name": "D1" + } + } +} \ No newline at end of file diff --git a/vnpy/app/cta_stock/engine.py b/vnpy/app/cta_stock/engine.py index 2d3b0ab1..7d24e9e9 100644 --- a/vnpy/app/cta_stock/engine.py +++ b/vnpy/app/cta_stock/engine.py @@ -1274,38 +1274,47 @@ class CtaEngine(BaseEngine): """ Add a new strategy. """ - if strategy_name in self.strategies: - msg = f"创建策略失败,存在重名{strategy_name}" - self.write_log(msg=msg, - level=logging.CRITICAL) - return False, msg + try: + if strategy_name in self.strategies: + msg = f"创建策略失败,存在重名{strategy_name}" + self.write_log(msg=msg, + level=logging.CRITICAL) + return False, msg - strategy_class = self.classes.get(class_name, None) - if not strategy_class: - msg = f"创建策略失败,找不到策略类{class_name}" - self.write_log(msg=msg, - level=logging.CRITICAL) - return False, msg + strategy_class = self.classes.get(class_name, None) + if not strategy_class: + msg = f"创建策略失败,找不到策略类{class_name}" + self.write_log(msg=msg, + level=logging.CRITICAL) + return False, msg - self.write_log(f'开始添加策略类{class_name},实例名:{strategy_name}') - strategy = strategy_class(self, strategy_name, vt_symbols, setting) - self.strategies[strategy_name] = strategy + self.write_log(f'开始添加策略类{class_name},实例名:{strategy_name}') + strategy = strategy_class(self, strategy_name, vt_symbols, setting) + self.strategies[strategy_name] = strategy - # Add vt_symbol to strategy map. - subscribe_symbol_set = self.strategy_symbol_map[strategy_name] - for vt_symbol in vt_symbols: - strategies = self.symbol_strategy_map[vt_symbol] - strategies.append(strategy) - subscribe_symbol_set.add(vt_symbol) + # Add vt_symbol to strategy map. + subscribe_symbol_set = self.strategy_symbol_map[strategy_name] + for vt_symbol in vt_symbols: + strategies = self.symbol_strategy_map[vt_symbol] + strategies.append(strategy) + subscribe_symbol_set.add(vt_symbol) - # Update to setting file. - self.update_strategy_setting(strategy_name, setting, auto_init, auto_start) + # Update to setting file. + self.update_strategy_setting(strategy_name, setting, auto_init, auto_start) - self.put_strategy_event(strategy) + self.put_strategy_event(strategy) - # 判断设置中是否由自动初始化和自动启动项目 - if auto_init: - self.init_strategy(strategy_name, auto_start=auto_start) + # 判断设置中是否由自动初始化和自动启动项目 + if auto_init: + self.init_strategy(strategy_name, auto_start=auto_start) + + except Exception as ex: + msg = f'添加策略实例{strategy_name}失败,{str(ex)}' + self.write_error(msg) + self.write_error(traceback.format_exc()) + self.send_wechat(msg) + + return False, f'添加策略实例{strategy_name}失败' return True, f'成功添加{strategy_name}' @@ -1497,15 +1506,19 @@ class CtaEngine(BaseEngine): if module_name: new_class_name = module_name + '.' + class_name self.write_log(u'转换策略为全路径:{}'.format(new_class_name)) - + old_strategy_class = self.classes[class_name] + self.write_log(f'旧策略ID:{id(old_strategy_class)}') strategy_class = import_module_by_str(new_class_name) if strategy_class is None: - err_msg = u'加载策略模块失败:{}'.format(class_name) + err_msg = u'加载策略模块失败:{}'.format(new_class_name) self.write_error(err_msg) return False, err_msg self.write_log(f'重新加载模块成功,使用新模块:{new_class_name}') + self.write_log(f'新策略ID:{id(strategy_class)}') self.classes[class_name] = strategy_class + else: + self.write_log(f'没有{class_name}的module_name,无法重新加载模块') # 停止当前策略实例的运行,撤单 self.stop_strategy(strategy_name) diff --git a/vnpy/app/cta_stock/template.py b/vnpy/app/cta_stock/template.py index 7414b3e1..34ded569 100644 --- a/vnpy/app/cta_stock/template.py +++ b/vnpy/app/cta_stock/template.py @@ -1,4 +1,6 @@ -"""""" +# 股票模板 +# 华富资产 @ 李来佳 + import os import sys import uuid @@ -38,7 +40,7 @@ class CtaTemplate(ABC): self, cta_engine: Any, strategy_name: str, - vt_symbols: List[str], + vt_symbols: str, setting: dict, ): """""" @@ -204,6 +206,9 @@ class CtaTemplate(ABC): if self.is_upper_limit(vt_symbol): self.write_error(u'涨停价不做FAK/FOK委托') return [] + if volume == 0: + self.write_error(f'委托数量有误,必须大于0,{vt_symbol}, price:{price}') + return [] return self.send_order(vt_symbol=vt_symbol, direction=Direction.LONG, offset=Offset.OPEN, @@ -224,6 +229,9 @@ class CtaTemplate(ABC): if self.is_lower_limit(vt_symbol): self.write_error(u'跌停价不做FAK/FOK sell委托') return [] + if volume == 0: + self.write_error(f'委托数量有误,必须大于0,{vt_symbol}, price:{price}') + return [] return self.send_order(vt_symbol=vt_symbol, direction=Direction.SHORT, offset=Offset.CLOSE, @@ -253,6 +261,7 @@ class CtaTemplate(ABC): return [] if not self.trading: + self.write_log(f'非交易状态') return [] vt_orderids = self.cta_engine.send_order( diff --git a/vnpy/data/stock/adjust_factor.py b/vnpy/data/stock/adjust_factor.py index c3d3fa6c..a77e2695 100644 --- a/vnpy/data/stock/adjust_factor.py +++ b/vnpy/data/stock/adjust_factor.py @@ -132,7 +132,7 @@ def download_adjust_factor(): if __name__ == '__main__': # 下载所有复权数据 - # download_adjust_factor() + download_adjust_factor() # 下载某个股票的复权数据 # f = get_adjust_factor(vt_symbol='000651.SZSE',stock_name='格力电器',need_login=True) diff --git a/vnpy/data/tdx/tdx_future_data.py b/vnpy/data/tdx/tdx_future_data.py index 29ade4b7..e692d10e 100644 --- a/vnpy/data/tdx/tdx_future_data.py +++ b/vnpy/data/tdx/tdx_future_data.py @@ -70,7 +70,8 @@ INIT_TDX_MARKET_MAP = { 'CJL9': 28, 'CYL9': 28, 'FGL9': 28, 'JRL9': 28, 'LRL9': 28, 'MAL9': 28, 'OIL9': 28, 'PML9': 28, 'RIL9': 28, 'RML9': 28, 'RSL9': 28, 'SFL9': 28, 'SML9': 28, 'SRL9': 28, 'TAL9': 28, 'ICL9': 47, 'IFL9': 47, 'IHL9': 47, - 'TFL9': 47, 'TL9': 47, 'TSL9': 47, 'SAL9': 28, 'PGL9': 29, 'PFL9': 28, 'LH':29} + 'TFL9': 47, 'TL9': 47, 'TSL9': 47, 'SAL9': 28, 'PGL9': 29, 'PFL9': 28, + 'LHL9':29,'LUL9':30} # 常量 QSIZE = 500 ALL_MARKET_BEGIN_HOUR = 8