Py学习  »  Python

夏普比率交易策略:让 Python 量化交易更高效

数据科学实战 • 3 周前 • 99 次点击  

欢迎加入专注于财经数据与量化投研的【数据科学实战】知识星球!在这里,您将获取持续更新的《财经数据宝典》和《量化投研宝典》,这两部宝典相辅相成,为您在量化投研道路上提供明确指引。 我们提供了精选的国内外量化投研的 120+ 篇高质量文章,并每日更新最新研究成果,涵盖策略开发、因子分析、风险管理等核心领域。 无论您是量化投资新手还是经验丰富的研究者,星球社区都能帮您少走弯路,事半功倍,共同探索数据驱动的投资世界!

引言

加密货币市场以其高波动性而闻名,这既是机遇也是挑战。如何在这样的市场中建立一个有纪律的交易系统?本文将探讨一个基于夏普比率(Sharpe Ratio)的简单交易策略,该策略使用 Python、Backtrader 和 CCXT 库实现,可以帮助我们在比特币、以太坊等加密货币市场中做出更理性的交易决策。

策略核心思想

夏普比率是衡量风险调整后收益的重要指标。我们的策略基于以下简单逻辑:

  • 当短期夏普比率非常正面(如 > 2)时,市场表现强劲且稳定 → 生成做多信号
  • 当短期夏普比率非常负面(如 < -2)时,市场表现持续不佳 → 生成做空信号
  • 其他情况,保持观望

策略实现代码

首先,我们需要一个函数来计算滚动夏普比率并生成交易信号:

import numpy as np
import pandas as pd

def periods_per_year(timeframe: str) -> float:
    """根据时间框架计算年化系数"""
    n, u = int(timeframe[:-1]), timeframe[-1]
    if u == "d"return365.0 / n
    if u == "h"return365.0 * 24.0 / n
     if u == "m"return365.0 * 24.0 * 60.0 / n
    if u == "w"return52.0 / n
    raise ValueError(f"不支持的时间框架: {timeframe}")

def compute_sharpe_signals(df: pd.DataFrame, timeframe: str, window: int, 
                          upper: float=2.0, lower: float=-2.0)
:

    """计算滚动夏普比率并生成交易信号"""
    df = df.copy()
    df["ret"] = df["Close"].pct_change()  # 计算每个周期的收益率
    roll_mean = df["ret"].rolling(window).mean()
    roll_std = df["ret"].rolling(window).std()
    ann = np.sqrt(periods_per_year(timeframe))
    df["rolling_sharpe"] = (roll_mean / roll_std).replace([np.inf, -np.inf], np.nan)
    df["signal"] = 0
    df.loc[df["rolling_sharpe"] > upper, "signal"] = 1
    df.loc[df["rolling_sharpe"] < lower, "signal"] = -1
    df["signal_shifted"] = df["signal"].shift(1).fillna(0).astype(int)  # 今天决策,下一个周期行动
    return df.dropna()

风险管理设计

任何交易策略都需要风险管理。在我们的策略中,添加了以下保护措施:

  1. 止损:如果交易亏损超过 20%,立即退出
  2. 最大持有期限:最多持有 7 个交易周期(使用日线时为 7 天)
  3. 交易成本:考虑了手续费(0.04%)和滑点(0.05%)

这确保我们不仅追随信号,也在保护资金。

