class Algo(Strategy): """ Class that ensemble Account, Strategy, Data and AlgoWeb to run algo trading """ def __init__(self, name: str, log_path='.', benchmark: str = 'HSI'): self.name = name self.benchmark = benchmark self._logger = logger.RootLogger(root_name=self.name, file_path=log_path) self._running = False self._initialized_date = None self._initialized = False self._data = None self._account = None self._execution = None self._webapp = None @try_expt(msg='Initialization Failed', pnt_original=True) def initialize(self, initial_capital: float, mq_ip: str, hook_ip: str, trading_environment: str, trading_universe: list, datatypes: list, txn_cost: float = 30, cache_rows: int = 3000, test_mq_con=True, hook_name: str = 'FUTU', prefill_period='1Y', **kwargs): assert trading_environment in ( 'BACKTEST', 'SIMULATE', 'REAL'), f'Invalid trading environment {trading_environment}' assert initial_capital > 0, 'Initial Capital cannot be 0' assert cache_rows > 1, 'No of cached data must be > 0 rows' self._account = Account(logger=self._logger, initial_capital=initial_capital, txn_cost=txn_cost) self._data = Data(mq_ip=mq_ip, logger=self._logger, hook_ip=hook_ip, trading_universe=trading_universe, datatypes=datatypes, cache_rows=cache_rows, test_mq_con=test_mq_con, hook_name=hook_name, prefill_period=prefill_period, add_pos_func=self._account.add_new_position) self._execution = Execution(account=self._account, data=self._data, trading_environment=trading_environment, logger=self._logger) self._webapp = AlgoApp(algo=self) self._initialized_date = datetime.datetime.today() self._running = False self._logger.debug('Initialized sucessfully.') self._initialized = True async def record_daily_performance(self): while True: self._account.log() await asyncio.sleep(60 * 60 * 24 - time.time() % 60 * 60 * 24) async def main(self): self._running = True self._data.start_sub() self._logger.debug(f'Algo {self.name} is running successfully!') while True: try: topic, bin_df = await self._data.receive_data() if not self._running: continue topic_split = topic.decode('ascii').split('.') datatype = topic_split[1] key = '.'.join(topic_split[2:]) df = pickle.loads(bin_df) if datatype == 'ORDER_UPDATE': self._account.update_positions(df) await self.on_order_update(order_id=key, df=df) else: self._account.update_prices(datatype=datatype, df=df) self._data.add_cache(datatype=datatype, df=df, ticker=key) trigger_strat, (tgr_dtype, tgr_ticker, tgr_df) = self.determine_trigger( datatype=datatype, ticker=key, df=df) if trigger_strat: await self.trigger_strat(datatype=tgr_dtype, ticker=tgr_ticker, df=tgr_df) except Exception as e: self._running = False self._logger.error( f'Exception occur, Algo stopped due to {str(e)}') def run(self, sanic_port, sanic_host='127.0.0.1'): if not self._initialized: self._logger.debug('Algo not initialized') else: loop = asyncio.get_event_loop() async def _run(): tasks = list() web_server = self._webapp.get_coroutine(host=sanic_host, port=sanic_port) tasks.append(web_server) tasks.append(self.main()) tasks.append(self.record_daily_performance()) await asyncio.gather(*tasks) loop.create_task(_run()) loop.run_forever() # ------------------------------------------------ [ Trade API ] ------------------------------------------ def trade(self): pass def buy_market(self, ticker, quantity): return self._execution.buy_market(ticker=ticker, quantity=quantity) def sell_market(self, ticker, quantity): return self._execution.sell_market(ticker=ticker, quantity=quantity) def buy_limit(self, ticker, quantity, price): return self._execution.buy_limit(ticker=ticker, quantity=quantity, price=price) def sell_limit(self, ticker, quantity, price): return self._execution.sell_limit(ticker=ticker, quantity=quantity, price=price) # ------------------------------------------------ [ Get Set ] ------------------------------------------ def get_data(self, datatype, ticker: str, start_date: datetime.datetime = None, n_rows: int = None, sort_drop=True): return self._data.get_data(datatype=datatype, ticker=ticker, start_date=start_date, n_rows=n_rows, sort_drop=sort_drop) def get_current_qty(self, ticker): return self._account.get_current_qty(ticker=ticker) def get_latest_price(self, ticker): return self._account.get_latest_price(ticker=ticker) def get_lot_size(self, ticker): return self._data.get_lot_size(ticker=ticker) def calc_max_buy_qty(self, ticker, cash=None, adjust_limit=1.03): lot_size = self.get_lot_size(ticker=ticker) return self._account.calc_max_buy_qty(ticker=ticker, lot_size=lot_size, cash=cash, adjust_limit=adjust_limit) def subscribe_tickers(self, tickers): self._data.subscribe_tickers(tickers=tickers) def unsubscribe_tickers(self, tickers): self._data.unsubscribe_tickers(tickers=tickers) def pause(self): self._running = False def resume(self): self._running = True @property def cash(self): return self._account.cash @property def mv(self): return self._account.mv @property def pv(self): return self._account.pv @property def n_trades(self): return self._account.n_trades @property def init_capital(self): return self._account.init_capital @property def total_txn_cost(self): return self._account.total_txn_cost @property def initialized_date(self): return self._initialized_date @property def running(self): return self._running @property def records(self): return self._account.records @property def pending_orders(self): return self._account.pending_orders @property def completed_orders(self): return self._account.completed_orders @property def positions(self): return self._account.positions @property def universe(self): return self._data.unverse @property def datatypes(self): return self._data.datatypes @property def initialized(self): return self._initialized