# QAAccount与MongoDB
在使用QAAccoount
类进行回测时,MongoDB
数据库quantaxis
中,会涉及到的数据表有 5 个, 分别是:
- portfolio
- user
- account
- risk
- strategy
回测中的逻辑关系
示例
创建资产用户/资产组合/帐户
Quantaxis
v1.3.0以后, QA_Account需要由组合来进行创建(推荐)。为避免出过多因使用未来版本会降级的函数导致的警告信息。 1
2import warnings
warnings.filterwarnings('ignore')
创建用户
用户名为qaacc
,密码为qaacc
1
2
3import QUANTAXIS as QA
user = QA.QA_User(username='qaacc', password='qaacc')1
portfolio = user.new_portfolio('stock')
创建帐户
可以创建多市场的帐户,这里以创建股票市场帐户为例。 1
account = portfolio.new_account(account_cookie='lvjun')
账户的初始资金/初始仓位
默认账户是无仓位, 默认现金 1,000,000 RMB 1
account.init_assets
1
{'cash': 1000000, 'hold': {}}
1 | account.init_cash |
输出: 1
1000000
1 | account.init_hold |
输出: 1
Series([], Name: amount, dtype: object)
简单回测
用帐户account_cookie='lvjun'
进行简单回测。Quantaxis
版本为 1.10.19。
新建策略
MACD_JCSC
策略思想是MACD指标出现金叉(DIF向上突破DEA)买入,出现死叉(DIF向下跌破DEA)卖出。
1 | import QUANTAXIS as QA |
初始化回测broker及保存策略
策略源码保存可以保存到网络侧(127.0.0.1:8010),也可以保存到本地。 1
2
3Broker = QA.QA_BacktestBroker()
# 策略保存到本地, 当含参`if_save=True`时, 会保存到本地。 在数据表·strategy`中可以看到具体信息。
QA.QA_SU_save_strategy('MACD_JCSC', 'Indicator', account.account_cookie)
选择测试标的物数据
1 | # get data from mongodb |
计算指标信号
指标的计算可以在回测前,也可以在回测中进行。区别在于回测前的计算则是批量计算,效率较高。而回测中进行计算,效率略低,但代码量较小,易于理解。 1
2
3ind = data.add_func(MACD_JCSC)
print("金叉信号出现:%d 次" % ind['CROSS_JC'].value_counts()[1])
print("死叉信号出现:%d 次" % ind['CROSS_SC'].value_counts()[1])1
2金叉信号出现:8 次
死叉信号出现:7 次1
ind.loc[(ind['CROSS_JC'] == 1) | (ind['CROSS_SC'] == 1)]
回测
通过迭代产生Dataframe
截面数据的方式,对产生信号的标的物进行交易回测。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34for items in data_forbacktest.panel_gen:
for item in items.security_gen:
daily_ind = ind.loc[item.index]
if daily_ind.CROSS_JC.iloc[0] > 0:
order = account.send_order(
code=item.code[0],
time=item.date[0],
amount=1000,
towards=QA.ORDER_DIRECTION.BUY,
price=0,
order_model=QA.ORDER_MODEL.CLOSE,
amount_model=QA.AMOUNT_MODEL.BY_AMOUNT
)
Broker.receive_order(QA.QA_Event(order=order, market_data=item))
trade_mes = Broker.query_orders(account.account_cookie, 'filled')
res = trade_mes.loc[order.account_cookie, order.realorder_id]
order.trade(res.trade_id, res.trade_price,res.trade_amount, res.trade_time)
elif daily_ind.CROSS_SC.iloc[0] > 0:
if account.sell_available.get(item.code[0], 0) > 0:
order = account.send_order(
code=item.code[0],
time=item.date[0],
amount=account.sell_available.get(item.code[0], 0),
towards=QA.ORDER_DIRECTION.SELL,
price=0,
order_model=QA.ORDER_MODEL.MARKET,
amount_model=QA.AMOUNT_MODEL.BY_AMOUNT
)
Broker.receive_order(QA.QA_Event(order=order, market_data=item))
trade_mes = Broker.query_orders(account.account_cookie, 'filled')
res = trade_mes.loc[order.account_cookie, order.realorder_id]
order.trade(res.trade_id, res.trade_price,res.trade_amount, res.trade_time)
account.settle()1
2
3
4
5
6
7QAACCOUNT ==> receive deal Time 2018-01-02 00:00:00/ Code:000001/ Price:12.48/ TOWARDS:1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-01-03 00:00:00/ Code:000001/ Price:12.32/ TOWARDS:-1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-01-12 00:00:00/ Code:000001/ Price:12.34/ TOWARDS:1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-01-29 00:00:00/ Code:000001/ Price:12.68/ TOWARDS:-1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-03-08 00:00:00/ Code:000001/ Price:11.03/ TOWARDS:1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-03-26 00:00:00/ Code:000001/ Price:10.05/ TOWARDS:-1/ Amounts:1000
QAACCOUNT ==> receive deal Time 2018-04-10 00:00:00/ Code:000001/ Price:10.4/ TOWARDS:1/ Amounts:1000
风险与绩效分析
可以手工完成分析,或是进入 Web 界面调用数据分析。
风险分析
1 | Risk = QA.QA_Risk(account) |
输出:
绩效分析
1 | Performance = QA.QA_Performance(account) |
输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75{
"status": 200,
"result": {
"total_profit": 340.0,
"total_loss": -1140.0,
"total_pnl": 0.3,
"trading_amounts": 3,
"profit_amounts": 1,
"loss_amounts": 2,
"even_amounts": 0,
"profit_precentage": 0.33,
"loss_precentage": 0.67,
"even_precentage": 0.0,
"average_profit": 340.0,
"average_loss": -570.0,
"average_pnl": 0.6,
"max_profit": 340.0,
"max_loss": -980.0,
"max_pnl": 0.35,
"netprofio_maxloss_ratio": 0.82,
"continue_profit_amount": 1,
"continue_loss_amount": 1,
"average_holdgap": "12 days 00:00:00",
"average_profitholdgap": "17 days 00:00:00",
"average_losssholdgap": "9 days 12:00:00",
"buyopen": {
"total_profit": 340.0,
"total_loss": -1140.0,
"total_pnl": 0.3,
"trading_amounts": 3,
"profit_amounts": 1,
"loss_amounts": 2,
"even_amounts": 0,
"profit_precentage": 0.33,
"loss_precentage": 0.67,
"even_precentage": 0.0,
"average_profit": 340.0,
"average_loss": -570.0,
"average_pnl": 0.6,
"max_profit": 340.0,
"max_loss": -980.0,
"max_pnl": 0.35,
"netprofio_maxloss_ratio": 0.82,
"continue_profit_amount": 1,
"continue_loss_amount": 1,
"average_holdgap": "12 days 00:00:00",
"average_profitholdgap": "17 days 00:00:00",
"average_losssholdgap": "9 days 12:00:00"
},
"sellopen": {
"total_profit": 0,
"total_loss": 0,
"total_pnl": 0,
"trading_amounts": 0,
"profit_amounts": 0,
"loss_amounts": 0,
"even_amounts": 0,
"profit_precentage": 0,
"loss_precentage": 0,
"even_precentage": 0,
"average_profit": 0,
"average_loss": 0,
"average_pnl": 0,
"max_profit": 0,
"max_loss": 0,
"max_pnl": 0,
"netprofio_maxloss_ratio": 0,
"continue_profit_amount": 0,
"continue_loss_amount": 0,
"average_holdgap": "no trade",
"average_profitholdgap": "no trade",
"average_losssholdgap": "no trade"
}
}
}
Web浏览方式
浏览器输入:http://127.0.0.1:81 ,使用回测时创建用户名/密码。
- 风险与绩效分析
- 买卖信号记录
备注:
计算 Performance.continue_profit_amount
等指标时时,原代码中使用了 for _, item in pnl.pnl_money.items():
, 在 Pandas ≥ 1.5 时,Series.iteritems()
, DataFrame.iteritems()
及 HDFStore.iteritems()
均使用 obj.items
进行替代。1
1 | ### What’s new in 1.5.0 (September 19, 2022) |