3  函数定义 算术平均收益率计算

3.1 引言函数在金融编程中的重要性

函数是程序设计中的基本抽象单元,它将一系列操作封装起来,使代码更加模块化、可重用和易维护。在金融计算中,函数尤其重要,因为许多金融指标和模型(如收益率、波动率、VaR等)需要反复计算。

理论背景:函数式编程思想

从编程范式来看,函数体现了几种核心思想: 1. 抽象(Abstraction): 隐藏实现细节,只暴露接口 2. 分解(Decomposition): 将复杂问题分解为简单子问题 3. 复用(Reuse): 一次编写,多次调用

Python支持多种编程范式,包括函数式编程。理解函数的数学定义(输入到输出的映射)对编写高质量金融代码至关重要。

3.2 算术平均收益率

算术平均(Arithmetic Mean)是最常用的集中趋势度量,在金融中广泛用于计算平均收益率。

3.2.1 数学定义与性质

对于收益率序列 \(r_1, r_2, ..., r_n\), 算术平均定义为:

\[ \bar{r} = \frac{1}{n} \sum_{i=1}^{n} r_i = \frac{r_1 + r_2 + \cdots + r_n}{n} \]

数学性质: 1. 线性性: \(\overline{aX + bY} = a\bar{X} + b\bar{Y}\) 2. 最小二乘性: 算术平均使误差平方和最小 3. 敏感性: 对极端值敏感,受异常值影响大

金融含义: 算术平均收益率适用于: - 单期期望收益率的估计 - 投资组合绩效评估 - 风险调整后收益率的基准

易混淆概念辨析:算术平均 vs. 几何平均

特性 算术平均 几何平均
公式 \(\bar{r}_A = \frac{1}{n}\sum r_i\) \(\bar{r}_G = \left[\prod(1+r_i)\right]^{1/n} - 1\)
应用场景 单期期望收益 多期复利增长
数值关系 \(\bar{r}_A \geq \bar{r}_G\) 波动率越大,差距越大
金融含义 期初财富的期望增长率 期末财富的实际增长率

关键理解: 如果你想知道”平均来说每期赚多少”,用算术平均;如果你想计算”长期复利增长”,用几何平均。

3.2.2 Python函数定义基础

Python提供多种定义函数的方式:

列表 3.1
# ==================== 方式1: 标准def定义 ====================
# def关键字用于定义函数,是Python中最常用的函数定义方式
# arithmetic_mean是函数名,遵循标识符命名规则(字母、数字、下划线,不能以数字开头)
# numbers是参数名,括号内是函数的参数列表
def arithmetic_mean(numbers):  # 定义函数名为arithmetic_mean,接收一个参数numbers
    """计算算术平均数"""  # 文档字符串(Docstring),用三引号包围,描述函数功能
    return sum(numbers) / len(numbers)  # return返回计算结果:sum求和,len求长度,/计算除法

# ==================== 方式2: lambda表达式(匿名函数) ====================
# lambda用于定义匿名函数,适合简单的单行函数
# 语法格式: lambda parameters: expression
# x是参数,sum(x) / len(x)是返回表达式
arithmetic_mean_lambda = lambda x: sum(x) / len(x)  # 定义lambda函数并赋值给变量

# ==================== 测试两种定义 ====================
# 创建测试数据列表,包含5个数值
data = [1, 2, 3, 4, 5]  # 方括号创建列表,元素用逗号分隔
print(arithmetic_mean(data))  # 调用def定义的函数,print输出结果: 3.0
print(arithmetic_mean_lambda(data))  # 调用lambda函数,输出: 3.0

