You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python/Django期权交易模拟:求支持无状态数学函数的用户自定义方案

安全实现用户自定义期权数学函数的几种方案

刚好做过类似的安全自定义表达式需求,给你几个实用的方向,既满足用户自定义期权逻辑的需求,又能严格避免执行恶意代码的风险:

方案1:基于Python AST的自定义表达式解析器(最可控)

Python的ast模块可以帮你直接解析用户输入的表达式字符串,然后遍历语法树验证每个节点是否都是允许的操作(比如基础算术、有限的数学函数、指定变量),完全杜绝执行任意代码的可能。

举个简单的实现思路:

  1. 接收用户输入的表达式(比如max(Asset_price - 150, 0))
  2. 用ast.parse将字符串转为抽象语法树
  3. 遍历语法树,检查所有节点:
    • 只允许Constant(数值)、Name(仅允许Asset_price这个变量)
    • 只允许BinOp(加减乘除幂等算术操作)、Call(仅允许指定的安全函数,比如max、min、math.sqrt等)
  4. 验证通过后,编译并执行这个表达式,或者手动遍历语法树求值(更安全)

示例代码片段:

import ast
import math

ALLOWED_VARIABLES = {'Asset_price'}
ALLOWED_FUNCTIONS = {'max', 'min', 'sqrt', 'log', 'exp'}
ALLOWED_OPS = {
    ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow,
    ast.FloorDiv, ast.Mod
}

def validate_expression(expr):
    tree = ast.parse(expr, mode='eval')
    for node in ast.walk(tree):
        if isinstance(node, ast.Name):
            if node.id not in ALLOWED_VARIABLES:
                raise ValueError(f"不允许使用变量: {node.id}")
        elif isinstance(node, ast.Call):
            if isinstance(node.func, ast.Name) and node.func.id not in ALLOWED_FUNCTIONS:
                raise ValueError(f"不允许调用函数: {node.func.id}")
            # 还可以检查函数参数数量是否合法
        elif isinstance(node, ast.BinOp):
            if type(node.op) not in ALLOWED_OPS:
                raise ValueError(f"不允许的操作符: {type(node.op).__name__}")
        # 禁止其他类型的节点,比如赋值、循环、导入等
        elif not isinstance(node, (ast.Constant, ast.Expression, ast.Eval)):
            raise ValueError(f"不允许的语法节点: {type(node).__name__}")
    return tree

def evaluate_option(asset_price, user_expr):
    try:
        tree = validate_expression(user_expr)
        # 构建安全的执行上下文
        ctx = {
            'Asset_price': asset_price,
            'max': max,
            'min': min,
            'sqrt': math.sqrt,
            'log': math.log,
            'exp': math.exp
        }
        return eval(compile(tree, '<user_expr>', 'eval'), {}, ctx)
    except Exception as e:
        return f"表达式无效: {str(e)}"

方案2:使用受限的表达式求值库

如果不想自己写AST解析,可以用现成的安全表达式求值库,比如simpleeval——它允许你明确指定用户能使用的变量和函数,默认会阻止任何危险操作。

示例代码:

from simpleeval import SimpleEval
import math

def evaluate_option(asset_price, user_expr):
    evaluator = SimpleEval()
    # 只暴露允许的变量
    evaluator.names['Asset_price'] = asset_price
    # 只允许安全的数学函数
    evaluator.functions = {
        'max': max,
        'min': min,
        'sqrt': math.sqrt,
        'log': math.log,
        'exp': math.exp,
        'abs': abs
    }
    try:
        return evaluator.eval(user_expr)
    except Exception as e:
        return f"表达式错误: {str(e)}"

这个方案的优点是代码量极少,而且simpleeval已经帮你处理了大部分安全限制,适合快速开发。

方案3:自定义极简领域特定语言(DSL)

如果需要更严格的语法控制(比如只允许特定的操作格式),可以用PEG语法定义一个专属的极简数学语言,然后用lark或parsimonious这样的库来解析和求值。

比如定义一个只支持Asset_price变量、基础算术和指定函数的语法:

start: expression
expression: additive
additive: multiplicative (("+" | "-") multiplicative)*
multiplicative: primary (("*" | "/") primary)*
primary: number | variable | "(" expression ")" | function_call
function_call: func_name "(" expression ("," expression)* ")"
func_name: "max" | "min" | "sqrt" | "log"
variable: "Asset_price"
number: SIGNED_NUMBER
%import common.SIGNED_NUMBER
%import common.WS
%ignore WS

然后用lark解析用户输入的字符串,生成语法树后手动求值,这样完全没有执行任意代码的可能,安全性拉满,但开发成本比前两个方案高一些。

方案4:沙箱化执行(谨慎选择)

如果确实需要支持更复杂的逻辑(不推荐,因为期权逻辑本质是纯数学函数),可以考虑用沙箱技术,比如:

  • 使用PyPy的沙箱功能
  • 把用户代码放到隔离的Docker容器中执行
    但这种方案复杂度高,运维成本大,而且沙箱也不是100%安全(存在逃逸风险),对于小型资产模拟应用来说,前三个方案足够满足需求,没必要冒这个风险。

总结

推荐优先选择方案1或方案2:

  • 方案1完全可控,适合对安全性要求极高的场景
  • 方案2开发快速,适合小型项目快速落地
    两者都能完美满足“无状态变更的纯数学函数”需求,同时杜绝安全风险。

内容的提问来源于stack exchange,提问作者tma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:48:35