1. Introduction
Open source address:
https://github.com/zhanggao2013/AmazingQuant
AmazingQuant is an open source framework for quantitative backtesting transactions based on event driven. The following figure shows the overall framework.
data_center
to_mongoDB stores market, financial and other data to the storage module of MongoDB
get_ Interface module for fetching data from database in data policy
trade_center
mossion_engine includes two parts: event_order and event_risk_management, which respectively complete the inspection and risk control before ordering
broker_engine is divided into two parts: simulate d broker (mainly event_deal) for matching transactions during backtesting and interfaces for connecting CTP, xSpeed and other real offer transactions
strategy_center
bar_ In the back test or trading mode, engine executes the trading logic of each bar by K-line, and can run under daily line, minute line and sub pen
analysis_center
analysis_engine analyzes and visualizes the transaction records formed by the back test, including net worth, annualized income, alpha, beta, pullback and other indicators, and the implementation of classical models such as brison and Fama
2. Installation configuration
MongoDB 3.4
It is recommended to use shard to configure the startup item example
pymongo
python calls MongoDB
talib
Technical index calculation library
anaconda
For Python version 3.5, if it is greater than 3.5, the ctp interface cannot be used temporarily because of the compilation problem, which can be solved later
Linux Ubuntu
The development environment is ubuntu. Of course, it can also be used under windows, but the database configuration and transaction interfaces such as ctp need to be redone
Installing AmazingQuant
pip install AmazingQuant direct installation
3. Strategy preparation
# -*- coding: utf-8 -*- __author__ = "gao" import numpy as np import talib # import strategy base class from AmazingQuant.strategy_center.strategy import * # import transaction module from AmazingQuant.trade_center.trade import Trade # Inherit strategy base class class MaStrategy(StrategyBase): def initialize(self): # Set operation mode, back test or transaction self.run_mode = RunMode.BACKTESTING.value # Set back test fund account number self.account = ["test0", "test1"] # Set the fund amount of the back test fund account self.capital = {"test0": 2000000, "test1": 1000} # Set back survey datum self.benchmark = "000300.SH" # Set weight recovery method self.rights_adjustment = RightsAdjustment.NONE.value # Set the start and end time of back measurement self.start = "2015-01-11" self.end = "2016-01-16" # Set running cycle self.period = "daily" # Set stock pool self.universe = ['000001.SZ', '000002.SZ', '000008.SZ', '000060.SZ', '000063.SZ', '000069.SZ', '000100.SZ', '000157.SZ', '000166.SZ', '000333.SZ', '000338.SZ', '000402.SZ', '000413.SZ', '000415.SZ', '000423.SZ', '000425.SZ', '000503.SZ', '000538.SZ', '000540.SZ', '000559.SZ', '000568.SZ', '000623.SZ', '000625.SZ', '000627.SZ', '000630.SZ', '000651.SZ', '000671.SZ', '000686.SZ', '000709.SZ', '000723.SZ', '000725.SZ', '000728.SZ', '000738.SZ', '000750.SZ', '000768.SZ', '000776.SZ', '000783.SZ', '000792.SZ', '000826.SZ', '000839.SZ', '000858.SZ', '000876.SZ', '000895.SZ', '000898.SZ', '000938.SZ', '000959.SZ', '000961.SZ', '000963.SZ', '000983.SZ', '001979.SZ', '002007.SZ', '002008.SZ', '002024.SZ', '002027.SZ', '002044.SZ', '002065.SZ', '002074.SZ', '002081.SZ', '002142.SZ', '002146.SZ', '002153.SZ', '002174.SZ', '002202.SZ', '002230.SZ', '002236.SZ', '002241.SZ', '002252.SZ', '002292.SZ', '002294.SZ', '002304.SZ', '002310.SZ', '002352.SZ', '002385.SZ', '002411.SZ', '002415.SZ', '002424.SZ', '002426.SZ', '002450.SZ', '002456.SZ', '002460.SZ', '002465.SZ', '002466.SZ', '002468.SZ', '002470.SZ', '002475.SZ', '002500.SZ', '002508.SZ', '002555.SZ', '002558.SZ', '002572.SZ', '002594.SZ', '002601.SZ', '002602.SZ', '002608.SZ', '002624.SZ', '002673.SZ', '002714.SZ', '002736.SZ', '002739.SZ', '002797.SZ', '002831.SZ', '002839.SZ', '002841.SZ', '300003.SZ', '300015.SZ', '300017.SZ', '300024.SZ', '300027.SZ', '300033.SZ', '300059.SZ', '300070.SZ', '300072.SZ', '300122.SZ', '300124.SZ', '300136.SZ', '300144.SZ', '300251.SZ', '300315.SZ', '600000.SH', '600008.SH', '600009.SH', '600010.SH', '600011.SH', '600015.SH', '600016.SH', '600018.SH', '600019.SH', '600021.SH', '600023.SH', '600028.SH', '600029.SH', '600030.SH', '600031.SH', '600036.SH', '600038.SH', '600048.SH', '600050.SH', '600061.SH', '600066.SH', '600068.SH', '600074.SH', '600085.SH', '600089.SH', '600100.SH', '600104.SH', '600109.SH', '600111.SH', '600115.SH', '600118.SH', '600153.SH', '600157.SH', '600170.SH', '600177.SH', '600188.SH', '600196.SH', '600208.SH', '600219.SH', '600221.SH', '600233.SH', '600271.SH', '600276.SH', '600297.SH', '600309.SH', '600332.SH', '600340.SH', '600352.SH', '600362.SH', '600369.SH', '600372.SH', '600373.SH', '600376.SH', '600383.SH', '600390.SH', '600406.SH', '600415.SH', '600436.SH', '600482.SH', '600485.SH', '600489.SH', '600498.SH', '600518.SH', '600519.SH', '600522.SH', '600535.SH', '600547.SH', '600549.SH', '600570.SH', '600583.SH', '600585.SH', '600588.SH', '600606.SH', '600637.SH', '600649.SH', '600660.SH', '600663.SH', '600674.SH', '600682.SH', '600685.SH', '600688.SH', '600690.SH', '600703.SH', '600704.SH', '600705.SH', '600739.SH', '600741.SH', '600795.SH', '600804.SH', '600816.SH', '600820.SH', '600827.SH', '600837.SH', '600871.SH', '600886.SH', '600887.SH', '600893.SH', '600895.SH', '600900.SH', '600909.SH', '600919.SH', '600926.SH', '600958.SH', '600959.SH', '600977.SH', '600999.SH', '601006.SH', '601009.SH', '601012.SH', '601018.SH', '601021.SH', '601088.SH', '601099.SH', '601111.SH', '601117.SH', '601118.SH', '601155.SH', '601163.SH', '601166.SH', '601169.SH', '601186.SH', '601198.SH', '601211.SH', '601212.SH', '601216.SH', '601225.SH', '601228.SH', '601229.SH', '601288.SH', '601318.SH', '601328.SH', '601333.SH', '601336.SH', '601375.SH', '601377.SH', '601390.SH', '601398.SH', '601555.SH', '601600.SH', '601601.SH', '601607.SH', '601608.SH', '601611.SH', '601618.SH', '601628.SH', '601633.SH', '601668.SH', '601669.SH', '601688.SH', '601718.SH', '601727.SH', '601766.SH', '601788.SH', '601800.SH', '601818.SH', '601857.SH', '601866.SH', '601872.SH', '601877.SH', '601878.SH', '601881.SH', '601888.SH', '601898.SH', '601899.SH', '601901.SH', '601919.SH', '601933.SH', '601939.SH', '601958.SH', '601966.SH', '601985.SH', '601988.SH', '601989.SH', '601991.SH', '601992.SH', '601997.SH', '601998.SH', '603160.SH', '603799.SH', '603833.SH', '603858.SH', '603993.SH'] # Set whether to cache the daily line, minute line and other cycle data before running self.daily_data_cache = True print(self.universe) # Set the back sliding point according to the fixed value of 0.01,20-0.01 = 19.99; Percentage 0.01,20 * (1-0.01) = 19.98; Use "+" when closing positions self.set_slippage(stock_type=StockType.STOCK.value, slippage_type=SlippageType.SLIPPAGE_FIX.value, value=0.01) # Back test of stock handling fee and stamp duty, sales stamp duty, one thousandth; Opening fee, 3 / 10000; Closing fee, 3 / 10000, minimum fee, 5 yuan # In the Shanghai stock market, there is a transfer fee of 2 / 10000 for sales, which is added to the selling handling fee self.set_commission(stock_type=StockType.STOCK_SH.value, tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5) # Shenzhen does not add transfer fee self.set_commission(stock_type=StockType.STOCK_SZ.value, tax=0.001, open_commission=0.0003, close_commission=0.0005, close_today_commission=0, min_commission=5) def handle_bar(self, event): # Take the current position available_position_dict = {} for position in Environment.bar_position_data_list: available_position_dict[position.instrument + "." + position.exchange] = position.position - position.frozen # The specific timestamp of the current bar current_date = data_transfer.millisecond_to_date(millisecond=self.timetag, format="%Y-%m-%d") # The timestamp is converted to int to facilitate subsequent data retrieval current_date_int = data_transfer.date_str_to_int(current_date) print(current_date) # Fetch data instance data_class = GetData() # Loop through the stock pool for stock in self.universe: # Take the closing price of the current stock close_price = data_class.get_market_data(Environment.daily_data, stock_code=[stock], field=["close"], end=current_date) # print(self.start, current_date) close_array = np.array(close_price) if len(close_array) > 0: # Calculating MA using talib ma5 = talib.MA(np.array(close_price), timeperiod=5) ma20 = talib.MA(np.array(close_price), timeperiod=20) # print(type(close_price.keys())) # Filter because the suspension has no data if current_date_int in close_price.keys(): # If the 5-day moving average breaks through the 20 day moving average and there is no position, buy 100 shares of this stock and trade at the closing price if ma5[-1] > ma20[-1] and stock not in available_position_dict.keys(): Trade(self).order_shares(stock_code=stock, shares=100, price_type="fix", order_price=close_price[current_date_int], account=self.account[0]) print("buy", stock, 1, "fix", close_price[current_date_int], self.account) # If the 20 day moving average breaks through the 5-day moving average and there is a position, sell 100 shares of the stock and trade at the closing price elif ma5[-1] < ma20[-1] and stock in available_position_dict.keys(): Trade(self).order_shares(stock_code=stock, shares=-100, price_type="fix", order_price=close_price[current_date_int], account=self.account[0]) print("sell", stock, -1, "fix", close_price[current_date_int], self.account) if __name__ == "__main__": # The time required to test and run a complete strategy has not been optimized at present, # 300 stocks, daily data, a year's time interval, more than 4000 transaction records, about 80s in my virtual machine. It should be much faster to change a machine with better personality from AmazingQuant.utils.performance_test import Timer time_test = Timer(True) with time_test: # Operation strategy, set whether to save entrustment, transaction, capital and position MaStrategy().run(save_trade_record=True)
4. Analysis of back test results
Automatically generate back test results
The generated cvs files of entrustment, transaction, fund and position are written to the folder where the strategy is located
Automatically generate back test report
The back test report is in html format and can be opened and viewed in the browser. The effect is as follows:
5. Firm offer transaction
At present, it has been implemented to package according to vnpy, and use boost to python3.0 for the C + + interface of CTP 5, which will be implemented with broker later_ Docking of engine
6. Implemented and upcoming functions
Implemented
Database construction
Read data
Policy run back test
Preservation and analysis of back test transaction records
Packaging of solid CTP interface
Coming soon
Docking of various data
For example, minute data of stocks, financial data of stocks, component stocks of stock sector, minute data of futures, daily data, etc
CTP and other transaction interfaces and brokers_ Engine docking
CTP, xSpeed, etc
Visualize the trading and position of each bar in the back test range
Enrichment of back test analysis module
Increase the analysis and visualization of various performance attribution models such as brison and FAMA