第 10 章 · 04 SQL 查询速查 本节摘要:freqtrade 把所有交易记录、配对锁、订单、自定义数据存进 SQLite 数据库(默认 )。虽然 Telegram 和 REST API 能查大多数信息,但有时你想直接问数据库——做自定义盈亏统计、排查异常交易、修复手动卖出后状态不同步的仓位。本节讲清几件事:怎么用 sqlite3(或 Docker 里的 sqlite3、或可视化工具 SqliteBrowser)打开数据库;trades 表的核心字段;常用的盈亏统计查询(按币种、按月、按出场原因);以及危险的写操作(修复手动平仓、删除交易)——强调绝对不要在 bot 运行时写数据库,且务必先备份。 内容来源:原项目文档 ,汉化并套用体系化模板。
本节摘要:freqtrade 把所有交易记录、配对锁、订单、自定义数据存进 SQLite 数据库(默认
user_data/tradesv3.sqlite)。虽然 Telegram 和 REST API 能查大多数信息,但有时你想直接问数据库——做自定义盈亏统计、排查异常交易、修复手动卖出后状态不同步的仓位。本节讲清几件事:怎么用 sqlite3(或 Docker 里的 sqlite3、或可视化工具 SqliteBrowser)打开数据库;trades 表的核心字段;常用的盈亏统计查询(按币种、按月、按出场原因);以及危险的写操作(修复手动平仓、删除交易)——强调绝对不要在 bot 运行时写数据库,且务必先备份。
内容来源:原项目文档
docs/sql_cheatsheet.md,汉化并套用体系化模板。
⚠️ 风险提示:SQL 写操作(update/insert/delete)有数据损坏风险。永远先备份数据库,且绝不在 bot 连着数据库时执行写操作——会导致不可恢复的损坏。优先用 Telegram/REST API 的命令(如
/delete)而非直接写 SQL。
阅读完本节,你应当能够:
freqtrade 默认用 SQLite,数据库文件一般在 user_data/tradesv3.sqlite(配置里 db_url 可改,也支持 PostgreSQL/MariaDB)。
安装 sqlite3:
sudo apt-get install sqlite3docker compose exec freqtrade /bin/bash sqlite3 <database-file>.sqlite
打开与基本操作:
sqlite3 .open <filepath> .tables # 列出所有表 .schema <table_name> # 看表结构 SELECT * FROM trades; # 查所有交易
💡 切换 PostgreSQL/MariaDB 后,查询语句相同,只是客户端工具不同。
trades 表是核心,记录每一笔交易。关键字段(简化):
| 字段 | 含义 |
|---|---|
id |
交易 ID |
pair |
交易对 |
is_short |
是否做空(0/1) |
is_open |
是否仍开仓(0/1) |
open_date / close_date |
开/平仓时间 |
open_rate / close_rate |
开/平仓价格 |
amount |
交易数量 |
stake_amount |
投入本金 |
close_profit |
平仓收益率(小数,如 0.05 = 5%) |
close_profit_abs |
平仓绝对盈亏(计价币) |
exit_reason |
出场原因(exit_signal/stop_loss/roi/...) |
enter_tag |
入场标签 |
leverage |
杠杆倍数 |
fee_open / fee_close |
开/平仓费率 |
其他常用表:
orders:每笔交易的所有订单(含部分成交)。pairlocks:被锁定的交易对(protection 触发)。trades / orders / trade_custom_data 之间通过 ft_trade_id 关联。按交易对统计盈亏:
SELECT pair, COUNT(*) AS trade_count, ROUND(SUM(close_profit_abs), 4) AS total_profit, ROUND(AVG(close_profit) * 100, 2) AS avg_profit_pct, ROUND(SUM(CASE WHEN close_profit > 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) AS winrate_pct FROM trades WHERE is_open = 0 GROUP BY pair ORDER BY total_profit DESC;
这会列出每个交易对的:交易次数、总绝对盈亏、平均收益率、胜率,按总盈亏降序——快速找出最赚钱和最亏钱的币。
按月统计:
SELECT strftime('%Y-%m', close_date) AS month, COUNT(*) AS trades, ROUND(SUM(close_profit_abs), 4) AS profit_abs, ROUND(AVG(close_profit) * 100, 2) AS avg_profit_pct FROM trades WHERE is_open = 0 GROUP BY month ORDER BY month DESC;
按出场原因统计(诊断策略问题):
SELECT exit_reason, COUNT(*) AS count, ROUND(AVG(close_profit) * 100, 2) AS avg_profit_pct, ROUND(SUM(close_profit_abs), 4) AS total_profit_abs FROM trades WHERE is_open = 0 GROUP BY exit_reason ORDER BY total_profit_abs;
如果 stop_loss 的总盈亏特别负,说明止损太宽或策略在某种行情下系统性亏损;如果 roi 占比高但单笔小,可能 ROI 太保守。
多空对比(合约策略):
SELECT is_short, COUNT(*) AS trades, ROUND(AVG(close_profit) * 100, 2) AS avg_profit_pct, ROUND(SUM(close_profit_abs), 4) AS total_profit_abs FROM trades WHERE is_open = 0 GROUP BY is_short;
平均持仓时长:
SELECT pair, ROUND(AVG((julianday(close_date) - julianday(open_date)) * 24 * 60), 1) AS avg_minutes FROM trades WHERE is_open = 0 GROUP BY pair;
⚠️ 再次强调:永远先备份,绝不在 bot 运行时写。
修复「手动卖出后状态不同步」:你在交易所手动卖了,bot 不知道,仍以为仓位开着。优先用 /forceexit <tradeid>(bot 会自动处理)。只有不得已才直接改库:
UPDATE trades SET is_open = 0, close_date = '2020-06-20 03:08:45.103418', close_rate = 0.19638016, close_profit = close_rate / open_rate - 1, close_profit_abs = (amount * 0.19638016 * (1 - fee_close) - (amount * (open_rate * (1 - fee_open)))), exit_reason = 'force_exit' WHERE id = 31;
删除交易(优先用 Telegram/REST 的 /delete <tradeid>,它会连带删订单和自定义数据):
-- 注意:先开外键 PRAGMA foreign_keys = ON (Ubuntu 默认关) DELETE FROM trades WHERE id = 31; DELETE FROM orders WHERE ft_trade_id = 31; DELETE FROM trade_custom_data WHERE ft_trade_id = 31;
⚠️ 绝不在没有
WHERE子句时跑DELETE/UPDATE——会清空整张表。Ubuntu 的 sqlite3 默认关外键,删交易前先PRAGMA foreign_keys = ON,否则会留下孤儿订单数据。
场景 1:bot 说不交易了,怀疑被 protection 锁
SELECT pair, side, reason, lock_end_time FROM pairlocks WHERE lock_end_time > datetime('now');
看哪些币被锁、锁到什么时候、什么原因。
场景 2:某笔交易盈亏对不上
SELECT * FROM trades WHERE id = <tradeid>; SELECT * FROM orders WHERE ft_trade_id = <tradeid>;
对比交易记录和订单明细,检查部分成交、费率、资金费率是否都算进去了。
场景 3:统计某段时间的净盈亏
SELECT ROUND(SUM(close_profit_abs), 4) AS net_profit, COUNT(*) AS trades FROM trades WHERE is_open = 0 AND close_date BETWEEN '2024-01-01' AND '2024-03-31';
场景 4:胜率与盈亏比(配合策略调优)
SELECT COUNT(*) AS total, SUM(CASE WHEN close_profit > 0 THEN 1 ELSE 0 END) AS wins, ROUND(SUM(CASE WHEN close_profit > 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) AS winrate_pct, ROUND(AVG(CASE WHEN close_profit > 0 THEN close_profit END) * 100, 2) AS avg_win_pct, ROUND(AVG(CASE WHEN close_profit < 0 THEN close_profit END) * 100, 2) AS avg_loss_pct FROM trades WHERE is_open = 0 AND exit_reason != 'force_exit';
排除强制平仓(干扰统计),看真实策略的胜率和盈亏比——这两个指标决定策略是否长期正期望。
sqlite3 命令行、Docker 内自带、或 SqliteBrowser 图形工具;.tables/.schema/SELECT 基本操作。pair/is_short/is_open/open_rate/close_rate/close_profit/close_profit_abs/exit_reason/enter_tag/leverage;与 orders/trade_custom_data 通过 ft_trade_id 关联。/forceexit//delete 等命令而非直接 SQL。is_open=0 及各 close 字段;删除交易:连删 trades/orders/trade_custom_data,Ubuntu 要先 PRAGMA foreign_keys=ON,绝不裸 DELETE/UPDATE。下一节,也是全书最后一节,我们看策略迁移与常见问题——怎么用 strategy-updater 升级老策略、v2 到 v3 的术语变更、FAQ。