Skip to content

3、执行过程

woldy edited this page Jan 31, 2024 · 4 revisions

一、基本格式

本项目所有命令均以如下格式执行:

finhack {module}  {action} --vendor={vendor}  [--background]   --arg1=xxx --arg2=xxx ……

翻译过来就是:

finhack {模块} {动作} --vendor={供给侧} [--后台运行] --参数1=xxx  --参数2=xxx

其中参数注册需要到 project_path/data/config/agrs.conf 下进行配置

命令运行后,将按如下优先级尝试执行对应action

  • {project_path}/loader/{module}_loader.py {action}()
  • finhack.core.loader.{module}_loader {action}()
  • finhack.core.loader.base_loader {action}()
  • {project_path}/{module}/{vendor}/{vendor}_{module}.py {action}()
  • finhack/{module}/{vendor}/{vendor}_{module}.py {action}()

上面5个文件,前3个loader类型的文件,基本都是对要执行的动作做一些预处理,最终大概率还是会执行到后两个:

  • {project_path}/{module}/{vendor}/{vendor}_{module}.py {action}()
  • finhack/{module}/{vendor}/{vendor}_{module}.py {action}()

二、流程详解

2.1、整体流程

image

2.2、入口文件

当安装了finhack框架后,本框架将自动在pip的可执行目录生成一个finhack文件,即本框架的主要入口文件,该文件代码如下:

#!/usr/bin/env python
import os
from finhack.core.core import Core
import multiprocessing
import atexit

def child_process_action(core):
    # 注册子进程退出时的清理函数,如果有的话
    # atexit.register(Utils.child_cleanup)
    core.do_action()
    # 子进程完成任务后退出
    os._exit(0)

if __name__ == '__main__':
    core = Core()
    core.load_module()

    from finhack.library.utils import Utils
    if core.args.background:
        pid = os.fork()  # 创建一个新进程
        if pid == -1:  # 创建进程失败
            print("创建后台进程失败!")
            exit(1)
        elif pid == 0:  # 子进程中执行
            child_process_action(core)
        else:  # 父进程中继续执行
            Utils.write_pids(pid)
            print("启动后台任务!")
    else:
        # 注册父进程退出时的清理函数
        #atexit.register(Utils.auto_exit)
        core.do_action()

可以看到,该文件首先初始化了finhack的Core类,然后判断是否是后台任务,如果是后台任务则启动一个子进程切换到后台执行。

2.3、Core类

Core类主要执行一些初始化的动作,包括:

  • 检查项目目录的可用性
  • 解析命令行参数
  • 生成运行时、全局变量等各种配置
  • 加载模块类、解析模块参数、执行模块action
  • 初始化日志

2.4、模块loader

运行命令后,框架会按照如下加载顺序寻找 model_loader,

  • {project_path}/loader/{module}_loader.py
  • finhack.core.loader.{module}_loader
  • finhack.core.loader.base_loader

以TraderLoader为例,类名都是以vendor的大写字母开头+Loader命名

from finhack.core.loader.base_loader import BaseLoader
class TraderLoader(BaseLoader):
    def run(self):
        # print(self.module_path)
        # print(self.user_module_path)
        # print(self.klass)
        trader=self.klass
        trader.args=self.args
        trader.run()
        pass

这里面的TraderLoader继承了BaseLoader,BaseLoader的用途如下:

  • 识别并加载对应的module文件
  • 注册stop函数,可以停止后台运行的任务
  • 注册run函数,内容同上面代码,默认直接调用vendor类的run

loader的用途是如果一个module存在多个vendor,那么有时候可能会借助loader进行一些处理,当loader类和vendor中的类存在同样的方法时,将运行loader类中的同名方法。(如大家都有run(),则优先运行loader中的run,如果loader没有,才运行vendor中的run)

2.5、加载模块Loader

按顺序优先级查找并加载,对应module的vendor类,并执行对应的action

  • finhack.{module}.{vendor}.{vendor}_{module}
  • project_path/{module}/{vendor}/{vendor}_{module}.py

若命令行中未指定--vendor参数,vendor默认为default

vendor类的用途在于方便自定义各种动作,比如finhack框架中有一个collector模块,vendor是tushare,讲使用tushare自动采集数据,使用者也可以在project目录,开发一些vendor为qmt、joinquant、yfinance等,进行对应数据源的采集。

三、自定义命令

个人认为finhack框架最大的优势就是可以自定义命令,在demo_project中,已经给出了具体的例子,我们只需要新建3个文件(最少为1个文件),即可建立一个完整的testmodule

1、[可选]首先我们在demo_project/loader目录下新建testmodule_loader.py,内容如下:

#类名首字母大写,为vendor+Loader的格式
import finhack.library.log as Log
from finhack.core.loader.base_loader import BaseLoader
class TestmoduleLoader(BaseLoader):
    def testaction(self):
        Log.logger.debug("----%s----" % (__file__))
        Log.logger.debug("this is TestmoduleLoader")
        Log.logger.debug("loading "+self.module_name)
        Log.logger.debug("testarg1 is:"+str(self.args.testarg1))
        Log.logger.debug("testarg2 is:"+str(self.args.testarg2))
        obj=self.klass
        obj.args=self.args
        obj.run()

2、然后再建立demo_project/default/default_testmodule.py (default为vendor名),内容如下:

#类名首字母大写,vendor名字首字母大写
import finhack.library.log as Log
class DefaultTestmodule():
    #finhack testmodule run
    def __init__(self):
        pass
    def run(self):
        Log.logger.debug("----%s----" % (__file__))
        Log.logger.debug("this is Testmodule")
        Log.logger.debug("vendor is default")
        print(self.args)
        while True:
            time.sleep(1)
            Log.logger.debug(".")

3、[可选]由于我们可能要接收一些自定义参数,所以要在demo_project/data/config/args.conf下新增节点:

[testmodule]
testarg1=this is a test key
testarg2=this is also a test key

表示testmodule接收两个参数,默认值在=后面

4、我们尝试运行自定义命令:

finhack testmodule testaction --testarg1=11111111111111111111

image

finhack testmodule run --testarg2=22222222222222222

image