问题描述
总体回测前要做的事情
def initialize(context):
set_variables() # 设置中间变量
set_backtest() # 设置回测条件
设置中间变量
def set_variables():
g.if_trade = False # 当天是否交易
设置回测条件
def set_backtest():
# 设置对比基准
set_benchmark('000300.XSHG')
# 使用真实价格
set_option('use_real_price', True)
# 设置报错等级
log.set_level('order', 'error')
'''
每天开盘前
'''
每天开盘前要做的事。计算tiaocang
def before_trading_start(context):
a = GetTradePeriod('2010-01-01','2020-09-30', 'ME')
# 获取每个月最后一个交易日
todayStr=str(context.current_dt)[0:10]#去掉时分秒,保留年月日
for i in a:
if str(i) == todayStr: # 今天是月末最后一个交易日
g.if_trade=True
# 设置手续费与手续费
set_slip_fee(context)
根据不同的时间段设置滑点与手续费
def set_slip_fee(context):
# 将滑点设置为0
set_slippage(FixedSlippage(0))
# 根据不同的时间段设置手续费
dt=context.current_dt
if dt>datetime.datetime(2013,1, 1):
set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
elif dt>datetime.datetime(2011,1, 1):
set_commission(PerTrade(buy_cost=0.001, sell_cost=0.002, min_cost=5))
elif dt>datetime.datetime(2009,1, 1):
set_commission(PerTrade(buy_cost=0.002, sell_cost=0.003, min_cost=5))
else:
set_commission(PerTrade(buy_cost=0.003, sell_cost=0.004, min_cost=5))
'''
每天交易时
'''
def handle_data(context, data):
# 每个调仓日截面上,进行数据提取和计算
if g.if_trade == True:
a1 = GetTradePeriod('2010-01-01','2020-09-30', 'ME')
todayStr1=str(context.current_dt)[0:10]
#去掉时分秒,保留年月日
for i in a1:
if str(i) == todayStr1: # 今天是月末最后一个交易日
ii = i
aa = a1.index(ii) # 今天是列表a的第几个元素
bb = result_df.xs(result_df.index.levels[0][aa])
orderlist = list(bb.index)
weights = list(bb['w'])
order_stock_sell(context,orderlist)
order_stock_buy(context,orderlist)
g.if_trade = False
执行卖出
def order_stock_sell(context,order_list):
# 对于不需要持仓的股票,全仓卖出
for stock in context.portfolio.positions:
# 除去buy_list内的股票,其他都卖出
if stock not in order_list:
order_target_value(stock, 0)
执行买入(自己修改了)
def order_stock_buy(context,order_list):
# 对于需要持仓的股票,按分配到的份额买入
for stock in order_list:
ww = weights[order_list.index(stock)]
order_target_value(stock, 50000000*ww)
运行时报错:
Traceback (most recent call last):
File "pandas/core/indexes/base.py", line 3078, in get_loc
return self._engine.get_loc(key)
File "pandas/_libs/index.pyx", line 140, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/index.pyx", line 162, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 1492, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 1500, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 0
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmp/jqcore/jqboson/jqboson/core/entry.py", line 368, in run
engine.start()
File "/tmp/jqcore/jqboson/jqboson/core/engine.py", line 231, in start
self.dispatcher.start()
File "/tmp/jqcore/jqboson/jqboson/core/dispatcher.py", line 273, in start
self._run_loop()
File "/tmp/jqcore/jqboson/jqboson/core/dispatcher.py", line 240, in _run_loop
self._loop.run()
File "/tmp/jqcore/jqboson/jqboson/core/loop/loop.py", line 105, in run
self._handle_queue()
File "/tmp/jqcore/jqboson/jqboson/core/loop/loop.py", line 151, in _handle_queue
message.callback(message.callback_data)
File "/tmp/jqcore/jqboson/jqboson/core/mds/market_data_subscriber.py", line 228, in broadcast
consumer.send(market_data)
File "/tmp/jqcore/jqboson/jqboson/core/mds/market_data_consumer_manager.py", line 59, in consumer_gen
msg_callback()
File "/tmp/jqcore/jqboson/jqboson/core/mds/market_data_consumer_manager.py", line 52, in msg_callback
callback(market_data)
File "/tmp/jqcore/jqboson/jqboson/core/mds/market_data_consumer_manager.py", line 122, in wrapper
result = callback(*args, kwargs)
File "/tmp/jqcore/jqboson/jqboson/core/strategy.py", line 407, in _wrapper
self._context.current_dt
File "/tmp/strategy/user_code.py", line 6410, in handle_data
order_stock_buy(context,orderlist)
File "/tmp/strategy/user_code.py", line 6633, in order_stock_buy
ww = weights[order_list.index(stock)]
File "pandas/core/frame.py", line 2688, in __getitem
return self._getitem_column(key)
File "pandas/core/frame.py", line 2695, in _getitem_column
return self._get_item_cache(key)
File "pandas/core/generic.py", line 2489, in _get_item_cache
values = self._data.get(item)
File "pandas/core/internals.py", line 4115, in get
loc = self.items.get_loc(item)
File "pandas/core/indexes/base.py", line 3080, in get_loc
return self._engine.get_loc(self._maybe_cast_indexer(key))
File "pandas/_libs/index.pyx", line 140, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/index.pyx", line 162, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 1492, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 1500, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 0
请问怎么解决?
解决方案
报错原因分析
根据您提供的报错堆栈信息,错误发生在 order_stock_buy 函数中的这一行:
ww = weights[order_list.index(stock)]
堆栈中明确显示了 File "pandas/core/frame.py", line 2688, in __getitem__,这说明在执行这一行时,weights 是一个 Pandas DataFrame 对象,而不是您期望的 Python 列表(List)。
为什么会这样?
- 在
handle_data函数中,您写了weights = list(bb['w'])。这里的weights只是handle_data函数内部的局部变量。 - 当代码执行到
order_stock_buy函数时,由于您没有把weights作为参数传进去,Python 会在局部作用域找不到weights,从而去全局作用域寻找。恰好您在策略的其他地方(全局)定义了一个名为weights的 DataFrame。 - 当您用
order_list.index(stock)(返回一个整数,比如0)去索引一个 DataFrame 时,Pandas 会尝试寻找列名为0的列,找不到就会抛出KeyError: 0。
解决方法
要解决这个问题,您需要将股票代码和对应的权重正确地传递给 order_stock_buy 函数。最优雅且高效的做法是将股票和权重组合成一个字典(Dict)进行传递。
以下是修改后的代码:
1. 修改 handle_data 函数
将提取出的股票和权重直接转换为字典,并传递给买入函数:
def handle_data(context, data):
# 每个调仓日截面上,进行数据提取和计算
if g.if_trade == True:
a1 = GetTradePeriod('2010-01-01','2020-09-30', 'ME')
todayStr1=str(context.current_dt)[0:10]
for i in a1:
if str(i) == todayStr1: # 今天是月末最后一个交易日
ii = i
aa = a1.index(ii) # 今天是列表a的第几个元素
bb = result_df.xs(result_df.index.levels[0][aa])
# 【修改点】直接将股票和权重转换为字典 {stock: weight}
buy_dict = bb['w'].to_dict()
orderlist = list(buy_dict.keys())
order_stock_sell(context, orderlist)
# 【修改点】将字典作为参数传递给买入函数
order_stock_buy(context, buy_dict)
g.if_trade = False
2. 修改 order_stock_buy 函数
接收字典参数,直接遍历字典的键值对进行买入,避免使用 index() 查找(提高运行效率):
def order_stock_buy(context, buy_dict):
# 【修改点】遍历字典,直接获取股票代码和对应的权重
for stock, ww in buy_dict.items():
order_target_value(stock, 50000000 * ww)
3. order_stock_sell 保持不变
def order_stock_sell(context, order_list):
# 对于不需要持仓的股票,全仓卖出
for stock in context.portfolio.positions:
# 除去buy_list内的股票,其他都卖出
if stock not in order_list:
order_target_value(stock, 0)
总结
在编写量化策略时,尽量避免在不同函数中隐式依赖同名的全局变量。通过显式地传递参数(如上述的 buy_dict),不仅可以彻底解决 KeyError 和作用域混乱的问题,还能让代码逻辑更加清晰,运行速度更快。