问题描述
class ztx(Factor):
# 设置因子名称
name = 'ztx'
# 设置获取数据的时间窗口长度
max_window = 90
# 设置依赖的数据
dependencies = ['close']
# 计算因子的函数, 需要返回一个 pandas.Series, index 是股票代码,value 是因子值
def calc(self, data):
close = data['close'] # close is a df, column is stock code, index is datetime
temp = pd.Series(0,index = close.iloc[0,:].index) # 初始化为0的矩阵
for i in (0,88): # 循环1
zhangfu_series = close.iloc[i+1,:]/close.iloc[i,:] # 计算涨幅
temp += zhangfu_series
for j in range(len(zhangfu_series)):
if zhangfu_series[j] > 1.095: # 大于9.5% 记作涨停
temp[j] += 1
return temp
计算的是 90日内有N个涨停板,但是temp 这个series 无法计算超过2天的数据 (max_window=90)通过打印日志, 函数中循环1 只被执行了2次,不知道是哪里出了问题,求解。
解决方案
您好!导致循环只执行了2次的原因是 Python 的基础语法问题。
问题原因分析
在您的代码中,循环语句写成了:
for i in (0,88):
在 Python 中,(0, 88) 是一个包含两个元素(0 和 88)的元组(Tuple)。因此,这个 for 循环只会遍历这两个具体的数字,也就是 i=0 执行一次,i=88 执行一次,总共只执行了 2 次。
如果您想让 i 从 0 遍历到 88,应该使用 range() 函数:
for i in range(89): # range(89) 会生成 0 到 88 的整数序列
优化方案(强烈推荐)
虽然修改 range 可以解决您的 Bug,但在量化回测中,使用 for 循环遍历 DataFrame 的行和列是非常低效的。Pandas 提供了强大的向量化运算功能,可以一行代码完成您想要的功能,且运行速度会快几十倍甚至上百倍。
您可以直接使用 Pandas 的 pct_change() 来计算涨幅,并用条件判断和 sum() 来统计涨停次数。以下是优化后的完整因子代码:
import pandas as pd
from jqfactor import Factor
class ztx(Factor):
# 设置因子名称
name = 'ztx'
# 设置获取数据的时间窗口长度
max_window = 90
# 设置依赖的数据
dependencies = ['close']
def calc(self, data):
close = data['close']
# 1. 计算每日涨幅 (今日收盘价 / 昨日收盘价 - 1)
# pct_change() 会自动计算相邻两行的变化率
pct_chg = close.pct_change()
# 2. 判断涨幅是否大于 9.5% (0.095)
# 这会返回一个由 True 和 False 组成的 DataFrame
is_limit_up = pct_chg > 0.095
# 3. 按列(即按股票)对 True 的个数进行求和
# sum() 会将 True 视为 1,False 视为 0
limit_up_counts = is_limit_up.sum()
# 返回的 limit_up_counts 就是一个 pandas.Series,index 是股票代码,value 是涨停次数
return limit_up_counts
优化代码的优势:
- 代码简洁:去掉了繁琐的嵌套
for循环,逻辑更加清晰。 - 性能极高:底层调用 C 语言级别的向量化运算,在处理全市场几千只股票的 90 天数据时,耗时几乎可以忽略不计,能大幅缩短您的回测和因子分析时间。
- 避免越界:自动处理了第一天没有昨日收盘价(
pct_change第一行为 NaN)的问题,不会报错。