代码深度解析:

  1. def语句的解剖:

    def function_name(parameters):
        """文档字符串(Docstring)"""
        function_body
        return value
    • def: 函数定义关键字
    • function_name: 函数名,遵循标识符命名规则
    • parameters: 参数列表,多个参数用逗号分隔
    • return: 返回值,无return则返回None
  2. lambda表达式的限制:

    • 只能包含单个表达式
    • 不能包含语句(如if, for, while)
    • 自动返回表达式的值
    • 适合简单函数,提高代码简洁性
  3. Python中的函数是一等对象:

    • 可以赋值给变量
    • 可以作为参数传递
    • 可以作为返回值
    • 可以存储在数据结构中

3.2.3 任务实现计算股票的算术平均收益率

列表 3.2
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
A_mean=lambda x:sum(x)/len(x)  # 定义匿名函数A_mean
sh000001=[26.468,-10.7081,2.8477,43.5348,13.4337]  # 定义列表sh000001
print(round(A_mean(sh000001),2))  # 输出四舍五入后的计算结果

代码逐步解析:

  1. lambda函数的拆解:

    lambda x: sum(x) / len(x)
    #      ^      ^^^^^^^^^^^^
    #      |           |
    #    参数      返回表达式
    • 参数x期望是一个可迭代对象(如列表)
    • sum(x)计算列表元素总和
    • len(x)获取列表长度
    • /执行浮点除法(Python 3特性)
  2. 数据处理流程:

    [26.468, -10.7081, 2.8477, 43.5348, 13.4337]
          ↓
    sum = 75.5762
    len = 5
          ↓
    mean = 75.5762 / 5 = 15.11524
          ↓
    round(15.11524, 2) = 15.11
  3. round函数的行为:

    • round(number, ndigits): 四舍五入到指定小数位
    • ndigits=2: 保留2位小数
    • 注意: Python 3采用”银行家舍入”(Round Half to Even)

金融实例分析:

上证指数在样本期内的平均年收益率为15.11%,这意味着: - 期望收益: 基于历史数据,投资者可期望每年获得约15.11%的回报 - 风险评估: 需结合波动率(标准差)评估风险调整后收益 - 基准比较: 可与无风险利率、其他市场指数进行比较

3.2.4 函数的进阶应用

在实际金融分析中,我们通常需要定义更复杂的函数:

列表 3.3
# ==================== 定义完整函数 ====================
# def关键字定义标准函数
# calculate_mean_return是函数名,采用snake_case命名规范
# returns是必选参数:收益率列表
# decimals=2是可选参数:保留小数位数,默认值为2
def calculate_mean_return(returns, decimals=2):  # 函数定义,包含两个参数
    """
    计算算术平均收益率

    参数:
        returns (list): 收益率序列
        decimals (int): 保留小数位数,默认为2

    返回:
        float: 算术平均收益率

    异常:
        ValueError: 如果输入为空列表或包含非数值
    """
    # ==================== 参数验证 ====================
    # if语句进行条件判断
    # not returns检查列表是否为空
    # raise用于抛出异常,中断程序执行
    if not returns:  # 如果returns列表为空
        raise ValueError("收益率列表不能为空")  # 抛出ValueError异常并提示

    # all()函数检查所有元素是否满足条件
    # isinstance(r, (int, float))检查r是否为整数或浮点数
    # for r in returns遍历returns列表中的每个元素
    # 这行代码确保所有收益率都是数值类型
    if not all(isinstance(r, (int, float)) for r in returns):  # 如果有非数值元素
        raise ValueError("收益率必须为数值类型")  # 抛出异常提示数据类型错误

    # ==================== 计算平均值 ====================
    # sum(returns)计算收益率总和
    # len(returns)获取收益率个数
    # / 执行除法,得到算术平均值
    mean = sum(returns) / len(returns)  # 计算算术平均收益率

    # ==================== 格式化返回 ====================
    # round函数四舍五入到指定小数位
    # mean是待舍入的数值
    # decimals是保留的小数位数
    # return将结果返回给调用者
    return round(mean, decimals)  # 返回四舍五入后的平均值

