表达式设计
基本思路
- 解析公式: 可以将每个公式看作一个字符串,然后用 Python 的字符串处理功能提取其中的变量、和运算符。其中,带问号的运算符因 python 不支持,需要进行特殊处理,使用 ast 替换为自定义的 where 函数实现
- 映射函数: 将公式中的函数名,例如 rank, ts_argmax, stddev 等,映射到实现的 python 函数
- 执行计算: 根据公式的逻辑和运算符,依次调用相应的函数,并传入对应的变量或中间结果,最终得到计算结果
解析代码
解析公式的代码如下,parse_and_calculate_alpha 输入参数为公式字符串和 pandas 股票数据:
class RewriteNode(ast.NodeTransformer):
# 重写 AST 节点,用于转换三元表达式
def visit_IfExp(self, node):
# 交换 node 的 body 和 test
node.body, node.test = node.test, node.body
# 将 AST 节点转换为字符串
test = ast.unparse(node.test)
body = ast.unparse(node.body)
orelse = ast.unparse(node.orelse)
# 构建新的 where 函数调用节点
newnode = ast.parse(f"where({test}, {body}, {orelse})").body[0]
return newnode
def ternary_trans(formula):
# 替换公式中的三元运算符为 if-else 形式
formula = formula.replace('?', ' if ')
formula = formula.replace(':', ' else ')
# 解析修改后的公式为 AST 树
tree = ast.parse(formula)
# 遍历 AST 树,应用 RewriteNode 转换
for node in ast.walk(tree):
RewriteNode().visit(node)
# 将转换后的 AST 树转换回字符串
return ast.unparse(tree)
def where(c: pd.Series, t: pd.Series, f: pd.Series) -> pd.Series:
return pd.Series(np.where(c, t, f))
def parse_and_calculate_alpha(formula, data):
# 暂时不支持的函数,返回空数据
todolist=['indneutralize','cap','filter','self','banchmarkindex']
for todo in todolist:
if todo in formula:
return pd.Series(index=data.index)
# 替换运算公式为python运算符
formula=formula.replace("||"," | ")
formula=formula.replace("&&"," & ")
formula=formula.replace("^"," ** ")
# 将 $xxx 替换为 data['xxx']
for col in data.columns:
formula = formula.replace(f'${col}', f"data['{col}']")
print(f"变量替换后的公式: {formula}")
# 使用 ast 处理三元表达式
if '?' in formula:
formula = ternary_trans(formula)
print(f"三元表达式替换后的公式: {formula}")
# 解析公式字符串为 AST 树
tree = ast.parse(formula, mode='eval')
print(f"ast 解析后的结果: {ast.dump(tree)}") # 打印 ast 解析后的结果
# 创建 ast.Expression 对象
expression = ast.Expression(body=tree.body)
# 将转换后的 AST 树编译为可执行的代码对象
code = compile(expression, filename='<alpha_formula>', mode='eval')
# 将 FUNCTION_MAP 中的所有函数添加到全局环境
global_env = {'__builtins__': {'True': True, 'False': False}}
for func_name, func in FUNCTION_MAP.items():
global_env[func_name] = func
# 将 data 变量添加到局部环境
local_env = {'data': data,'where':where}
# 执行代码并返回结果
result = eval(code, global_env, {**local_env})
return pd.Series(result, index=data.index)
算子
abs(x)
: 取 x 的绝对值log(x)
: 取 x 的自然对数sign(x)
: 获取 x 的符号rank(x)
: x 的横截面排名,跨股票计算,本次暂不复现delay(x, d)
: 取 d天前x的值correlation(x, y, d)
: 在过去 d 天中,x 和 y 的时间序列相关性covariance(x, y, d)
: 在过去 d 天中,x 和 y 的时间序列协方差scale(x, a)
: 将 x 缩放,使得 abs(x) 之和为 a(默认情况下 a = 1)delta(x, d)
: 今天的 x 值减去 d 天前的 x 值signedpower(x, a)
: x 的 a 次幂decay_linear(x, d)
: 过去 d 天的加权移动平均,采用线性衰减的权重(d、d-1、…、1),权重之和为 1indneutralize(x, g)
: x 在各组 g 中的横截面去均值化(可以是子行业、行业、部门等),即在每个组 g 内部,x 是经过去均值处理的ts_min(x, d)
: 在过去 d 天的时间序列中,取 x 的最小值ts_max(x, d)
: 在过去 d 天的时间序列中,取 x 的最大值ts_argmax(x, d)
: 在过去 d 天的时间序列中,取 x 的最大值对应的天数ts_argmin(x, d)
: 在过去 d 天的时间序列中,取 x 的最小值对应的天数ts_rank(x, d)
: 在过去 d 天的时间序列中,对 x 进行排名min(x, d)
: 同 ts_min(x, d),取 x 在过去 d 天的最小值(参数类型需改为两个序列)max(x, d)
: 同 ts_max(x, d),取 x 在过去 d 天的最大值(参数类型需改为两个序列)sum(x, d)
: 对过去 d 天的 x 进行求和product(x, d)
: 对过去 d 天的 x 进行求积stddev(x, d)
: 计算过去 d 天的 x 的移动时间序列标准差
国泰因子函数定义
Qlib Alpha158
GPU 因子计算
GPU,作为并行计算的利器,已经在深度学习领域大放异彩。它能够同时处理大量数据,显著提升计算速度。考虑到这一点,使用 GPU 复现因子计算
需要精心设计一个对比测试函数,将 GPU 加速计算出的因子值与传统的 pandas 版本进行对比。这一步骤将能够验证我们使用 GPU 加速因子计算方法的有效性