2020-08-16 18:32:16 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf8 -*-
|
|
|
|
|
# cp936
|
|
|
|
|
#
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# ===============================================================================
|
2020-08-16 18:32:16 +08:00
|
|
|
|
# History
|
|
|
|
|
# 1. 20200816, Added by fasiondog
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# ===============================================================================
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2024-01-29 16:47:32 +08:00
|
|
|
|
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
|
|
|
|
|
from sqlalchemy import (create_engine, Sequence, Column, Integer, String, and_, UniqueConstraint)
|
|
|
|
|
from hikyuu.util.singleton import SingletonType
|
|
|
|
|
from hikyuu.util.check import checkif
|
2020-08-16 18:32:16 +08:00
|
|
|
|
import os
|
2020-08-23 01:07:32 +08:00
|
|
|
|
import stat
|
|
|
|
|
import errno
|
2020-08-17 00:35:01 +08:00
|
|
|
|
import sys
|
2020-08-18 00:45:17 +08:00
|
|
|
|
import shutil
|
2020-08-23 18:27:09 +08:00
|
|
|
|
import pathlib
|
2020-08-18 00:45:17 +08:00
|
|
|
|
import logging
|
|
|
|
|
import importlib
|
2020-08-16 18:32:16 +08:00
|
|
|
|
from configparser import ConfigParser
|
|
|
|
|
|
2020-09-12 00:56:46 +08:00
|
|
|
|
# 引入 git 前需设置环境变量,否则某些情况下会报错失败
|
|
|
|
|
os.environ['GIT_PYTHON_REFRESH'] = 'quiet'
|
2021-01-29 00:39:33 +08:00
|
|
|
|
try:
|
|
|
|
|
import git
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(e)
|
|
|
|
|
print("You need install git! see: https://git-scm.com/downloads")
|
2020-09-12 00:56:46 +08:00
|
|
|
|
|
2022-02-26 10:32:17 +08:00
|
|
|
|
|
2020-08-16 18:32:16 +08:00
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
|
|
|
|
|
2020-08-17 00:35:01 +08:00
|
|
|
|
class ConfigModel(Base):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
__tablename__ = 'hub_config'
|
2020-08-17 00:35:01 +08:00
|
|
|
|
id = Column(Integer, Sequence('config_id_seq'), primary_key=True)
|
|
|
|
|
key = Column(String, index=True) # 参数名
|
|
|
|
|
value = Column(String) # 参数值
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2020-08-17 00:35:01 +08:00
|
|
|
|
__table_args__ = (UniqueConstraint('key'), )
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return "ConfigModel(id={}, key={}, value={})".format(self.id, self.key, self.value)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return "<{}>".format(self.__str__())
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
class HubModel(Base):
|
|
|
|
|
__tablename__ = 'hub_repo'
|
2020-08-16 18:32:16 +08:00
|
|
|
|
id = Column(Integer, Sequence('remote_id_seq'), primary_key=True)
|
|
|
|
|
name = Column(String, index=True) # 本地仓库名
|
2020-08-29 17:06:32 +08:00
|
|
|
|
hub_type = Column(String) # 'remote' (远程仓库) | 'local' (本地仓库)
|
2020-08-30 20:47:21 +08:00
|
|
|
|
local_base = Column(String) # 本地路径的基础名称
|
|
|
|
|
local = Column(String) # 本地路径
|
2020-08-16 18:32:16 +08:00
|
|
|
|
url = Column(String) # git 仓库地址
|
|
|
|
|
branch = Column(String) # 远程仓库分支
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
|
|
|
|
__table_args__ = (UniqueConstraint('name'), )
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
|
|
|
|
def __str__(self):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return "HubModel(id={}, name={}, hub_type={}, local={}, url={}, branch={})".format(
|
|
|
|
|
self.id, self.name, self.hub_type, self.local, self.url, self.branch
|
2020-08-19 00:50:10 +08:00
|
|
|
|
)
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return "<{}>".format(self.__str__())
|
|
|
|
|
|
|
|
|
|
|
2020-08-17 00:35:01 +08:00
|
|
|
|
class PartModel(Base):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
__tablename__ = 'hub_part'
|
2020-08-17 00:35:01 +08:00
|
|
|
|
id = Column(Integer, Sequence('part_id_seq'), primary_key=True)
|
2024-01-29 16:47:32 +08:00
|
|
|
|
hub_name = Column(String) # 所属仓库标识
|
2020-08-17 00:35:01 +08:00
|
|
|
|
part = Column(String) # 部件类型
|
|
|
|
|
name = Column(String) # 策略名称
|
2020-08-18 00:45:17 +08:00
|
|
|
|
author = Column(String) # 策略作者
|
2020-08-24 02:00:38 +08:00
|
|
|
|
version = Column(String) # 版本
|
2020-08-23 18:27:09 +08:00
|
|
|
|
doc = Column(String) # 帮助说明
|
2020-08-23 01:07:32 +08:00
|
|
|
|
module_name = Column(String) # 实际策略导入模块名
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
2020-08-19 00:50:10 +08:00
|
|
|
|
def __str__(self):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return 'PartModel(id={}, hub_name={}, part={}, name={}, author={}, module_name={})'.format(
|
|
|
|
|
self.id, self.hub_name, self.part, self.name, self.author, self.module_name
|
2020-08-19 00:50:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return '<{}>'.format(self.__str__())
|
|
|
|
|
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
class HubNameRepeatError(Exception):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
def __init__(self, name):
|
|
|
|
|
self.name = name
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return "已存在相同名称的仓库({}),请更换仓库名!".format(self.name)
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
class HubNotFoundError(Exception):
|
2020-08-23 18:27:09 +08:00
|
|
|
|
def __init__(self, name):
|
|
|
|
|
self.name = name
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '找不到指定的仓库("{}")'.format(self.name)
|
|
|
|
|
|
|
|
|
|
|
2020-08-22 00:48:20 +08:00
|
|
|
|
class ModuleConflictError(Exception):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def __init__(self, hub_name, conflict_module, hub_path):
|
|
|
|
|
self.hub_name = hub_name
|
2020-08-22 19:15:31 +08:00
|
|
|
|
self.conflict_module = conflict_module
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self.hub_path = hub_path
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
def __str__(self):
|
2020-08-30 00:17:08 +08:00
|
|
|
|
return '该仓库({})路径名与其他 python 模块("{}")冲突,请更改目录名称!("{}")'.format(
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self.hub_name, self.conflict_module, self.hub_path
|
2020-08-22 19:15:31 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PartNotFoundError(Exception):
|
|
|
|
|
def __init__(self, name, cause):
|
|
|
|
|
self.name = name
|
|
|
|
|
self.cause = cause
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '未找到指定的策略部件: "{}", {}!'.format(self.name, self.cause)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-23 18:27:09 +08:00
|
|
|
|
class PartNameError(Exception):
|
|
|
|
|
def __init__(self, name):
|
|
|
|
|
self.name = name
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '无效的策略部件名称: "{}"!'.format(self.name)
|
|
|
|
|
|
|
|
|
|
|
2020-08-23 01:07:32 +08:00
|
|
|
|
# Windows下 shutil.rmtree 删除的目录中如有存在只读文件或目录会导致失败,需要此函数辅助处理
|
|
|
|
|
# 可参见:https://blog.csdn.net/Tri_C/article/details/99862201
|
|
|
|
|
def handle_remove_read_only(func, path, exc):
|
|
|
|
|
excvalue = exc[1]
|
|
|
|
|
if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES:
|
|
|
|
|
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777
|
|
|
|
|
func(path)
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError('无法移除目录 "{}",请手工删除'.format(path))
|
|
|
|
|
|
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
def dbsession(func):
|
|
|
|
|
def wrapfunc(*args, **kwargs):
|
|
|
|
|
x = args[0]
|
|
|
|
|
old_session = x._session
|
|
|
|
|
if x._session is None:
|
|
|
|
|
x._session = x._scoped_Session()
|
|
|
|
|
result = func(*args, **kwargs)
|
|
|
|
|
x._session.commit()
|
|
|
|
|
if old_session is not x._session:
|
|
|
|
|
x._session.close()
|
|
|
|
|
x._session = old_session
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
return wrapfunc
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
class HubManager(metaclass=SingletonType):
|
2020-08-16 18:32:16 +08:00
|
|
|
|
"""策略库管理"""
|
2024-01-29 16:47:32 +08:00
|
|
|
|
|
2020-08-16 18:32:16 +08:00
|
|
|
|
def __init__(self):
|
2020-08-18 00:45:17 +08:00
|
|
|
|
self.logger = logging.getLogger(self.__class__.__name__)
|
2020-08-16 18:32:16 +08:00
|
|
|
|
usr_dir = os.path.expanduser('~')
|
2020-09-10 22:34:40 +08:00
|
|
|
|
hku_dir = '{}/.hikyuu'.format(usr_dir)
|
|
|
|
|
if not os.path.lexists(hku_dir):
|
|
|
|
|
os.mkdir(hku_dir)
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
|
|
|
|
# 创建仓库数据库
|
2020-08-29 17:06:32 +08:00
|
|
|
|
engine = create_engine("sqlite:///{}/.hikyuu/hub.db".format(usr_dir))
|
2020-08-16 18:32:16 +08:00
|
|
|
|
Base.metadata.create_all(engine)
|
2022-02-26 10:32:17 +08:00
|
|
|
|
self._scoped_Session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
|
2020-08-22 19:15:31 +08:00
|
|
|
|
self._session = None
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def setup_hub(self):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""初始化 hikyuu 默认策略仓库"""
|
2020-08-20 02:20:19 +08:00
|
|
|
|
usr_dir = os.path.expanduser('~')
|
|
|
|
|
|
2020-08-17 00:35:01 +08:00
|
|
|
|
# 检查并建立远端仓库的本地缓存目录
|
2022-02-26 10:32:17 +08:00
|
|
|
|
self.remote_cache_dir = self._session.query(ConfigModel.value).filter(ConfigModel.key == 'remote_cache_dir'
|
|
|
|
|
).first()
|
2020-08-17 00:35:01 +08:00
|
|
|
|
if self.remote_cache_dir is None:
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self.remote_cache_dir = "{}/.hikyuu/hub_cache".format(usr_dir)
|
2020-08-17 00:35:01 +08:00
|
|
|
|
record = ConfigModel(key='remote_cache_dir', value=self.remote_cache_dir)
|
|
|
|
|
self._session.add(record)
|
2020-08-16 18:32:16 +08:00
|
|
|
|
else:
|
2020-08-17 00:35:01 +08:00
|
|
|
|
self.remote_cache_dir = self.remote_cache_dir[0]
|
|
|
|
|
|
|
|
|
|
if not os.path.lexists(self.remote_cache_dir):
|
|
|
|
|
os.makedirs(self.remote_cache_dir)
|
|
|
|
|
|
2020-08-18 00:45:17 +08:00
|
|
|
|
# 将远程仓库本地缓存地址加入系统路径
|
|
|
|
|
sys.path.append(self.remote_cache_dir)
|
|
|
|
|
|
2020-08-19 00:50:10 +08:00
|
|
|
|
# 将所有本地仓库的上层路径加入系统路径
|
2020-08-29 17:06:32 +08:00
|
|
|
|
hub_models = self._session.query(HubModel).filter_by(hub_type='local').all()
|
|
|
|
|
for model in hub_models:
|
2020-08-21 00:07:53 +08:00
|
|
|
|
sys.path.append(os.path.dirname(model.local))
|
2020-08-19 00:50:10 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
# 检查并下载 hikyuu 默认策略仓库, hikyuu_hub 避免导入时模块和 hikyuu 重名
|
2022-02-26 10:32:17 +08:00
|
|
|
|
hikyuu_hub_path = self._session.query(HubModel.local).filter(HubModel.name == 'default').first()
|
2020-08-29 17:06:32 +08:00
|
|
|
|
if hikyuu_hub_path is None:
|
|
|
|
|
self.add_remote_hub('default', 'https://gitee.com/fasiondog/hikyuu_hub.git', 'master')
|
2020-08-22 19:15:31 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def download_remote_hub(self, local_dir, url, branch):
|
2020-08-22 19:15:31 +08:00
|
|
|
|
print('正在下载 hikyuu 策略仓库至:"{}"'.format(local_dir))
|
|
|
|
|
|
|
|
|
|
# 如果存在同名缓存目录,则强制删除
|
|
|
|
|
if os.path.lexists(local_dir):
|
2020-08-23 01:07:32 +08:00
|
|
|
|
shutil.rmtree(local_dir, onerror=handle_remove_read_only)
|
2020-08-22 19:15:31 +08:00
|
|
|
|
|
|
|
|
|
try:
|
2020-08-23 01:07:32 +08:00
|
|
|
|
git.Repo.clone_from(url, local_dir, branch=branch)
|
2020-08-22 19:15:31 +08:00
|
|
|
|
except:
|
2021-06-12 17:14:07 +08:00
|
|
|
|
raise RuntimeError("需要安装git(https://git-scm.com/),或检查网络是否正常或链接地址({})是否正确!".format(url))
|
2020-08-22 19:15:31 +08:00
|
|
|
|
print('下载完毕')
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def add_remote_hub(self, name, url, branch='master'):
|
2020-08-17 00:35:01 +08:00
|
|
|
|
"""增加远程策略仓库
|
|
|
|
|
|
2020-08-22 00:48:20 +08:00
|
|
|
|
:param str name: 本地仓库名称(自行起名)
|
2020-08-17 00:35:01 +08:00
|
|
|
|
:param str url: git 仓库地址
|
|
|
|
|
:param str branch: git 仓库分支
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
record = self._session.query(HubModel).filter(HubModel.name == name).first()
|
|
|
|
|
checkif(record is not None, name, HubNameRepeatError)
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2022-02-26 10:32:17 +08:00
|
|
|
|
record = self._session.query(HubModel).filter(and_(HubModel.url == url, HubModel.branch == branch)).first()
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
# 下载远程仓库
|
2020-08-17 00:35:01 +08:00
|
|
|
|
local_dir = "{}/{}".format(self.remote_cache_dir, name)
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self.download_remote_hub(local_dir, url, branch)
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
2020-08-20 02:20:19 +08:00
|
|
|
|
# 导入仓库各部件策略信息
|
2022-02-26 10:32:17 +08:00
|
|
|
|
record = HubModel(name=name, hub_type='remote', url=url, branch=branch, local_base=name, local=local_dir)
|
2020-08-20 02:20:19 +08:00
|
|
|
|
self.import_part_to_db(record)
|
|
|
|
|
|
|
|
|
|
# 更新仓库记录
|
2020-08-16 18:32:16 +08:00
|
|
|
|
self._session.add(record)
|
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-30 00:17:08 +08:00
|
|
|
|
def add_local_hub(self, name, path):
|
2020-08-21 00:07:53 +08:00
|
|
|
|
"""增加本地数据仓库
|
|
|
|
|
|
2020-08-30 00:17:08 +08:00
|
|
|
|
:param str name: 仓库名称
|
2020-08-21 00:07:53 +08:00
|
|
|
|
:param str path: 本地全路径
|
|
|
|
|
"""
|
2020-08-22 00:48:20 +08:00
|
|
|
|
checkif(not os.path.lexists(path), '找不到指定的路径("{}")'.format(path))
|
2020-08-21 00:07:53 +08:00
|
|
|
|
|
|
|
|
|
# 获取绝对路径
|
|
|
|
|
local_path = os.path.abspath(path)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
record = self._session.query(HubModel).filter(HubModel.name == name).first()
|
|
|
|
|
checkif(record is not None, name, HubNameRepeatError)
|
2020-08-21 00:07:53 +08:00
|
|
|
|
|
|
|
|
|
# 将本地路径的上一层路径加入系统路径
|
|
|
|
|
sys.path.append(os.path.dirname(path))
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
# 检查仓库目录名称是否与其他 python 模块存在冲突
|
2020-08-30 00:17:08 +08:00
|
|
|
|
tmp = importlib.import_module(os.path.basename(local_path))
|
2020-08-22 19:15:31 +08:00
|
|
|
|
checkif(
|
|
|
|
|
tmp.__path__[0] != local_path,
|
|
|
|
|
name,
|
|
|
|
|
ModuleConflictError,
|
|
|
|
|
conflict_module=tmp.__path__[0],
|
2020-08-29 17:06:32 +08:00
|
|
|
|
hub_path=local_path
|
2020-08-22 19:15:31 +08:00
|
|
|
|
)
|
2020-08-21 00:07:53 +08:00
|
|
|
|
|
|
|
|
|
# 导入部件信息
|
2020-08-30 20:47:21 +08:00
|
|
|
|
local_base = os.path.basename(local_path)
|
|
|
|
|
hub_model = HubModel(name=name, hub_type='local', local_base=local_base, local=local_path)
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self.import_part_to_db(hub_model)
|
2020-08-21 00:07:53 +08:00
|
|
|
|
|
|
|
|
|
# 更新仓库记录
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self._session.add(hub_model)
|
2020-08-21 00:07:53 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def update_hub(self, name):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""更新指定仓库
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
hub_model = self._session.query(HubModel).filter_by(name=name).first()
|
|
|
|
|
checkif(hub_model is None, '指定的仓库({})不存在!'.format(name))
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self._session.query(PartModel).filter_by(hub_name=name).delete()
|
|
|
|
|
if hub_model.hub_type == 'remote':
|
|
|
|
|
self.download_remote_hub(hub_model.local, hub_model.url, hub_model.branch)
|
|
|
|
|
self.import_part_to_db(hub_model)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
2024-01-29 16:47:32 +08:00
|
|
|
|
@dbsession
|
|
|
|
|
def build_hub(self, name, cmd='buildall'):
|
|
|
|
|
"""构建 cpp 部分 part
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
:param str cmd: 同仓库下 python setup.py 后的命令参数,如: build -t ind -n cpp_example
|
|
|
|
|
"""
|
|
|
|
|
hub_model = self._session.query(HubModel).filter_by(name=name).first()
|
|
|
|
|
checkif(hub_model is None, '指定的仓库({})不存在!'.format(name))
|
2024-01-30 13:07:36 +08:00
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
|
os.system(f"python {hub_model.local}/setup.py {cmd}")
|
|
|
|
|
else:
|
|
|
|
|
os.system(f"python3 {hub_model.local}/setup.py {cmd}")
|
2024-01-29 16:47:32 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def remove_hub(self, name):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""删除指定的仓库
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
self._session.query(PartModel).filter_by(hub_name=name).delete()
|
|
|
|
|
self._session.query(HubModel).filter_by(name=name).delete()
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def import_part_to_db(self, hub_model):
|
2020-08-18 00:45:17 +08:00
|
|
|
|
part_dict = {
|
|
|
|
|
'af': 'part/af',
|
|
|
|
|
'cn': 'part/cn',
|
|
|
|
|
'ev': 'part/ev',
|
2024-04-03 11:24:24 +08:00
|
|
|
|
'mf': 'part/mf',
|
2020-08-18 00:45:17 +08:00
|
|
|
|
'mm': 'part/mm',
|
|
|
|
|
'pg': 'part/pg',
|
|
|
|
|
'se': 'part/se',
|
|
|
|
|
'sg': 'part/sg',
|
|
|
|
|
'sp': 'part/sp',
|
2020-08-22 00:48:20 +08:00
|
|
|
|
'st': 'part/st',
|
2024-04-04 03:14:45 +08:00
|
|
|
|
'pf': 'pf',
|
2020-08-24 02:00:38 +08:00
|
|
|
|
'sys': 'sys',
|
2023-10-25 17:53:21 +08:00
|
|
|
|
'ind': 'ind',
|
2024-02-01 15:49:38 +08:00
|
|
|
|
'other': 'other',
|
2020-08-18 00:45:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 检查仓库本地目录是否存在,不存在则给出告警信息并直接返回
|
2020-08-29 17:06:32 +08:00
|
|
|
|
local_dir = hub_model.local
|
2020-08-18 00:45:17 +08:00
|
|
|
|
if not os.path.lexists(local_dir):
|
|
|
|
|
self.logger.warning(
|
2022-02-26 10:32:17 +08:00
|
|
|
|
'The {} hub path ("{}") is not exists! Ignored this hub!'.format(hub_model.name, hub_model.local)
|
2020-08-18 00:45:17 +08:00
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
base_local = os.path.basename(local_dir)
|
|
|
|
|
|
2020-08-19 00:50:10 +08:00
|
|
|
|
# 遍历仓库导入部件信息
|
2020-08-18 00:45:17 +08:00
|
|
|
|
for part, part_dir in part_dict.items():
|
2020-08-29 17:06:32 +08:00
|
|
|
|
path = "{}/{}".format(hub_model.local, part_dir)
|
2020-08-18 00:45:17 +08:00
|
|
|
|
try:
|
|
|
|
|
with os.scandir(path) as it:
|
|
|
|
|
for entry in it:
|
2022-02-26 10:32:17 +08:00
|
|
|
|
if (not entry.name.startswith('.')) and entry.is_dir() and (entry.name != "__pycache__"):
|
2020-08-19 00:50:10 +08:00
|
|
|
|
# 计算实际的导入模块名
|
2022-02-26 10:32:17 +08:00
|
|
|
|
module_name = '{}.part.{}.{}.part'.format(base_local, part, entry.name) if part not in (
|
2024-04-04 03:14:45 +08:00
|
|
|
|
'pf', 'sys', 'ind', 'other'
|
2020-08-19 00:50:10 +08:00
|
|
|
|
) else '{}.{}.{}.part'.format(base_local, part, entry.name)
|
|
|
|
|
|
|
|
|
|
# 导入模块
|
|
|
|
|
try:
|
2020-08-21 00:07:53 +08:00
|
|
|
|
part_module = importlib.import_module(module_name)
|
2020-08-19 00:50:10 +08:00
|
|
|
|
except ModuleNotFoundError:
|
2024-01-30 13:07:36 +08:00
|
|
|
|
self.logger.error('{} 缺失 part.py 文件, 位置:"{}"!'.format(module_name, entry.path))
|
2020-08-24 02:00:38 +08:00
|
|
|
|
continue
|
|
|
|
|
except:
|
2024-01-30 13:07:36 +08:00
|
|
|
|
self.logger.error('{} 无法导入该文件: {}'.format(module_name, entry.path))
|
2020-08-19 00:50:10 +08:00
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
module_vars = vars(part_module)
|
2020-08-25 23:27:13 +08:00
|
|
|
|
if 'part' not in module_vars:
|
|
|
|
|
self.logger.error('缺失 part 函数!("{}")'.format(entry.path))
|
|
|
|
|
continue
|
2020-08-19 00:50:10 +08:00
|
|
|
|
|
2022-02-26 10:32:17 +08:00
|
|
|
|
name = '{}.{}.{}'.format(hub_model.name, part, entry.name) if part not in (
|
2024-04-04 03:14:45 +08:00
|
|
|
|
'pf', 'sys', 'ind', 'other'
|
2020-08-29 17:06:32 +08:00
|
|
|
|
) else '{}.{}.{}'.format(hub_model.name, part, entry.name)
|
2020-08-19 00:50:10 +08:00
|
|
|
|
|
2020-08-25 23:27:13 +08:00
|
|
|
|
try:
|
|
|
|
|
part_model = PartModel(
|
2020-08-29 17:06:32 +08:00
|
|
|
|
hub_name=hub_model.name,
|
2020-08-25 23:27:13 +08:00
|
|
|
|
part=part,
|
|
|
|
|
name=name,
|
|
|
|
|
module_name=module_name,
|
2022-02-26 10:32:17 +08:00
|
|
|
|
author=part_module.author.strip() if 'author' in module_vars else 'None',
|
|
|
|
|
version=part_module.version.strip() if 'version' in module_vars else 'None',
|
|
|
|
|
doc=part_module.part.__doc__.strip() if part_module.part.__doc__ else "None"
|
2020-08-25 23:27:13 +08:00
|
|
|
|
)
|
|
|
|
|
self._session.add(part_model)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error('存在语法错误 ("{}/part.py")! {}'.format(entry.path, e))
|
|
|
|
|
continue
|
2020-08-23 18:27:09 +08:00
|
|
|
|
|
2020-08-18 00:45:17 +08:00
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
continue
|
2020-08-19 00:50:10 +08:00
|
|
|
|
|
2020-08-22 19:15:31 +08:00
|
|
|
|
@dbsession
|
2020-08-23 18:27:09 +08:00
|
|
|
|
def get_part(self, name, **kwargs):
|
2020-08-22 19:15:31 +08:00
|
|
|
|
"""获取指定策略部件
|
|
|
|
|
|
|
|
|
|
:param str name: 策略部件名称
|
2020-08-23 18:27:09 +08:00
|
|
|
|
:param kwargs: 其他部件相关参数
|
2020-08-22 19:15:31 +08:00
|
|
|
|
"""
|
2020-08-23 18:27:09 +08:00
|
|
|
|
name_parts = name.split('.')
|
|
|
|
|
checkif(
|
2022-02-26 10:32:17 +08:00
|
|
|
|
len(name_parts) < 2
|
2024-04-04 03:14:45 +08:00
|
|
|
|
or (name_parts[-2] not in ('af', 'cn', 'ev', 'mf', 'mm', 'pg', 'se', 'sg', 'sp', 'st', 'pf', 'sys', 'ind', 'other')),
|
2023-10-25 17:53:21 +08:00
|
|
|
|
name, PartNameError
|
2020-08-23 18:27:09 +08:00
|
|
|
|
)
|
|
|
|
|
|
2020-08-30 00:17:08 +08:00
|
|
|
|
# 未指定仓库名,则默认使用 'default' 仓库
|
|
|
|
|
part_name = 'default.{}'.format(name) if len(name_parts) == 2 else name
|
2020-08-23 18:27:09 +08:00
|
|
|
|
part_model = self._session.query(PartModel).filter_by(name=part_name).first()
|
|
|
|
|
checkif(part_model is None, part_name, PartNotFoundError, cause='仓库中不存在')
|
2020-08-22 19:15:31 +08:00
|
|
|
|
try:
|
|
|
|
|
part_module = importlib.import_module(part_model.module_name)
|
|
|
|
|
except ModuleNotFoundError:
|
2020-08-23 18:27:09 +08:00
|
|
|
|
raise PartNotFoundError(part_name, '请检查部件对应路径是否存在')
|
2020-08-25 00:24:43 +08:00
|
|
|
|
part = part_module.part(**kwargs)
|
2023-10-25 17:53:21 +08:00
|
|
|
|
try:
|
|
|
|
|
part.name = part_model.name
|
|
|
|
|
part.info = self.get_part_info(part.name)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2020-08-19 00:50:10 +08:00
|
|
|
|
return part
|
2020-08-17 00:35:01 +08:00
|
|
|
|
|
2020-08-23 18:27:09 +08:00
|
|
|
|
@dbsession
|
|
|
|
|
def get_part_info(self, name):
|
|
|
|
|
"""获取策略部件信息
|
|
|
|
|
|
|
|
|
|
:param str name: 部件名称
|
|
|
|
|
"""
|
|
|
|
|
part_model = self._session.query(PartModel).filter_by(name=name).first()
|
|
|
|
|
checkif(part_model is None, name, PartNotFoundError, cause='仓库中不存在')
|
|
|
|
|
return {
|
|
|
|
|
'name': name,
|
|
|
|
|
'author': part_model.author,
|
2020-08-24 02:00:38 +08:00
|
|
|
|
'version': part_model.version,
|
2020-08-23 18:27:09 +08:00
|
|
|
|
'doc': part_model.doc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def print_part_info(self, name):
|
|
|
|
|
info = self.get_part_info(name)
|
2022-02-26 10:44:44 +08:00
|
|
|
|
print('+---------+------------------------------------------------')
|
|
|
|
|
print('| name | ', info['name'])
|
|
|
|
|
print('+---------+------------------------------------------------')
|
|
|
|
|
print('| author | ', info['author'])
|
|
|
|
|
print('+---------+------------------------------------------------')
|
|
|
|
|
print('| version | ', info['version'])
|
|
|
|
|
print('+---------+------------------------------------------------')
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# print('\n')
|
2020-08-24 02:00:38 +08:00
|
|
|
|
print(info['doc'])
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# print('\n')
|
|
|
|
|
# print('----------------------------------------------------------')
|
2020-08-23 18:27:09 +08:00
|
|
|
|
|
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_hub_path(self, name):
|
2020-08-23 18:27:09 +08:00
|
|
|
|
"""获取仓库所在的本地路径
|
2024-01-29 16:47:32 +08:00
|
|
|
|
|
2020-08-23 18:27:09 +08:00
|
|
|
|
:param str name: 仓库名
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
path = self._session.query(HubModel.local).filter_by(name=name).first()
|
|
|
|
|
checkif(path is None, name, HubNotFoundError)
|
2020-08-23 18:27:09 +08:00
|
|
|
|
return path[0]
|
|
|
|
|
|
2020-08-26 00:09:58 +08:00
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_hub_name_list(self):
|
2020-08-26 00:09:58 +08:00
|
|
|
|
"""返回仓库名称列表"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return [record[0] for record in self._session.query(HubModel.name).all()]
|
2020-08-26 00:09:58 +08:00
|
|
|
|
|
|
|
|
|
@dbsession
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_part_name_list(self, hub=None, part_type=None):
|
2020-08-26 00:09:58 +08:00
|
|
|
|
"""获取部件名称列表
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
:param str hub: 仓库名
|
2020-08-26 00:09:58 +08:00
|
|
|
|
:param str part_type: 部件类型
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
if hub is None and part_type is None:
|
2020-08-26 00:09:58 +08:00
|
|
|
|
results = self._session.query(PartModel.name).all()
|
2020-08-29 17:06:32 +08:00
|
|
|
|
elif hub is None:
|
2020-08-26 00:09:58 +08:00
|
|
|
|
results = self._session.query(PartModel.name).filter_by(part=part_type).all()
|
|
|
|
|
elif part_type is None:
|
2020-08-29 17:06:32 +08:00
|
|
|
|
results = self._session.query(PartModel.name).filter_by(hub_name=hub).all()
|
2020-08-26 00:09:58 +08:00
|
|
|
|
else:
|
2022-02-26 10:32:17 +08:00
|
|
|
|
results = self._session.query(PartModel.name
|
|
|
|
|
).filter(and_(PartModel.hub_name == hub, PartModel.part == part_type)).all()
|
2020-08-26 00:09:58 +08:00
|
|
|
|
return [record[0] for record in results]
|
|
|
|
|
|
2020-08-30 20:47:21 +08:00
|
|
|
|
@dbsession
|
|
|
|
|
def get_current_hub(self, filename):
|
|
|
|
|
"""用于在仓库part.py中获取当前所在的仓库名
|
|
|
|
|
|
|
|
|
|
示例: get_current_hub(__file__)
|
|
|
|
|
"""
|
2024-01-29 16:47:32 +08:00
|
|
|
|
abs_path = os.path.abspath(filename) # 当前文件的绝对路径
|
2020-08-30 20:47:21 +08:00
|
|
|
|
path_parts = pathlib.Path(abs_path).parts
|
2024-06-11 20:52:18 +08:00
|
|
|
|
local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[-5]
|
2020-08-30 20:47:21 +08:00
|
|
|
|
hub_model = self._session.query(HubModel.name).filter_by(local_base=local_base).first()
|
|
|
|
|
checkif(hub_model is None, local_base, HubNotFoundError)
|
|
|
|
|
return hub_model.name
|
|
|
|
|
|
2020-08-16 18:32:16 +08:00
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def add_remote_hub(name, url, branch='master'):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""增加远程策略仓库
|
|
|
|
|
|
|
|
|
|
:param str name: 本地仓库名称(自行起名)
|
|
|
|
|
:param str url: git 仓库地址
|
|
|
|
|
:param str branch: git 仓库分支
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().add_remote_hub(name, url, branch)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-30 00:17:08 +08:00
|
|
|
|
def add_local_hub(name, path):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""增加本地数据仓库
|
|
|
|
|
|
2020-08-30 00:17:08 +08:00
|
|
|
|
:param str name: 仓库名称
|
2020-08-22 00:48:20 +08:00
|
|
|
|
:param str path: 本地全路径
|
|
|
|
|
"""
|
2020-08-30 00:17:08 +08:00
|
|
|
|
HubManager().add_local_hub(name, path)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def update_hub(name):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""更新指定仓库
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().update_hub(name)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
|
2024-01-29 16:47:32 +08:00
|
|
|
|
def build_hub(name, cmd='buildall'):
|
|
|
|
|
"""构建 cpp 部分 part
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
:param str cmd: 同仓库下 python setup.py 后的命令参数,如: build -t ind -n cpp_example
|
|
|
|
|
"""
|
|
|
|
|
HubManager().build_hub(name, cmd)
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def remove_hub(name):
|
2020-08-22 00:48:20 +08:00
|
|
|
|
"""删除指定的仓库
|
|
|
|
|
|
|
|
|
|
:param str name: 仓库名称
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().remove_hub(name)
|
2020-08-22 00:48:20 +08:00
|
|
|
|
|
|
|
|
|
|
2024-03-30 03:17:15 +08:00
|
|
|
|
def get_part(name, *args, **kwargs):
|
2020-08-22 19:15:31 +08:00
|
|
|
|
"""获取指定策略部件
|
|
|
|
|
|
|
|
|
|
:param str name: 策略部件名称
|
2024-03-30 03:17:15 +08:00
|
|
|
|
:param args: 其他部件相关参数
|
2020-08-23 18:27:09 +08:00
|
|
|
|
:param kwargs: 其他部件相关参数
|
2020-08-22 19:15:31 +08:00
|
|
|
|
"""
|
2024-03-30 03:17:15 +08:00
|
|
|
|
return HubManager().get_part(name, *args, **kwargs)
|
2020-08-23 18:27:09 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_hub_path(name):
|
2020-08-23 18:27:09 +08:00
|
|
|
|
"""获取仓库所在的本地路径
|
2024-01-29 16:47:32 +08:00
|
|
|
|
|
2020-08-23 18:27:09 +08:00
|
|
|
|
:param str name: 仓库名
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return HubManager().get_hub_path(name)
|
2020-08-23 18:27:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_part_info(name):
|
|
|
|
|
"""获取策略部件信息
|
2024-01-29 16:47:32 +08:00
|
|
|
|
|
2020-08-23 18:27:09 +08:00
|
|
|
|
:param str name: 部件名称
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return HubManager().get_part_info(name)
|
2020-08-23 18:27:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_part_info(name):
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().print_part_info(name)
|
2020-08-22 19:15:31 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_hub_name_list():
|
2020-08-26 00:09:58 +08:00
|
|
|
|
"""返回仓库名称列表"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return HubManager().get_hub_name_list()
|
2020-08-26 00:09:58 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_part_name_list(hub=None, part_type=None):
|
2020-08-26 00:09:58 +08:00
|
|
|
|
"""获取部件名称列表
|
2020-08-29 17:06:32 +08:00
|
|
|
|
:param str hub: 仓库名
|
2020-08-26 00:09:58 +08:00
|
|
|
|
:param str part_type: 部件类型
|
|
|
|
|
"""
|
2020-08-29 17:06:32 +08:00
|
|
|
|
return HubManager().get_part_name_list(hub, part_type)
|
2020-08-26 00:09:58 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
def get_current_hub(filename):
|
2020-08-27 00:34:58 +08:00
|
|
|
|
"""用于在仓库part.py中获取当前所在的仓库名
|
|
|
|
|
|
2020-08-29 17:06:32 +08:00
|
|
|
|
示例: get_current_hub(__file__)
|
2020-08-27 00:34:58 +08:00
|
|
|
|
"""
|
2020-08-30 20:47:21 +08:00
|
|
|
|
return HubManager().get_current_hub(filename)
|
2020-08-27 00:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
2020-08-26 00:26:13 +08:00
|
|
|
|
# 初始化仓库
|
2020-08-26 00:34:43 +08:00
|
|
|
|
try:
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().setup_hub()
|
2020-08-26 00:34:43 +08:00
|
|
|
|
except Exception as e:
|
2020-08-29 17:06:32 +08:00
|
|
|
|
HubManager().logger.warning("无法初始化 hikyuu 策略仓库! {}".format(e))
|
2020-08-26 00:26:13 +08:00
|
|
|
|
|
|
|
|
|
__all__ = [
|
2020-08-29 17:06:32 +08:00
|
|
|
|
'add_remote_hub',
|
|
|
|
|
'add_local_hub',
|
|
|
|
|
'update_hub',
|
|
|
|
|
'remove_hub',
|
2024-01-30 13:07:36 +08:00
|
|
|
|
'build_hub',
|
2020-08-26 00:26:13 +08:00
|
|
|
|
'get_part',
|
2020-08-29 17:06:32 +08:00
|
|
|
|
'get_hub_path',
|
2020-08-26 00:26:13 +08:00
|
|
|
|
'get_part_info',
|
|
|
|
|
'print_part_info',
|
2020-08-29 17:06:32 +08:00
|
|
|
|
'get_hub_name_list',
|
2020-08-26 00:26:13 +08:00
|
|
|
|
'get_part_name_list',
|
2020-08-29 17:06:32 +08:00
|
|
|
|
'get_current_hub',
|
2020-08-26 00:26:13 +08:00
|
|
|
|
]
|
|
|
|
|
|
2020-08-16 18:32:16 +08:00
|
|
|
|
if __name__ == "__main__":
|
2020-08-22 19:15:31 +08:00
|
|
|
|
logging.basicConfig(
|
2022-02-26 10:32:17 +08:00
|
|
|
|
level=logging.INFO, format='%(asctime)-15s [%(levelname)s] - %(message)s [%(name)s::%(funcName)s]'
|
2020-08-22 19:15:31 +08:00
|
|
|
|
)
|
2023-10-25 17:53:21 +08:00
|
|
|
|
# add_local_hub('dev', '/home/fasiondog/workspace/stockhouse')
|
|
|
|
|
remove_hub('dev')
|
|
|
|
|
add_local_hub('dev', r'D:\workspace\hikyuu_hub')
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# update_hub('test1')
|
|
|
|
|
# update_hub('default')
|
|
|
|
|
# build_hub('dev', 'buildall')
|
2023-10-25 17:53:21 +08:00
|
|
|
|
# sg = get_part('dev.st.fixed_percent')
|
2024-01-29 16:47:32 +08:00
|
|
|
|
# print(sg)
|
2023-10-25 17:53:21 +08:00
|
|
|
|
# print_part_info('default.sp.fixed_value')
|
|
|
|
|
# print(get_part_name_list(part_type='sg'))
|