惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
O
OpenAI News
V
V2EX
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
小众软件
小众软件
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - Franky
Security Latest
Security Latest
S
SegmentFault 最新的问题
Project Zero
Project Zero
Spread Privacy
Spread Privacy
K
Kaspersky official blog
J
Java Code Geeks
V
Vulnerabilities – Threatpost
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
WordPress大学
WordPress大学
量子位
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
腾讯CDC
Cyberwarzone
Cyberwarzone
IT之家
IT之家
GbyAI
GbyAI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
云风的 BLOG
云风的 BLOG
T
Troy Hunt's Blog
D
Docker

lucifer的网络博客

我不知道的《易经》 | lucifer的网络博客 加密交易的风险管理:仓位、止损与资金分配 | lucifer的网络博客 永续合约:无到期日的杠杆交易是如何运作的 | lucifer的网络博客 代币经济学:如何判断一个代币值不值得买 | lucifer的网络博客 稳定币机制全解:从 USDT 到 UST 崩盘的启示 DeFi 保险:谁来保护你的链上资产? | lucifer的网络博客 Claude Code 高级指南:驾驭上下文的艺术 | lucifer的网络博客 NotebookLM:AI 驱动的知识炼金术,瞬间转化复杂信息为易懂洞见 | lucifer的网络博客 当下流动性池分析 | lucifer的网络博客 苹果AI转型:Siri升级与生态潜力 | lucifer的网络博客 我们是如何让 JSON.stringify 的速度提升两倍以上的 | lucifer的网络博客 ACP:AI 代理协作协议在 Web3 空投自动化中的应用 | lucifer的网络博客 从零构建一个真正工作的简单搜索引擎 | lucifer的网络博客 限制即提示:算法解题的隐秘指引 | lucifer的网络博客 如何阅读财报:从入门到进阶指南 | lucifer的网络博客 Freqtrade 量化风暴:Python 铸就的加密交易自动化帝国 | lucifer的网络博客 DePIN 觉醒:Web3 如何让你的闲置硬件变黄金 | lucifer的网络博客 自己动手制作 Web3 代币估值表 | lucifer的网络博客 当 RWA 遇上幸福小区玉米 | lucifer的网络博客 探索 DeFi 借贷与收益率交易的无限潜力 | lucifer的网络博客 Uniswap V4:更灵活、更高效的去中心化交易所 | lucifer的网络博客 Web3 科学家之如何查看鲸鱼挂单数据 | lucifer的网络博客 抖音也开始搞外卖大战了? | lucifer的网络博客 Web3 的 Anti MEV:深入剖析“夹”与反制之道 | lucifer的网络博客 《小菜的交易系统》-一个完整的简易的交易系统 | lucifer的网络博客 回炉重铸, 91 天见证不一样的自己(第 16 期)
Freqtrade 进阶:把多个弱信号拧成一根绳——多信号评分策略实战 | lucifer的网络博客
2026-07-20 · via lucifer的网络博客

大家好,我是 Lucifer,又来聊聊 Web3 的那些有趣事儿了。今天咱们来聊聊 Freqtrade。

上一篇我们从零搭好了 Freqtrade,写了个最简单的 SMA 交叉策略。很多人跑完回测后跟我说:“这策略也太简单了,实盘根本扛不住。”

说得对。那篇是入门用的“Hello World”。真正能活下来的策略,从来不是依赖某一个“神级指标”,而是把一堆看起来不怎么样的弱信号,用正确的方式组合起来。

今天这篇是正式续篇。我们会把「组合弱信号」这个核心思想,落地成一个能直接在 Freqtrade 里跑的策略。

先补一点前置知识

在写策略之前,先把两个最基础的概念说清楚。

1. 什么是信号?

信号不是“我觉得这个币要涨”这种模糊感觉,而是一个可量化、有方向、可重复的数据点。比如:

  • RSI 从 30 以下回升
  • 快线上穿慢线
  • 成交量放大到均线的 1.3 倍

这些都是信号。单个信号通常很弱,准确率往往只有 50% 多一点点。

2. dataframe.loc 是干什么的?

Freqtrade 策略的核心是一张表格(DataFrame),每一行是一根 K 线,每一列是价格或指标。

dataframe.loc[条件, 列名] = 值 的意思是:
找到符合条件的那些 K 线,给它们打上标记。

比如:

1
2
3
4
dataframe.loc[
(dataframe['rsi'] < 30) & (dataframe['volume'] > 0),
'enter_long'
] = 1

这句话的意思就是:“凡是 RSI 低于 30 且有成交量的 K 线,都标记为可以买入。”

后面所有买卖逻辑,本质上都是在用 .loc 给不同的行打分或打标记。

为什么要组合多个弱信号?

单一信号最大的问题是:它大部分时候都是错的

量化里把单个信号的准确度叫 IC(Information Coefficient)。机构级信号的 IC 通常只有 0.05~0.15,意思是它跟市场真实走势的相关性非常弱。

但主动管理基本定律告诉我们:

IR = IC × √N

系统整体表现 = 单个信号准确度 × 独立信号数量的平方根。

