QUANTAXIS单股回测( QUANTAXIS探索(二)策略运行)使用了QAStrategyStockBase类,但该类用于多股回测会有一些不方便之处,来看源代码,回测会调用debug函数,
def debug(self):
... ...
#data可以对多只股票获取行情,是pandas多重索引
data = QA.QA_quotation(self.code, self.start, self.end, source=QA.DATASOURCE.MONGO,
frequence=self.frequence, market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT)
#针对每一行进行迭代
data.data.apply(self.x1, axis=1)
多股的数据示例如下:
由于代码针对每一行进行迭代,对于N个股票,则同一天会被迭代N次on_bar事件。虽然self.x1函数在换日时会调用self.self.on_dailyclose()函数,但最新的数据没有传过去,需要额外的处理逻辑,同时循环需要的次数也会增加。
为了简化处理,需要重写debug逻辑。
class MyStrategy(QAStrategyStockBase):
def __init__(self,init_cash=1000000):
'''
原来的debug函数,现金是指定的,所以修改以动态定义初始现金
'''
self.init_cash=init_cash
super().__init()
def user_init(self):
self.counter=0
def debug(self):
self.running_mode = 'backtest'
self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
user = QA_User(username=self.username, password=self.password
port = user.new_portfolio(self.portfolio)
# 每次重置账户历史,不然quantaxis对于同一标识的account,会保存所有历史运行数据
try:
port.drop_account(self.strategy_id)
except:
pass
self.acc = port.new_accountpro(
account_cookie=self.strategy_id,
init_cash=self.init_cash,
market_type=self.market_type,
frequence=self.frequence
)
# 适配多股结构
self.positions={}
for code in self.code:
self.positions[code] = 0
print(self.acc.market_type)
data = QA.QA_quotation([code.upper() for code in self.code],
self.start,
self.end,
source=QA.DATASOURCE.MONGO,
frequence=self.frequence,
market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT)
#data.data.apply(self.x1, axis=1)
# 按日滚动回测
data.data.groupby('date').apply(self.x_by_period)
def x_by_period(self, group):
items=[]
date=group.name
for _,item in group.iterrows():
# 初始化on_bar中的数据
item['code']=item.name[1]
item['date']=date
items.append(item)
self.latest_price[item.name[1]] = item['close']
self._market_data.append(copy.deepcopy(item))
if str(date)[0:10] != str(self.running_time)[0:10]:
self.on_dailyclose()
self.on_dailyopen()
if self.market_type == QA.MARKET_TYPE.STOCK_CN:
print(f'backtest: {str(date)[0:10]} Settle!')
self.acc.settle()
self._on_1min_bar()
self.running_time = str(date)
self.on_bar(items)
def on_bar(self,data):
'''
data是一个list结构,对应当日所有股票的数据。
data[0]为第一只,data[0]的结构是pd.Series
可以用bar['code'],也可以用bar.code访问
'''
# 任意定义一个策略,T日买,T+10日卖
# 这里使用了期货的概念,买开,卖平等,所以有BUY/SELL,'OPEN'/'CLOSE'等区分
for bar in data:
if self.counter % 15 == 0:
print('buy')
self.send_order('BUY','OPEN',code=bar.code,price=bar.open,volume=10000)
if self.counter % 15== 10:
print('sell')
self.send_order('SELL','CLOSE',code=bar.code,price=bar.open,volume=10000)
self.counter+=1
策略运行:
if __name__ == "__main__":
code_list = ['000001','000002']
s = MyStrategy(
code=code_list,
frequence="day",
start="2020-05-01",
end="2021-06-05",
strategy_id="test",
)
s.run_backtest()