σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academy学习路线
Ctrl K
AO
+1.2k 位学习者
登录加入 ↗
学习路线
AO
+1.2k 位学习者
登录
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python 学习路线›模块 1 · 面向量化的 Python 基础›主题 1.10·~40分钟入门

作用域与闭包

路线进度模块 1/11 · 主题 10/33
1.10 作用域与闭包
—实验得分— / 100总 XP—连续天数—

名字存在于哪里

名字的作用域是程序中可以使用它的范围。在函数内部创建的名字是局部的:只在该函数运行期间存在,外部看不到。在文件最顶层创建的名字是全局的:函数可以读取它。Python 遇到一个名字时,先在函数内部找,再找外层函数,然后是文件,最后是内置名字。

rate = 0.001            # global: defined outside any function

def fee(notional):
    minimum = 1.0       # local: exists only during the call
    return max(minimum, notional * rate)

print(fee(36850.0))     # 36.85
# print(minimum)        # would raise NameError: minimum is not defined

赋值会创建局部名字

在函数里读取全局名字没有问题。但在函数里对一个名字赋值,会创建一个新的局部名字,即使存在同名的全局名字,全局的那个也不会被改动。这保护了你的数据,也是为什么在函数里修改全局状态需要额外的关键字(global),而你应该很少需要它。请把值作为参数传入,用 return 把结果交出来。

# Assigning inside a function makes a new local name
balance = 1000

def deposit(amount):
    balance = amount     # a different, local balance
    return balance

print(deposit(50))  # 50
print(balance)      # 1000 (the global one is unchanged)

生成函数的函数

函数可以在内部定义另一个函数并用 return 返回它。内层函数即使在外层函数结束之后,仍能访问外层的变量。这种“函数加上它记住的值”的组合叫做闭包。这是根据设置造出现成工具的好办法:一个用于股票的计算器,另一个用于 ETF。

# A closure remembers the variables of the function that made it
def make_fee_calculator(rate, minimum):
    def fee(notional):
        return max(minimum, notional * rate)
    return fee

stock_fee = make_fee_calculator(0.001, 1.0)
etf_fee = make_fee_calculator(0.0005, 0.5)
print(stock_fee(36850.0))  # 36.85
print(etf_fee(36850.0))    # 18.425

在多次调用之间保持的状态:nonlocal

闭包还可以修改它记住的值。因为赋值会创建新的局部名字,内层函数要先声明 nonlocal count,意思是“这个名字属于外层函数”。外层函数每被调用一次,就会创建一份各自独立的记忆,所以两个计数器不会互相干扰。订单号生成器或滚动平均就是这样在没有全局变量的情况下保存状态的。

# nonlocal lets the inner function rebind the outer variable
def make_counter():
    count = 0
    def next_id():
        nonlocal count
        count += 1
        return count
    return next_id

order_ids = make_counter()
print(order_ids(), order_ids(), order_ids())  # 1 2 3
other = make_counter()
print(other())                                # 1 (its own count)
# State between calls: an exponential moving average
def make_ema(alpha):
    value = None
    def update(price):
        nonlocal value
        value = price if value is None else alpha * price + (1 - alpha) * value
        return value
    return update

ema = make_ema(0.5)
print(ema(100.0), ema(110.0), ema(110.0))  # 100.0 105.0 107.5
闭包保留的是变量本身,而不是副本内层函数在运行时才去查找这个变量,所以看到的总是最新值。计数器和平均值能工作正是因为这一点;同样的原因也使得在循环里创建的闭包看到的是循环变量的最终值,除非你把它作为参数或默认值传进去。
  • 函数内赋值的名字是局部的;数据通过参数传入,用 return 传出。
  • 构建并返回函数的函数,把设置打包成一个工具。
  • 需要在多次调用间保留的状态用 nonlocal,每个闭包各有一份记忆。

下一节:列表推导式,用一行清晰的代码从一个列表构建出新列表。

实验:评分内容

  • 用 make_counter 构建会记住计数的计数器25 分
  • 用 make_fee_calculator 把费用设置打包成函数25 分
  • 用 make_ema 在多次调用之间保持移动平均25 分
  • 用 make_limit_checker 根据限价生成价格检查函数25 分
资源管理器 · lab-10-1
▾ lab-10-1
…
bash · 浏览器内
learner@quantcraft:~/lab-10-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-10-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续