# ==================== 使用示例 ====================
# try-except用于捕获和处理异常
# try块包含可能抛出异常的代码
try:  # 尝试执行以下代码
    # 调用calculate_mean_return函数
    # sh000001是收益率数据
    # decimals=3指定保留3位小数
    result = calculate_mean_return(sh000001, decimals=3)  # 调用函数并接收返回值
    # f-string格式化输出
    # {result:.3f}表示格式化为浮点数,保留3位小数
    print(f"上证指数平均收益率: {result:.3f}%")  # 输出格式化的结果
# except块处理特定类型的异常
except ValueError as e:  # 捕获ValueError异常并赋值给e
    # e是异常对象,包含错误信息
    print(f"计算错误: {e}")  # 输出错误信息

代码质量要素:

  1. 文档字符串(Docstring):
    • 描述函数功能
    • 说明参数类型和含义
    • 说明返回值
    • 列出可能抛出的异常
  2. 参数验证:
    • 检查输入有效性
    • 提供清晰的错误信息
    • 防止程序崩溃
  3. 默认参数:
    • 提供合理默认值
    • 增强函数灵活性
    • 提高使用便利性

3.2.5 lambda表达式的高级用法

lambda表达式在金融数据处理中非常实用,特别是在结合map, filter, reduce等函数时:

列表 3.4
# ==================== 导入模块 ====================
# from...import语句从模块中导入特定函数
# functools是Python标准库,提供高阶函数工具
# reduce函数用于累积计算
from functools import reduce  # 导入reduce函数

# ==================== 创建示例数据 ====================
# portfolio_returns是一个字典
# 键(Key): 股票名称(Stock A, Stock B, Stock C)
# 值(Value): 收益率列表,每个列表包含5个交易日的收益率
portfolio_returns = {  # 花括号创建字典
    'Stock A': [0.05, 0.03, -0.02, 0.04, 0.06],  # 股票A的收益率
    'Stock B': [0.08, 0.02, 0.01, -0.03, 0.05],  # 股票B的收益率
    'Stock C': [-0.01, 0.04, 0.07, 0.02, 0.03]   # 股票C的收益率
}

# ==================== 使用map应用函数 ====================
# map(function, iterable)将函数应用于可迭代对象的每个元素
# lambda returns: sum(returns) / len(returns)定义匿名函数计算平均收益率
# portfolio_returns.values()获取字典的所有值(收益率列表)
# map返回迭代器,需要用list()转换为列表
mean_calculator = lambda returns: sum(returns) / len(returns)  # 定义计算均值的lambda函数
mean_returns = list(map(mean_calculator, portfolio_returns.values()))  # 对每只股票计算平均收益

# f-string格式化输出
# mean_returns是一个列表,包含3只股票的平均收益率
print(f"各股票平均收益率: {mean_returns}")  # 输出所有股票的平均收益率

# ==================== 使用filter筛选 ====================
# filter(function, iterable)根据函数条件筛选元素
# lambda r: r > 0定义条件:收益率大于0
# 中括号创建列表,包含多个收益率数据
# list()将filter对象转换为列表
positive_returns = lambda r: r > 0  # 定义判断是否为正收益的lambda函数
filtered_stocks = list(filter(positive_returns, [0.05, -0.02, 0.03, -0.01]))  # 筛选正收益
print(f"正收益股票: {filtered_stocks}")  # 输出筛选结果

# ==================== 使用reduce累积计算 ====================
# reduce(function, sequence, initial)对序列进行累积计算
# lambda x, y: x * (1 + y)定义累积函数:计算复利
# returns是收益率列表
# 1是初始值(代表初始本金1)
# -1在最后减去1,得到净收益率
returns = [0.05, 0.03, -0.02, 0.04]  # 创建收益率列表
cumulative_return = reduce(lambda x, y: x * (1 + y), returns, 1) - 1  # 计算累积收益率
# f-string格式化,.4f表示保留4位小数
print(f"累积收益率: {cumulative_return:.4f}")  # 输出累积收益率

