class OnePiece(object): env = Environment() def __init__(self): self.market_maker = MarketMaker() self.order_checker = PendingOrderChecker() self.cur_event = None self.env.logger = logging.getLogger("OnePy") def sunny(self, summary=True): """主循环,OnePy的核心""" self._initialize_trading_system() while True: try: self.cur_event = self.env.event_bus.get() except queue.Empty: try: self.market_maker.update_market() self.order_checker.run() except BacktestFinished: self.output.summary() if summary else None break else: self._run_event_loop() def _run_event_loop(self): for element in self.env.event_loop: if self._event_is_executed(**element): break def _event_is_executed(self, if_event, then_event, module_dict): if self.cur_event.event_type == if_event: [value.run() for value in module_dict.values()] self.env.event_bus.put(Event(then_event)) if then_event else None return True def _initialize_trading_system(self): self.env.refresh() OnePyEnvBase.env = self.env self.env.event_loop = EVENT_LOOP self.market_maker.initialize() if self.env.recorder: self.env.recorder.initialize() @property def output(self): return OutPut() def show_log(self, file=False): if file: LoggerFactory("OnePy").logger logging.basicConfig(level=logging.INFO)
def test_frequency_generate(): # 若不指定 frequency, 是否会产生新的 frequency set_easy_context() StockRecorder() reader = CSVReader(data_path='./', file_name=TICKER, ticker=TICKER) MarketMaker._initialize_feeds() MarketMaker._initialize_calendar() cleaner = SMA(rolling_window=10, buffer_day=20) cleaner.initialize_buffer_data(TICKER, buffer_day=20) assert cleaner.frequency == FREQUENCY
def func_test_update_cleaner(frequency): set_easy_context() StockRecorder() reader = CSVReader(data_path='./', file_name=TICKER, ticker=TICKER) cleaner = SMA(rolling_window=10, buffer_day=10, frequency=frequency) cleaner2 = SMA(rolling_window=10, buffer_day=1, frequency=frequency) env = reader.env # 初始化 MarketMaker._initialize_feeds() MarketMaker._initialize_calendar() MarketMaker._initialize_cleaners() # 以sys_date初始化 # 以 sys_date 来进行初始化数据,因为一开始 sys_date 会比 fromdate 向前一步 # 测试开始回测时 buffer 数据最新一条是否为 Start 前一根 bar 的数据。 key = f'{TICKER}_{frequency}' assert arrow.get(cleaner.data[key]['date'][-1]) <= arrow.get(env.sys_date) # sys_date和cleaner中的数据都更新到和fromdate同步 MarketMaker.calendar.update_calendar() MarketMaker._update_bar() cleaner.run() if INSTRUMENT == 'A_Shares': # 测试buffer_day是否会自动变化 assert cleaner2.buffer_day == 5 if cleaner.frequency == env.sys_frequency: cur_bar = env.feeds[TICKER] else: cur_bar = env.cleaners_feeds[key + '_' + cleaner.name] # cleaner run之后日期一切正常 latest_date = cleaner.data[key]['date'][-1] assert latest_date == cur_bar.date, f'{latest_date} != {cur_bar.date}' # 测试更新后是否成功 env.execute_on_close_or_next_open = 'open' env.cur_suspended_tickers.clear() MarketMaker.calendar.update_calendar() MarketMaker._update_bar() cleaner.run() latest_date = cleaner.data[key]['date'][-1] assert latest_date == cur_bar.date, f'{latest_date} != {cur_bar.date}' assert cleaner.data[key]['open'][-1] == cur_bar.current_ohlc['open'] assert cleaner.data[key]['high'][-1] == cur_bar.current_ohlc['high'] assert cleaner.data[key]['low'][-1] == cur_bar.current_ohlc['low'] assert cleaner.data[key]['close'][-1] == cur_bar.current_ohlc['close'] assert cleaner.data[key]['volume'][-1] == cur_bar.current_ohlc['volume']
def _save_cleaners_feeds(self, ticker: str): key = f'{ticker}_{self.frequency}_{self.name}' value = MarketMaker.get_bar(ticker, self.frequency) if value.initialize(7): self.env.cleaners_feeds.update({key: value})
def func_test_reader(reader: op.ReaderBase): def test_load(start, end, iter_data): first_date = next(iter_data)['date'] assert isinstance(first_date, str), "it should be iterable" if FREQUENCY == 'H1': shift_num = 1 / 24 elif FREQUENCY == 'D': shift_num = 1 next_date = shift_date(first_date, shift_num) assert next(iter_data)['date'] == next_date last_date = [i for i in iter_data][-1]['date'] assert arrow.get(last_date) <= arrow.get( end), f"{last_date} can't more than {END}" StockRecorder() go = op.OnePiece() go.set_date(START, END, FREQUENCY, INSTRUMENT) test_iter_data = reader.load(START, END, FREQUENCY) test_load(START, END, test_iter_data) NEW_START = shift_date(START, -9) NEW_END = shift_date(NEW_START, 10) test_iter_data = reader.load_by_cleaner(NEW_START, NEW_END, FREQUENCY) test_load(NEW_START, NEW_END, test_iter_data) # 获取bar bars = MarketMaker.get_bar(TICKER, FREQUENCY) assert isinstance(bars, BarBase) assert reader in reader.env.readers.values()
def test_different_frequency(): set_easy_context() StockRecorder() reader = CSVReader(data_path='./', file_name=TICKER, ticker=TICKER) MarketMaker._initialize_feeds() MarketMaker._initialize_calendar() cleaner = SMA(rolling_window=10, buffer_day=20, frequency=FREQUENCY) cleaner.initialize_buffer_data(TICKER, buffer_day=20) key = f'{TICKER}_{FREQUENCY}' # 测试不同 frequency 是否会新建 cleaner feed assert key + '_' + cleaner.name in reader.env.cleaners_feeds # 低 frequency 的 cleaners feed 中 bar 的更新不超过系统 bar 时间 MarketMaker.env.cur_suspended_tickers.clear() MarketMaker.calendar.update_calendar() MarketMaker._update_bar() cleaner.run() cleaner_latest_date = cleaner.data[key]['date'][-1] bar_latest_date = cleaner.env.feeds[TICKER].date assert arrow.get(cleaner_latest_date) == arrow.get(bar_latest_date) assert arrow.get(cleaner_latest_date) <= arrow.get(cleaner.env.sys_date) func_test_update_cleaner(FREQUENCY) func_test_update_cleaner('H1')
def _pre_initialize_trading_system(self): self.event_loop = EVENT_LOOP self.market_maker = MarketMaker() self.pending_order_checker = PendingOrderChecker()
class OnePiece(OnePyEnvBase): def __init__(self): # 内置模块 self.market_maker: MarketMaker = None self.pending_order_checker: PendingOrderChecker = None self.event_loop: list = None # 其他模块 self.optimizer = Optimizer() self.forward_analysis = ForwardAnalysis() def _pre_initialize_trading_system(self): self.event_loop = EVENT_LOOP self.market_maker = MarketMaker() self.pending_order_checker = PendingOrderChecker() def initialize_trading_system(self): # 清空内存,便于参数优化 self._pre_initialize_trading_system() self.env.initialize_env() self.market_maker.initialize() self.env.recorder.initialize() def sunny(self, summary: bool = True, show_process: bool = False): """主循环,OnePy的核心""" self.initialize_trading_system() while True: try: if self.env.event_engine.is_empty(): self.market_maker.update_market() self.pending_order_checker.run() if show_process: self._show_process() else: cur_event = self.env.event_engine.get() self._run_event_loop(cur_event) except BacktestFinished: if summary: print("\n") self.output.summary() break def _run_event_loop(self, cur_event): for element in self.event_loop: if self._event_is_executed(cur_event, **element): break def _event_is_executed(self, cur_event, if_event: EVENT, then_event: EVENT, module_dict: dict) -> bool: if cur_event is None: return True elif cur_event == if_event: [value.run() for value in module_dict.values()] self.env.event_engine.put(then_event) return True else: return False def _show_process(self): fromdate = arrow.get(self.env.fromdate) todate = arrow.get(self.env.todate) curdate = arrow.get(self.env.sys_date) total_days = (todate - fromdate).days finished_days = (curdate - fromdate).days show_process(finished_days, total_days) def set_date(self, fromdate: str, todate: str, frequency: str, instrument: str): """ Instrument: A_shares, Forex Frequency: (S5, S10, S30, M1, M2, M4, M5) <- BAD Interval M10, M15, M30, H1, H2, H3, H4, H6, H8, H12 """ self.env.instrument = instrument self.env.fromdate = fromdate self.env.todate = todate self.env.sys_frequency = frequency def set_forex_live_trading(self, frequency: str): """ Frequency: (S5, S10, S30, M1, M2, M4, M5) <- BAD Interval M10, M15, M30, H1, H2, H3, H4, H6, H8, H12 """ fromdate = arrow.utcnow().format("YYYY-MM-DD HH:mm:ss") self.set_date(fromdate, None, frequency, "Forex") self.env.sys_date = fromdate self.env.is_live_trading = True def show_today_signals(self): """ 能够显示当天的最新信号,但是会导致回测结果不准确。 """ self.env.is_show_today_signals = True @classmethod def show_log(cls, file=False, no_console=False): if file: LoggerFactory("OnePy") if no_console: logging.getLogger("OnePy").propagate = False logging.basicConfig(level=logging.INFO) @classmethod def set_recursion_limit(cls, limit: int = 2000): """ 突破递归次数限制,有时候信号太多会导致撮合引擎递归太多次而假死 """ sys.setrecursionlimit(limit) def save_original_signal(self): self.env.is_save_original = True @property def output(self) -> OutPut: return OutPut()
def __init__(self): self.market_maker = MarketMaker() self.order_checker = PendingOrderChecker() self.cur_event = None self.env.logger = logging.getLogger("OnePy")