class SharpeSignalStrategy(bt.Strategy):
    """基于夏普比率的交易策略"""
    params = dict(hold_bars=7, stop_loss_pct=0.20, invest_pct=1.0, printlog=False)
    
    def __init__(self):
        self.sig = self.datas[0].signal_shifted
        self.entry_bar = None
        self.entry_price = None
        self.stop_order = None
    
    def place_stop(self, side: str):
        """设置止损单"""
        # 取消旧的止损单,然后相对于入场价格设置新的止损
        if self.stop_order:
            self.cancel(self.stop_order)
            self.stop_order = None
        if side == 'long':
            price = self.entry_price * (1 - self.p.stop_loss_pct)
            self.stop_order = self.sell(exectype=bt.Order.Stop, price=price)
        else:
            price = self.entry_price * (1 + self.p.stop_loss_pct)
            self.stop_order = self.buy(exectype=bt.Order.Stop, price=price)
    
    def notify_order(self, order):
        """订单状态更新"""
        if order.status == order.Completed:
            self.entry_price = order.executed.price
            self.entry_bar = len(self)
            self.place_stop('long'if order.isbuy() else'short')
    
    def next(self):
        """每个交易周期的策略逻辑"""
        s = int(self.sig[0])
        price = float(self.data.close[0])
        
        # 达到最大持有期限时退出
        if self.position and self.entry_bar isnotNone:
            if (len(self) - self.entry_bar) >= self.p.hold_bars:
                self.close()
                if self.stop_order: 
                    self.cancel(self.stop_order)
                    self.stop_order = None
                self.entry_bar = None
                self.entry_price = None
                return
        
        # 空仓时入场
        ifnot self.position and s != 0:
            notional = self.broker.getvalue() * self.p.invest_pct
            size = max(int(notional // price), 1)
            self.buy(size=size) if s == 1else self.sell(size=size)

数据获取

我们使用 CCXT 库从币安获取 USDC 交易对的历史数据:

import ccxt, time
from datetime import datetime, timezone
import  pandas as pd

def timeframe_to_ms(tf: str) -> int:
    """将时间框架转换为毫秒"""
    n, u = int(tf[:-1]), tf[-1]
    return n * {"m"60_000, "h"3_600_000, "d"86_400_000, "w"604_800_000}[u]

def fetch_ohlcv_paginated(exchange, symbol, timeframe, since_ms, until_ms=None, 
                         limit=1000, pause=1.0)
:

    """分页获取 OHLCV 数据"""
    out, cursor, tf_ms = [], since_ms, timeframe_to_ms(timeframe)
    until_ms = min(until_ms, int(time.time() * 1000))
    
    while cursor < until_ms:
        for attempt in range(5):
            try:
                batch = exchange.fetch_ohlcv(symbol, timeframe=timeframe, 
                                           since=cursor, limit=limit)
                break
            except (ccxt.NetworkError, ccxt.ExchangeError):
                time.sleep(pause * (attempt + 1))
        else:
            break
            
        ifnot batch: break
        out.extend([r for r in batch if r[0] < until_ms])
        cursor = batch[-1][0] + tf_ms
        time.sleep(pause)
        
        if len(batch) < limit and cursor + tf_ms >= until_ms: break
    return out

def get_ccxt_dataframe(symbol="ETH/USDC", timeframe="1d", start_date="2020-01-01"
                      end_date=None)
:

    """获取并处理 CCXT 数据为 DataFrame"""
    ex = ccxt.binance({"enableRateLimit"True"options": {"defaultType""spot"}})
    ex.load_markets()
    assert symbol in ex.markets, f"{symbol} 不在币安现货市场中"
    
    end_dt = datetime.now(timezone.utc) if end_date isNoneelse datetime.fromisoformat(end_date)
    start_dt = datetime.fromisoformat(start_date).replace(tzinfo=timezone.utc)
    
    raw = fetch_ohlcv_paginated(ex, symbol, timeframe, int(start_dt.timestamp() * 1000), 
                               int(end_dt.timestamp() * 1000))
    df = pd.DataFrame(raw, columns=["datetime","Open","High","Low","Close","Volume"])
    df["datetime"] = pd.to_datetime(df["datetime"], unit="ms", utc=True)
    return df.set_index("datetime")

回测示例

下面是一个实际的回测示例参数:

run_backtest(
    symbol="BTC/USDC",
    timeframe="1d",
    start_date="2021-01-01",
    end_date="2023-01-01",
    window=30,
    upper_threshold=2.0,
    lower_threshold=-2.0,
    hold_days=7,
    stop_loss_pct=0.20,
)

在测试中,我们可以得到以下结果:

最终组合价值:185,329.35
策略总回报率:61.70%
买入并持有回报率:-41.32%
夏普比率:0.8086785623645067
最大回撤:34.2717594145437%

策略变体尝试

我们可以调整策略参数来适应不同的市场条件:

# 日内交易实验:更多信号,不同的年化系数
run_backtest(
    symbol="ETH/USDC"
    timeframe="15m"
    start_date="2022-01-01",
    window=96,  # 约等于 15 分钟周期的 1 天
    upper_threshold=2.0
    lower_threshold=-2.0
    hold_days=3
    stop_loss_pct=0.15
)

# 更严格的过滤器:更少的交易,更高的确信度
run_backtest(
    symbol="BTC/USDC"
    timeframe="1d"
    start_date="2020-01-01",
    window=45
    upper_threshold=2.5
    lower_threshold=-2.5
    hold_days=5
    stop_loss_pct=0.25
)

策略观察与分析

通过对回测结果的分析,我们可以得出以下观察:

  1. 由于夏普比率 > 2 或 < -2 的情况并不常见,所以该策略交易频率较低
  2. 当确实发生交易时,策略能够捕捉到强劲的趋势表现
  3. 止损机制有效地切断了亏损交易
  4. 与简单的买入持有相比,该策略可能减少了回撤,但在强劲的牛市中可能表现不佳
  5. 这符合预期:基于夏普的过滤器更注重风险控制,而非最大化原始收益

策略局限性与未来改进方向

本策略仍有以下局限性:

  1. 阈值选择较为主观:我们选择了 ±2.0,但这些值可以并且应该经过调整
  2. 回顾窗口很重要:30 天只是一种选择,更短或更长的窗口表现会不同
  3. 尚未准备好实盘:这是研究回测,不是生产代码。实盘交易需要订单簿数据、执行逻辑和稳健测试
  4. 尚无仓位管理逻辑:目前每次交易都是全仓,基于风险的仓位管理可能改善结果

可能的扩展方向:

  • 尝试日内数据(15 分钟 K 线)而非日线
  • 将夏普比率与其他信号(动量、波动率等)结合
  • 应用前向优化以减少过拟合
  • 增加动态止损策略

总结

本文展示了如何将夏普比率——一个经典的风险调整性能指标——转化为加密货币市场中的简单交易信号。结果并不神奇,但这正是重点:在早期研究中,实验比优化更重要。通过在 Backtrader 中使用币安数据构建类似的小型原型,我们可以快速测试想法,从结果中学习,并决定哪些方向值得深入探索。

对于 Python 量化交易爱好者来说,这种方法提供了一个良好的起点,帮助你建立自己的交易系统。记住,成功的量化交易不仅仅是复杂的算法,更在于纪律、风险管理和持续的学习与改进。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。

财经数据与量化投研知识社区

核心权益如下:

  1. 赠送《财经数据宝典》完整文档,汇集多年财经数据维护经验
  2. 赠送《量化投研宝典》完整文档,汇集多年量化投研领域经验
  3. 赠送《PyBroker-入门及实战》视频课程,手把手学习量化策略开发
  4. 每日分享高质量量化投研文章(已更新120+篇)、代码和相关资料
  5. 定期更新高频财经数据
  6. 参与年度不少于 10 次专属直播与录播课程
  7. 与核心开发者直接交流,解决实际问题
  8. 获取专业微信群交流机会和课程折扣

星球已有丰富内容积累,包括量化投研论文、财经高频数据、 PyBroker 视频教程、定期直播、数据分享和答疑解难。适合对量化投研和财经数据分析有兴趣的学习者及从业者。欢迎加入我们!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐

《Python编程:从入门到实践(第3版)》是一本广受欢迎的 Python 入门经典教材,由经验丰富的程序员 Eric Matthes 编写。该书采用循序渐进的教学方式,从基础语法讲解到实战项目开发,内容编排合理,实例丰富,语言通俗易懂。全书配有大量练习题和完整项目实战,包括数据可视化、网络爬虫、Web 应用开发等,让读者在实践中掌握编程技巧。第3版还增加了 f-string、海龟绘图等最新的 Python 特性内容。这本书不仅适合零基础读者入门学习,也非常适合想系统掌握 Python 的编程爱好者以及数据分析、人工智能等领域的学习者。它不仅教授编程知识,更注重培养读者的编程思维,是一本非常值得投资的 Python 学习指南。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/185937
 
99 次点击