3.3 算术平均的局限性与改进

3.3.1 局限性分析

算术平均虽然简单直观,但在金融应用中存在重要局限:

  1. 极端值敏感: 一个极端值会显著影响平均值

    # 示例:极端值的影响
    normal_returns = [0.05, 0.03, 0.04, 0.06, 0.02]
    with_outlier = normal_returns + [0.50]  # 添加极端收益率
    
    print(f"正常平均: {sum(normal_returns)/len(normal_returns):.4f}")
    print(f"含极端值: {sum(with_outlier)/len(with_outlier):.4f}")
  2. 不适用于多期增长: 算术平均会高估长期复利增长

  3. 分布假设: 隐含假设收益率对称分布,但实际金融收益常呈偏态分布(Skewed Distribution)

3.3.2 改进方案稳健统计量

列表 3.5
# ==================== 导入必要的库 ====================
# numpy是Python科学计算基础库,提供高性能数组运算
# as np给numpy起别名,简化代码书写
import numpy as np  # 导入numpy库并简写为np

# ==================== 创建示例数据 ====================
# returns列表包含收益率数据,其中包含极端值0.50和-0.10
# 这种数据在金融中很常见,突发事件会造成极端收益
returns = [0.05, 0.03, 0.04, 0.50, 0.02, -0.10, 0.06]  # 包含极端值的收益率数据

# ==================== 1. 算术平均 ====================
# np.mean()计算算术平均值
# arithmetic_mean对极端值敏感,会被0.50拉高
arithmetic_mean = np.mean(returns)  # 计算算术平均值

# ==================== 2. 中位数 ====================
# np.median()计算中位数
# 中位数对极端值不敏感,是稳健的统计量
# 中位数是将数据排序后位于中间位置的值
median = np.median(returns)  # 计算中位数

# ==================== 3. 截尾平均 ====================
# scipy是科学计算库,stats模块提供统计函数
from scipy import stats  # 从scipy导入stats模块

# stats.trim_mean(data, proportion)计算截尾平均
# 0.1表示从两端各截去10%的数据
# 这样可以消除极端值的影响
trimmed_mean = stats.trim_mean(returns, 0.1)  # 去掉两端各10%后计算平均

# ==================== 4. 缩尾平均 ====================
# stats.mstats.winsorize将极端值替换为分位数值
# limits=[0.1, 0.1]表示将最低10%和最高10%的值替换
# 这比直接删除数据更保守
winsorized_mean = stats.mstats.winsorize(np.array(returns), limits=[0.1, 0.1]).mean()  # 转为数组后缩尾计算平均

# ==================== 输出比较结果 ====================
# f-string格式化输出,.4f保留4位小数
# 可以看到不同统计量的差异
print(f"算术平均: {arithmetic_mean:.4f}")  # 输出算术平均
print(f"中位数: {median:.4f}")  # 输出中位数
print(f"截尾平均: {trimmed_mean:.4f}")  # 输出截尾平均
print(f"缩尾平均: {winsorized_mean:.4f}")  # 输出缩尾平均

选择建议: - 正常分布数据: 使用算术平均 - 含极端值: 使用中位数或截尾平均 - 投资组合基准: 使用算术平均(与CAPM一致) - 长期绩效评估: 使用几何平均

实际应用指南:

在实际金融分析中,选择合适的集中趋势度量至关重要。对于简单一次性函数,使用lambda可以提高代码简洁性;对于复杂逻辑,应使用def定义并添加完整文档字符串;同时始终验证输入参数的有效性,确保函数的健壮性。理解算术平均与几何平均的区别——前者用于单期期望收益估计,后者用于多期复利增长计算——是正确应用这些统计量的基础。

下一步学习: 在下一章中,我们将学习Python的内置函数,这些函数提供了丰富的基础功能,可以大大提高编程效率。