def __init__(self, name: Text, import_name, engine_method: str = "thread", instance_path=None): """ 初始化 """ self.name = name self.import_name = import_name if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持thread和async,请检查代码") if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.') self.risk_gateway = RiskLevel(self) self.instance_path = instance_path self.config = self.make_config() self.interface = Interface() _app_context_ctx.push(self.name, self)
def __init__(self, name: Text, import_name, engine_method: str = "thread", work_mode="limit_time", refresh: bool = False, instance_path=None): """ 初始化 """ self.name = name self.import_name = import_name self.engine_method = engine_method self.refresh = refresh if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持 thread 和 async,请检查代码") if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.') self.risk_gateway_class = None self.instance_path = instance_path self.config = self.make_config() self.init_finished = False self.p = None self.p_flag = True self.r = None self.r_flag = True self.work_mode = work_mode _app_context_ctx.push(self.name, self)
def __init__(self, name: Text, import_name, action_class: Action = None, engine_method: str = "thread", logger_class=None, logger_config_path=None, refresh: bool = False, risk=None, instance_path=None): """ 初始化 """ self.name = name if name else 'ctpbee' self.import_name = import_name self.engine_method = engine_method self.refresh = refresh self.active = False # 是否加载以使用默认的logger类/ choose if use the default logging class if logger_class is None: self.logger = VLogger(CP, app_name=self.name) self.logger.set_default(name=self.logger.app_name, owner='App') else: if logger_config_path: self.logger = logger_class(logger_config_path, app_name=self.name) else: self.logger = logger_class(CP, app_name=self.name) self.logger.set_default(name=self.logger.app_name, owner='App') if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持 thread 和 async,请检查代码") """ If no risk is specified by default, set the risk_decorator to None 如果默认不指定action参数, 那么使用设置风控装饰器为空 """ if risk is None: self.risk_decorator = None else: self.risk_decorator = risk """ If no action is specified by default, use the default Action class 如果默认不指定action参数, 那么使用默认的Action类 """ if action_class is None: self.action = Action(self) else: self.action = action_class(self) """ 根据action里面的函数更新到CtpBee上面来 bind the function of action to CtpBee """ """ update """ if self.risk_decorator is not None: self.risk_decorator.update_app(self) for x in dir(self.action): func = getattr(self.action, x) if x.startswith("__"): continue if ismethod(func): setattr(self, func.__name__, func) """ If engine_method is specified by default, use the default EventEngine and Recorder or use the engine and recorder basis on your choice 如果不指定engine_method参数,那么使用默认的事件引擎 或者根据你的参数使用不同的引擎和记录器 """ if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.' ) self.instance_path = instance_path self.config = self.make_config() self.init_finished = False # default monitor and flag self.p = None self.p_flag = True self.r = None self.r_flag = True _app_context_ctx.push(self.name, self)
class CtpBee(object): """ ctpbee 源于我对于做项目的痛点需求, 旨在开发出一套具有完整api的交易微框架 I hope it will help you ! """ # 默认回测配置参数 default_params = { 'cash': 10000.0, 'check_submit': True, 'eos_bar': False, 'filler': None, "commision": 0.01, # slippage options 'slip_percent': 0.0, 'slip_fixed': 0.0, 'slip_open': False, 'slip_match': True, 'slip_limit': True, 'slip_out': False, 'coc': False, 'coo': False, 'int2pnl': True, 'short_cash': True, 'fund_start_val': 100.0, 'fund_mode': False } default_config = ImmutableDict( dict(LOG_OUTPUT=True, # 是否开启输出模式 TD_FUNC=False, # 是否开启交易功能 INTERFACE="ctp", # 接口参数,默认指定国内期货ctp MD_FUNC=True, # 是否开启行情功能 XMIN=[], # k线序列周期, 支持一小时以内的k线任意生成 ALL_SUBSCRIBE=False, SHARE_MD=False, # 是否多账户之间共享行情,---> 等待完成 SLIPPAGE_COVER=0, # 平多头滑点设置 SLIPPAGE_SELL=0, # 平空头滑点设置 SLIPPAGE_SHORT=0, # 卖空滑点设置 SLIPPAGE_BUY=0, # 买多滑点设置 LOOPER_PARAMS=default_params, # 回测需要设置的参数 SHARED_FUNC=False, # 分时图数据 --> 等待优化 REFRESH_INTERVAL=1.5, # 定时刷新秒数, 需要在CtpBee实例化的时候将refresh设置为True才会生效 INSTRUMENT_INDEPEND=False, # 是否开启独立行情,策略对应相应的行情 CLOSE_PATTERN="today", # 面对支持平今的交易所,优先平今或者平昨 ---> today: 平今, yesterday: 平昨, 其他:处罚异常 TODAY_EXCHANGE=[Exchange.SHFE.value, Exchange.INE.value], # 需要支持平今的交易所代码列表 AFTER_TIMEOUT=3, # 设置after线程执行超时 )) config_class = Config import_name = None # 交易api与行情api / trade api and market api market = None trader = None # 插件Api系统 /Extension system extensions = {} # 工具, 用于提供一些比较优秀的工具/ Toolbox, using by providing some good tools tools = {} def __init__(self, name: Text, import_name, action_class: Action = None, engine_method: str = "thread", logger_class=None, logger_config_path=None, refresh: bool = False, risk=None, instance_path=None): """ 初始化 """ self.name = name if name else 'ctpbee' self.import_name = import_name self.engine_method = engine_method self.refresh = refresh self.active = False # 是否加载以使用默认的logger类/ choose if use the default logging class if logger_class is None: self.logger = VLogger(CP, app_name=self.name) self.logger.set_default(name=self.logger.app_name, owner='App') else: if logger_config_path: self.logger = logger_class(logger_config_path, app_name=self.name) else: self.logger = logger_class(CP, app_name=self.name) self.logger.set_default(name=self.logger.app_name, owner='App') if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持 thread 和 async,请检查代码") """ If no risk is specified by default, set the risk_decorator to None 如果默认不指定action参数, 那么使用设置风控装饰器为空 """ if risk is None: self.risk_decorator = None else: self.risk_decorator = risk """ If no action is specified by default, use the default Action class 如果默认不指定action参数, 那么使用默认的Action类 """ if action_class is None: self.action = Action(self) else: self.action = action_class(self) """ 根据action里面的函数更新到CtpBee上面来 bind the function of action to CtpBee """ """ update """ if self.risk_decorator is not None: self.risk_decorator.update_app(self) for x in dir(self.action): func = getattr(self.action, x) if x.startswith("__"): continue if ismethod(func): setattr(self, func.__name__, func) """ If engine_method is specified by default, use the default EventEngine and Recorder or use the engine and recorder basis on your choice 如果不指定engine_method参数,那么使用默认的事件引擎 或者根据你的参数使用不同的引擎和记录器 """ if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.' ) self.instance_path = instance_path self.config = self.make_config() self.init_finished = False # default monitor and flag self.p = None self.p_flag = True self.r = None self.r_flag = True _app_context_ctx.push(self.name, self) def update_action_class(self, action_class): if isinstance(action_class, Action): raise TypeError(f"更新action_class出现错误, 你传入的action_class类型为{type(action_class)}") self.action = action_class() def update_risk_gateway(self, gateway_class): self.risk_decorator = gateway_class self.risk_decorator.update_app(self) def make_config(self): """ 生成class类""" defaults = dict(self.default_config) return self.config_class(self.instance_path, defaults) def auto_find_instance_path(self): prefix, package_path = find_package(self.import_name) if prefix is None: return os.path.join(package_path) return os.path.join(prefix, 'var', self.name + '-instance') @property def td_login_status(self): """ 交易 API 都应该实现td_status""" return self.trader.td_status @property def md_login_status(self): """ 行情 API 都应该实现md_status""" return self.market.md_status def _load_ext(self): """根据当前配置文件下的信息载入行情api和交易api,记住这个api的选项是可选的""" self.active = True if "CONNECT_INFO" in self.config.keys(): info = self.config.get("CONNECT_INFO") else: raise ConfigError(message="没有相应的登录信息", args=("没有发现登录信息",)) MdApi, TdApi = Interface.get_interface(self) if self.config.get("MD_FUNC"): self.market = MdApi(self.event_engine) self.market.connect(info) if self.config.get("TD_FUNC"): if self.config['INTERFACE'] == "looper": self.trader = TdApi(self.event_engine, self) else: self.trader = TdApi(self.event_engine) self.trader.connect(info) show_me = graphic_pattern(__version__, self.engine_method) print(show_me) if self.refresh: if self.r is not None: self.r_flag = False sleep(self.config['REFRESH_INTERVAL'] + 1.5) self.r = Thread(target=refresh_query, args=(self,),daemon=True) self.r.start() else: self.r = Thread(target=refresh_query, args=(self,), daemon=True) self.r.start() self.r_flag = True @locked_cached_property def name(self): if self.import_name == '__main__': fn = getattr(sys.modules['__main__'], '__file__', None) if fn is None: return '__main__' return os.path.splitext(os.path.basename(fn))[0] return self.import_name def start(self, log_output=True, debug=False): """ 开启处理 :param log_output: 是否输出log信息 :param debug: 是否开启调试模式 ----> 等待完成 :return: """ if not self.event_engine.status: self.event_engine.start() self.config["LOG_OUTPUT"] = log_output self._load_ext() def stop(self): """ 停止运行 """ if self.event_engine.status: self.event_engine.stop() def remove_extension(self, extension_name: Text) -> None: """移除插件""" if extension_name in self.extensions: del self.extensions[extension_name] def add_extension(self, extension: CtpbeeApi): """添加插件""" self.extensions.pop(extension.extension_name, None) extension.init_app(self) self.extensions[extension.extension_name] = extension def suspend_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = True return True def enable_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = False return True def del_extension(self, extension_name): self.extensions.pop(extension_name, None) def reload(self): """ 重新载入接口 """ if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() # 清空处理队列 self.event_engine._queue.empty() sleep(3) self.market, self.trader = None, None self._load_ext() def release(self): """ 释放账户,安全退出 """ try: if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() self.market, self.trader = None, None self.event_engine.stop() del self.event_engine if self.r is not None: """ 强行终结掉线程 """ end_thread(self.r) except AttributeError: pass
class CtpBee(object): """ ctpbee 源于我对于做项目的痛点需求, 旨在开发出一套具有完整api的交易微框架 , 在这里你可以看到很多借鉴自flask的设计 , 毕竟本人实在热爱flask .... ctpbee提供完整的支持 ,一个CtpBee对象可以登录一个账户 , 当你登录多个账户时, 可以通过current_app, 以及switch_app还有 get_app提供完整的支持, 每个账户对象都拥有单独的发单接口 ,在你实现策略的地方 可以通过上述api实现发单支持, 当然ctpbee提供了CtpbeeApi 抽象插件 ,继承此插件即可快速载入支持. 总而言之,希望能够极大的简化目前的开发流程 ! """ # 默认配置 default_config = ImmutableDict( dict(LOG_OUTPUT=True, TD_FUNC=False, INTERFACE="ctp", MD_FUNC=True, XMIN=[], ALL_SUBSCRIBE=False, SHARE_MD=False, ENGINE_METHOD="thread")) config_class = Config import_name = None # 数据记录载体 __active = False # 交易api与行情api market = None trader = None # 插件系统 # todo :等共享内存块出来了 是否可以尝试在外部进行 extensions = {} def __init__(self, name: Text, import_name, engine_method: str = "thread", instance_path=None): """ 初始化 """ self.name = name self.import_name = import_name if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持thread和async,请检查代码") if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.') self.risk_gateway = RiskLevel(self) self.instance_path = instance_path self.config = self.make_config() self.interface = Interface() _app_context_ctx.push(self.name, self) def make_config(self): """ 生成class类""" defaults = dict(self.default_config) return self.config_class(self.instance_path, defaults) def auto_find_instance_path(self): prefix, package_path = find_package(self.import_name) if prefix is None: return os.path.join(package_path) return os.path.join(prefix, 'var', self.name + '-instance') @property def td_login_status(self): """ 交易 API 都应该实现td_status""" return self.trader.td_status @property def md_login_status(self): """ 行情 API 都应该实现md_status""" return self.market.md_status def _load_ext(self): """根据当前配置文件下的信息载入行情api和交易api,记住这个api的选项是可选的""" self.__active = True if "CONNECT_INFO" in self.config.keys(): info = self.config.get("CONNECT_INFO") else: raise ConfigError(message="没有相应的登录信息", args=("没有发现登录信息", )) MdApi, TdApi = self.interface.get_interface(self) if self.config.get("MD_FUNC"): self.market = MdApi(self.event_engine) self.market.connect(info) if self.config.get("TD_FUNC"): self.trader = TdApi(self.event_engine) self.trader.connect(info) sleep(0.5) @locked_cached_property def name(self): if self.import_name == '__main__': fn = getattr(sys.modules['__main__'], '__file__', None) if fn is None: return '__main__' return os.path.splitext(os.path.basename(fn))[0] return self.import_name def start(self, log_output=True): """开始""" if not self.event_engine.status: self.event_engine.start() self.config["LOG_OUTPUT"] = log_output self._load_ext() def stop(self): """ 停止运行 """ if self.event_engine.status: self.event_engine.stop() @check(type="trader") def send_order(self, order_req: OrderRequest) -> AnyStr: """发单""" result = self.risk_gateway.send(self) if False in result: event = Event(type=EVENT_LOG, data="风控阻止下单") self.event_engine.put(event) return send_monitor.send(order_req) return self.trader.send_order(order_req) @check(type="trader") def cancel_order(self, cancle_req: CancelRequest): """撤单""" cancle_monitor.send(cancle_req) self.trader.cancel_order(cancle_req) @check(type="market") def subscribe(self, symbol: AnyStr): """订阅行情""" if "." in symbol: symbol = symbol.split(".")[1] return self.market.subscribe(symbol) @check(type="trader") def query_position(self): """查询持仓""" return self.trader.query_position() @check(type="trader") def transfer(self, req, type): """ req currency attribute ["USD", "HKD", "CNY"] :param req: :param type: :return: """ self.trader.transfer(req, type=type) @check(type="trader") def query_account_register(self, req): self.trader.query_account_register(req) @check(type="trader") def query_bank_account_money(self, req): self.trader.query_bank_account_money(req) @check(type="trader") def query_transfer_serial(self, req): self.trader.query_transfer_serial(req) @check(type="trader") def query_bank(self): pass @check(type="trader") def query_account(self): """查询账户""" return self.trader.query_account() def remove_extension(self, extension_name: Text) -> None: """移除插件""" if extension_name in self.extensions: del self.extensions[extension_name] def add_extension(self, extension: CtpbeeApi): """添加插件""" if extension.extension_name in self.extensions: return extension.init_app(self) self.extensions[extension.extension_name] = extension def suspend_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = True return True def enable_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = False return True def reload(self): """ 重新载入接口 """ if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() self._load_ext() def __del__(self): """释放账户 安全退出""" print("注销") if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() self.market, self.trader = None, None del self.event_engine
class CtpBee(object): """ ctpbee 源于我对于做项目的痛点需求, 旨在开发出一套具有完整api的交易微框架 I hope it will help you ! """ # 默认回测配置参数 default_params = { 'cash': 10000.0, 'check_submit': True, 'eos_bar': False, 'filler': None, "commision": 0.01, # slippage options 'slip_percent': 0.0, 'slip_fixed': 0.0, 'slip_open': False, 'slip_match': True, 'slip_limit': True, 'slip_out': False, 'coc': False, 'coo': False, 'int2pnl': True, 'short_cash': True, 'fund_start_val': 100.0, 'fund_mode': False } default_config = ImmutableDict( dict(LOG_OUTPUT=True, TD_FUNC=False, INTERFACE="ctp", MD_FUNC=True, XMIN=[], ALL_SUBSCRIBE=False, SHARE_MD=False, ENGINE_METHOD="thread", LOOPER_SETTING=default_params, SHARED_FUNC=False, REFRESH_INTERVAL=1.5)) config_class = Config import_name = None __active = False # 交易api与行情api market = None trader = None # 插件Api系统 extensions = {} # 工具, 用于提供一些比较优秀的工具 tools = {} def __init__(self, name: Text, import_name, engine_method: str = "thread", work_mode="limit_time", refresh: bool = False, instance_path=None): """ 初始化 """ self.name = name self.import_name = import_name self.engine_method = engine_method self.refresh = refresh if engine_method == "thread": self.event_engine = EventEngine() self.recorder = Recorder(self, self.event_engine) elif engine_method == "async": self.event_engine = AsyncEngine() self.recorder = AsyncRecorder(self, self.event_engine) else: raise TypeError("引擎参数错误,只支持 thread 和 async,请检查代码") if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( 'If an instance path is provided it must be absolute.' ' A relative path was given instead.') self.risk_gateway_class = None self.instance_path = instance_path self.config = self.make_config() self.init_finished = False self.p = None self.p_flag = True self.r = None self.r_flag = True self.work_mode = work_mode _app_context_ctx.push(self.name, self) def add_risk_gateway(self, gateway_class, risk=True): self.risk_gateway_class = gateway_class self.risk_gateway_class.update_app(self) if risk: self.send_order = self.risk_gateway_class(self.send_order) self.cancel_order = self.risk_gateway_class(self.cancel_order) def make_config(self): """ 生成class类""" defaults = dict(self.default_config) return self.config_class(self.instance_path, defaults) def auto_find_instance_path(self): prefix, package_path = find_package(self.import_name) if prefix is None: return os.path.join(package_path) return os.path.join(prefix, 'var', self.name + '-instance') @property def td_login_status(self): """ 交易 API 都应该实现td_status""" return self.trader.td_status @property def md_login_status(self): """ 行情 API 都应该实现md_status""" return self.market.md_status def _load_ext(self): """根据当前配置文件下的信息载入行情api和交易api,记住这个api的选项是可选的""" self.__active = True if "CONNECT_INFO" in self.config.keys(): info = self.config.get("CONNECT_INFO") else: raise ConfigError(message="没有相应的登录信息", args=("没有发现登录信息", )) MdApi, TdApi = Interface.get_interface(self) if self.config.get("MD_FUNC"): self.market = MdApi(self.event_engine) self.market.connect(info) if self.config.get("TD_FUNC"): if self.config['INTERFACE'] == "looper": self.trader = TdApi(self.event_engine, self) else: self.trader = TdApi(self.event_engine) self.trader.connect(info) show_me = \ f""" {"*" * 60} * * * ------------------------------------- * * | | * * | ctpbee: {__version__.ljust(16, " ")} | * * | work_mode: {self.work_mode.ljust(16, " ")} | * * | engine: {self.engine_method.ljust(16, " ")} | * * | | * * ------------------------------------- * * * {"*" * 60} """ print(show_me) # 检查work_mode if self.work_mode == "forever": """ 7×24小时 """ # 启动监视器 if self.p is not None: self.p_flag = False sleep(1.5) self.p = Thread(target=run_forever, args=(self, )) self.p.start() else: self.p = Thread(target=run_forever, args=(self, )) self.p.start() self.p_flag = True else: pass if self.refresh: if self.r is not None: self.r_flag = False sleep(self.config['REFRESH_INTERVAL'] + 1.5) self.r = Thread(target=refresh_query, args=(self, )) self.r.start() else: self.r = Thread(target=refresh_query, args=(self, )) self.r.start() self.r_flag = True self.p_flag = True @locked_cached_property def name(self): if self.import_name == '__main__': fn = getattr(sys.modules['__main__'], '__file__', None) if fn is None: return '__main__' return os.path.splitext(os.path.basename(fn))[0] return self.import_name def start(self, log_output=True): """开始""" if not self.event_engine.status: self.event_engine.start() self.config["LOG_OUTPUT"] = log_output self._load_ext() def stop(self): """ 停止运行 """ if self.event_engine.status: self.event_engine.stop() @check(type="trader") def send_order(self, order_req: OrderRequest) -> AnyStr: """发单""" send_monitor.send(order_req) return self.trader.send_order(order_req) @check(type="trader") def cancel_order(self, cancle_req: CancelRequest): """撤单""" cancel_monitor.send(cancle_req) return self.trader.cancel_order(cancle_req) @check(type="market") def subscribe(self, symbol: AnyStr): """订阅行情""" if "." in symbol: symbol = symbol.split(".")[1] return self.market.subscribe(symbol) @check(type="trader") def query_position(self): """查询持仓""" return self.trader.query_position() @check(type="trader") def transfer(self, req, type): """ req currency attribute ["USD", "HKD", "CNY"] :param req: :param type: :return: """ self.trader.transfer(req, type=type) @check(type="trader") def query_account_register(self, req): self.trader.query_account_register(req) @check(type="trader") def query_bank_account_money(self, req): self.trader.query_bank_account_money(req) @check(type="trader") def query_transfer_serial(self, req): self.trader.query_transfer_serial(req) @check(type="trader") def query_bank(self): pass @check(type="trader") def query_account(self): """查询账户""" return self.trader.query_account() def remove_extension(self, extension_name: Text) -> None: """移除插件""" if extension_name in self.extensions: del self.extensions[extension_name] def add_extension(self, extension: CtpbeeApi): """添加插件""" if extension.extension_name in self.extensions: return extension.init_app(self) self.extensions[extension.extension_name] = extension def suspend_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = True return True def enable_extension(self, extension_name): extension = self.extensions.get(extension_name, None) if not extension: return False extension.frozen = False return True def reload(self): """ 重新载入接口 """ if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() self._load_ext() def __del__(self): """释放账户 安全退出""" print("注销") if self.market is not None: self.market.close() if self.trader is not None: self.trader.close() self.market, self.trader = None, None del self.event_engine