def __init__(self, instruments, frequency=Frequency.MINUTE, table_name='bins', file_name='sqlite'): self.__engine = create_engine(f'sqlite:///{file_name}') self.__DBSession = sessionmaker(bind=self.__engine) self.__logger = get_logger(SQLiteFeed.LOGGER_NAME) if table_name in table_names.keys(): self.__bar_model = table_names[table_name] else: self.__bar_model = make_bar_model(table_name) table_names[table_name] = self.__bar_model def before_cursor_execute(conn, cursor, statement, parameters, context, executemany): conn.info.setdefault('query_start_time', []).append(time.time()) self.__logger.debug( f'Start Query: {statement} with params: {parameters}') def after_cursor_execute(conn, cursor, statement, parameters, context, executemany): total = time.time() - conn.info['query_start_time'].pop(-1) self.__logger.debug("Query Complete!") self.__logger.debug("Total Time: %f", total) event.listens_for(self.__engine, "before_cursor_execute")(before_cursor_execute) event.listens_for(self.__engine, "after_cursor_execute")(after_cursor_execute) super(SQLiteFeed, self).__init__(frequency, instruments, None, None) self.bars = []
def __init__(self, broker: BaseBroker, series_max_len=None): self.__broker = broker self.__activePositions = set() self.__orderToPosition = {} self.__barsProcessedEvent = Event() self.__analyzers = [] self.__namedAnalyzers = {} self.__resampledBarFeeds = [] self.__dispatcher = Dispatcher() self.__broker.order_events.subscribe(self.__onOrderEvent) self.bar_feed.bar_events.subscribe(self.__onBars) self.bar_feed.feed_reset_event.subscribe(self.reset) # onStart will be called once all subjects are started. self.__dispatcher.getStartEvent().subscribe(self.onStart) self.__dispatcher.getIdleEvent().subscribe(self.__onIdle) # It is important to dispatch broker events before feed events, specially if we're backtesting. self.__dispatcher.addSubject(self.broker) self.__dispatcher.addSubject(self.bar_feed) # Initialize logging. self.__logger = logger.get_logger(BaseStrategy.LOGGER_NAME) self.use_event_datetime_logs = True self.__onEnterOkEvent = Event() self.__onEnterCanceledEvent = Event() self.__onExitOkEvent = Event() self.__onExitCanceledEvent = Event() self.__onEnterOkEvent.subscribe(self.onEnterOk) self.__onEnterCanceledEvent.subscribe(self.onEnterCanceled) self.__onExitOkEvent.subscribe(self.onExitOk) self.__onExitCanceledEvent.subscribe(self.onExitCanceled) self.__onEnterStartEvent = Event() self.__onExitStartEvent = Event() """ 我们尝试把数据序列记在策略中 TODO: 对于离线的回测,这个过程可以利用本来的数据 """ instruments = broker.instruments self.bar_series = dict() self.max_series_length = series_max_len for instrument in instruments: self.bar_series[instrument] = BarDataSeries(max_len=series_max_len) """ 反正都要用,为何不把所有Positions管理起来呢 """ self.__positions = [] self.__onEnterStartEvent.subscribe(self.__on_enter_start)
def __init__(self, path, instruments, frequency=Frequency.MINUTE, maxLen=None): super(DataFrameFeed, self).__init__(frequency, instruments, None, maxLen) self.store = pd.HDFStore(path) self.__logger = get_logger("DATAFrameFeed Logger")
def __init__(self, table_name='bars', db_name='database', db_username='******', db_password='******', connector='mysqldb', db_host='127.0.0.1', db_port=3306): self.__engine = create_engine( f'mysql+{connector}://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}' ) self.__DBSession = sessionmaker(bind=self.__engine) self.__logger = get_logger(MySQLFeed.LOGGER_NAME) self.__bar_model = make_bar_model(table_name) def before_cursor_execute(conn, cursor, statement, parameters, context, executemany): conn.info.setdefault('query_start_time', []).append(time.time()) self.__logger.debug( f'Start Query: {statement} with params: {parameters}') def after_cursor_execute(conn, cursor, statement, parameters, context, executemany): total = time.time() - conn.info['query_start_time'].pop(-1) self.__logger.debug("Query Complete!") self.__logger.debug("Total Time: %f", total) event.listens_for(self.__engine, "before_cursor_execute")(before_cursor_execute) event.listens_for(self.__engine, "after_cursor_execute")(after_cursor_execute) super(MySQLFeed, self).__init__() self.bars = []
""" 想法: 对于每个需要被操作的Indicator,我们把它传入到这里,然后再将它attach上去。 每笔交易之前我们都可以记录Indicator的数值,然后记录下来。 然后当交易结束后,则得到一个交易与其之前的Indicator的一一对应 当然,有四个时间点,分别是: 入场前、入场后、出场前、出场后 每个时间点都对应有一堆indicator的数值 """ logger = get_logger('indicator_analyz') class Indicators(stratanalyzer.StrategyAnalyzer): """ indicators: 你所打算处理的Indicator的一个字典. 注意需要传入一个Generator,而不是实例,因为Indicator将会在attach之前被给上priceDS instrument: 标的 这里用来分析具体针对某种策略的indicator """ def __init__(self, indicators: dict, instrument): super(Indicators, self).__init__() self.__priceDS = None
class OrderBook(stratanalyzer.StrategyAnalyzer): l = get_logger('OrderBook') def to_df(self): self.l.debug('Converting The Order Book...') positions = [] for position in self.strat.positions: dict = { "instrument": None, "return": None, "PnL": None, "entry_state": None, "entry_type": None, "entry_fill_price": None, "entry_amounts": None, "entry_filled": None, "entry_submitted_at": None, "entry_accepted_at": None, "entry_finished_at": None, "entry_canceled_at": None, "exit_state": None, "exit_type": None, "exit_fill_price": None, "exit_submitted_at": None, "exit_accepted_at": None, "exit_finished_at": None, "exit_canceled_at": None, "exit_amounts": None, "exit_filled": None, "not_filled": None, } entry_order: Order = position.getEntryOrder() dict["instrument"] = position.getInstrument() dict["entry_state"] = entry_order.state dict["amounts"] = position.getAmounts() dict["entry_submitted_at"] = entry_order.submitted_at dict["entry_accepted_at"] = entry_order.accepted_at dict["entry_finished_at"] = entry_order.finish_datetime dict["entry_canceled_at"] = entry_order.canceled_at dict["entry_type"] = entry_order.type dict["entry_amounts"] = entry_order.quantity dict["entry_filled"] = entry_order.filled if entry_order.is_filled: dict["entry_fill_price"] = position.getEntryOrder( ).avg_fill_price exit_order: Order = position.getExitOrder() if exit_order is not None: dict["exit_submitted_at"] = exit_order.submitted_at dict["exit_accepted_at"] = exit_order.accepted_at dict["exit_canceled_at"] = exit_order.canceled_at dict["exit_finished_at"] = exit_order.finish_datetime dict["exit_type"] = exit_order.type dict["exit_state"] = exit_order.state dict["exit_amounts"] = exit_order.quantity dict["exit_filled"] = exit_order.filled if exit_order.is_filled: dict["exit_fill_price"] = exit_order.avg_fill_price dict["not_filled"] = position.getAmounts() dict["PnL"] = position.getPnL() dict["return"] = position.getReturn() positions.append(dict) dict = { "instrument": None, "return": None, "PnL": None, "entry_state": None, "entry_type": None, "entry_fill_price": None, "entry_amounts": None, "entry_filled": None, "entry_submitted_at": None, "entry_accepted_at": None, "entry_finished_at": None, "entry_canceled_at": None, "exit_state": None, "exit_type": None, "exit_fill_price": None, "exit_submitted_at": None, "exit_accepted_at": None, "exit_finished_at": None, "exit_canceled_at": None, "exit_amounts": None, "exit_filled": None, "not_filled": None, } df = pd.DataFrame(positions, columns=dict.keys()) return df def save_csv(self, filename): return self.to_df().to_csv(filename)
class ResultDrawer: logger = get_logger('Drawer Logger') def __init__(self, results: [ResultModel], dpi: int = 128): self.__results = results self.__dpi = dpi def __draw_contour(self, ax2, low_bound=None, upper_bound=None, param_name="sharp_ratio"): def judge(v): rtv = True if low_bound is not None: rtv = rtv and low_bound <= v if upper_bound is not None: rtv = rtv and v <= upper_bound return rtv new_line = [np.array([line.p1, line.p2, getattr(line, param_name)]) for line in self.__results if judge(line.sharp_ratio)] if len(new_line) <= 4: self.logger.debug('NO RESULTS SELECTED') return new_line = np.array(new_line) points = new_line[:, 0:2] x = points[:, 0] y = points[:, 1] z = new_line[:, 2] ax2.tricontour(x, y, z, levels=14, linewidths=0.5, colors='k') cntr2 = ax2.tricontourf(x, y, z, levels=14, cmap="coolwarm") ax2.plot(x, y, 'ko', ms=0.1) ax2.set_title(f'(p1,p2) and {param_name}') ax2.locator_params(nbins=40) ax2.grid() return cntr2 # p.subplots_adjust(hspace=0.5) def draw_contour(self, save_path, low_bound=None, upper_bound=None, param_name=None): fig = plt.figure(dpi=self.__dpi) ax = fig.subplots(nrows=1) cntr2 = self.__draw_contour(ax, low_bound, upper_bound, param_name) if cntr2 is None: return fig.colorbar(cntr2, ax=ax) fig.savefig(save_path) def __draw_line(self, ax, low_bound=None, upper_bound=None, param_name="sharp_ratio"): def judge(v): rtv = True if low_bound is not None: rtv = rtv and low_bound <= v if upper_bound is not None: rtv = rtv and v <= upper_bound return rtv new_line = [np.array([line.p1, getattr(line, param_name)]) for line in self.__results if judge(line.sharp_ratio)] new_line = np.array(new_line) if len(new_line) is 0: self.logger.error('NO RESULTS SELECTED') return pair = new_line pair = pair[np.argsort(pair[:, 0])] ax.plot(pair[:, 0], pair[:, 1]) ax.set(xlabel='p1', ylabel=param_name, title=f'p1-{param_name} curve') ax.grid() def draw_line(self, save_path, low_bound=None, upper_bound=None, param_name="sharp_ratio"): fig = plt.figure(dpi=self.__dpi) ax = fig.subplots(nrows=1) self.__draw_line(ax, low_bound, upper_bound, param_name) fig.savefig(save_path) def draw_lines(self, save_path, params=["sharp_ratio", "ret", "win_rate", "trade_count", "draw_down", "plr", "draw_down_duration"]): fig = plt.figure(dpi=self.__dpi) n = len(params) fig.set_figheight(n * 3) fig.set_figwidth(12) axex = fig.subplots(ncols=1, nrows=len(params)) i = 0 for param in params: ax = axex[i] if n > 1 else axex self.__draw_line(ax, param_name=param) i += 1 fig.subplots_adjust(wspace=0.5, hspace=0.5) fig.savefig(save_path) def draw_contours(self, save_path, params=["sharp_ratio", "ret", "win_rate", "trade_count", "draw_down", "plr", "draw_down_duration"]): n = len(params) fig = plt.figure(dpi=self.__dpi, figsize=(25, n * 25)) axex = fig.subplots(ncols=1, nrows=len(params)) i = 0 for param in params: ax = axex[i] if n > 1 else axex cntr2 = self.__draw_contour(ax, param_name=param) i += 1 if cntr2 is not None: fig.colorbar(cntr2, ax=ax) fig.subplots_adjust(wspace=0.5, hspace=0.5) fig.savefig(save_path)