所以,与其苦苦寻找一个“永远正确”的神级信号,不如收集几十个“稍微有点准”的弱信号,然后把它们正确地组合起来。噪音会互相抵消,优势会叠加。

这就是今天策略的核心思路。

策略思路

我们不追求某一个指标特别准,而是让多个弱信号一起“投票”。

具体做法:

  1. 选取 6 个相关性较低的常见信号(动量、均值回归、成交量、趋势强度等)。
  2. 每个信号根据自己的条件给当前 K 线打分(有的 +1,有的 +2)。
  3. 把所有分数加总,得到一个综合置信度分数。
  4. 只有当综合分数达到设定阈值时,才允许开仓。
  5. 出场逻辑反过来,用反向信号打分,达到阈值就平仓。

这样做的好处是:

  • 不再要求所有条件同时成立(那会让信号极少)。
  • 不同信号可以有不同权重(比如 RSI 回升给 2 分,普通交叉只给 1 分)。
  • 噪音被分散,真正有优势的时刻更容易被识别出来。

这已经是文章里“组合引擎”思想的简化落地版。真正的机构还会做去均值、正交化、残差加权等更复杂的步骤,但对我们现阶段来说,评分投票已经足够体现核心逻辑。

完整策略代码(辅助参考)

把下面代码保存为 user_data/strategies/MultiSignalScore.py 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from freqtrade.strategy import IStrategy, IntParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class MultiSignalScore(IStrategy):
"""
多弱信号评分策略
核心思想:用多个低 IC 信号投票,提升整体系统表现
"""

INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False

minimal_roi = {
"0": 0.035,
"40": 0.02,
"90": 0.01
}
stoploss = -0.055
trailing_stop = True
trailing_stop_positive = 0.012
trailing_stop_positive_offset = 0.022
trailing_only_offset_is_reached = True

startup_candle_count = 50


buy_score_threshold = IntParameter(4, 8, default=5, space='buy')
sell_score_threshold = IntParameter(3, 6, default=4, space='sell')

def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

dataframe['sma_fast'] = ta.SMA(dataframe, timeperiod=10)
dataframe['sma_slow'] = ta.SMA(dataframe, timeperiod=30)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']


dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lower'] = bollinger['lower']
dataframe['bb_middle'] = bollinger['mid']
dataframe['bb_upper'] = bollinger['upper']


dataframe['adx'] = ta.ADX(dataframe)
dataframe['volume_sma'] = ta.SMA(dataframe, timeperiod=20, price='volume')

return dataframe

def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['buy_score'] = 0


dataframe.loc[
(dataframe['sma_fast'] > dataframe['sma_slow']) &
(dataframe['sma_fast'].shift(1) <= dataframe['sma_slow'].shift(1)),
'buy_score'] += 1


dataframe.loc[
(dataframe['macdhist'] > 0) &
(dataframe['macdhist'].shift(1) <= 0),
'buy_score'] += 1


dataframe.loc[
(dataframe['rsi'] > 30) &
(dataframe['rsi'].shift(1) <= 30),
'buy_score'] += 2


dataframe.loc[
(dataframe['close'] < dataframe['bb_lower'] * 1.01),
'buy_score'] += 1


dataframe.loc[
(dataframe['volume'] > dataframe['volume_sma'] * 1.3),
'buy_score'] += 1


dataframe.loc[
(dataframe['adx'] > 20) &
(dataframe['adx'] > dataframe['adx'].shift(1)),
'buy_score'] += 1


dataframe.loc[
(dataframe['buy_score'] >= self.buy_score_threshold.value) &
(dataframe['volume'] > 0),
'enter_long'] = 1

return dataframe

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['sell_score'] = 0

dataframe.loc[
(dataframe['sma_fast'] < dataframe['sma_slow']) &
(dataframe['sma_fast'].shift(1) >= dataframe['sma_slow'].shift(1)),
'sell_score'] += 1

dataframe.loc[
(dataframe['macdhist'] < 0) &
(dataframe['macdhist'].shift(1) >= 0),
'sell_score'] += 1

dataframe.loc[
(dataframe['rsi'] > 70),
'sell_score'] += 2

dataframe.loc[
(dataframe['close'] > dataframe['bb_upper'] * 0.99),
'sell_score'] += 1

dataframe.loc[
(dataframe['sell_score'] >= self.sell_score_threshold.value) &
(dataframe['volume'] > 0),
'exit_long'] = 1

return dataframe

怎么跑起来

下载数据后执行回测:

1
freqtrade backtesting --strategy MultiSignalScore --timerange 20240701-20250701 --export trades

想优化阈值参数,可以用:

1
freqtrade hyperopt --strategy MultiSignalScore --hyperopt-loss SharpeHyperOptLoss --spaces buy sell --epochs 100

总结

一个现实中真正可用的策略基本就是按照这个方式去叠加信号, 给不同信号分配不同的权重等等。

量化最反直觉的一点是:你越执着于寻找“永远正确”的信号,你就越容易亏钱。真正能持续活下来的人,都在默默收集那些“稍微有点准”的弱信号,然后用数学把它们拧成一股绳。

 上一页

易经 读后感