231 lines
7.3 KiB
Markdown
231 lines
7.3 KiB
Markdown
|
|
# 每周 Bench Review 流程
|
|||
|
|
|
|||
|
|
> 跟原作者的方法论一致:每周五对跟单钱包和 bench 观察区钱包做前瞻盈亏 review,决定晋升/降级。
|
|||
|
|
>
|
|||
|
|
> 原作者的原话(HANDOFF_ARCHIVE.md):forward table = portfolio.py resolved rows, two windows: since last review and since the last swap.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 为什么要每周 review
|
|||
|
|
|
|||
|
|
watch_sharps.json 的 copy_pnl 是 **in-sample**(用历史数据算的),只能告诉你"过去跟这个钱包赚没赚"。但市场在变,钱包的表现也会变。
|
|||
|
|
|
|||
|
|
**前瞻盈亏(Forward P&L)** 是 out-of-sample 的:看"从某个时间点开始,这个钱包的信念单实际赚了多少"。这才是决定留还是踢的真实依据。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## review 时间
|
|||
|
|
|
|||
|
|
**每周五**(跟作者一样)。可以在 1Panel 加一个定时提醒。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## review 步骤
|
|||
|
|
|
|||
|
|
### 第 1 步:跑回测(在服务器上)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /opt/winning-wallet-finder/live
|
|||
|
|
|
|||
|
|
# 跑 30 天回测(包含 live 8 个 + bench 8 个,共 16 个钱包)
|
|||
|
|
python3 portfolio.py --days 30
|
|||
|
|
|
|||
|
|
# 看回测结果
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
d = json.load(open('portfolio.json'))
|
|||
|
|
print(f'总体: equity \${d[\"equity\"]:,.0f} | realized \${d[\"realized\"]:+,.0f} | {d[\"resolved\"]} resolved')
|
|||
|
|
"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第 2 步:看每个钱包的前瞻表现
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 看回测里每个钱包的盈亏
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
d = json.load(open('portfolio.json'))
|
|||
|
|
wallets = {}
|
|||
|
|
for b in d.get('bets', []):
|
|||
|
|
name = b.get('name', '?')
|
|||
|
|
if name not in wallets:
|
|||
|
|
wallets[name] = {'n': 0, 'pnl': 0, 'wins': 0}
|
|||
|
|
wallets[name]['n'] += 1
|
|||
|
|
wallets[name]['pnl'] += b.get('pnl', 0)
|
|||
|
|
if b.get('pnl', 0) > 0:
|
|||
|
|
wallets[name]['wins'] += 1
|
|||
|
|
|
|||
|
|
# 按 pnl 排序
|
|||
|
|
for name, stats in sorted(wallets.items(), key=lambda x: x[1]['pnl'], reverse=True):
|
|||
|
|
wr = stats['wins']/stats['n']*100 if stats['n'] > 0 else 0
|
|||
|
|
print(f'{name:20s} {stats[\"n\"]:3d} bets pnl \${stats[\"pnl\"]:+8.2f} 胜率 {wr:.0f}%')
|
|||
|
|
"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第 3 步:对比 live vs bench
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 当前跟的 8 个
|
|||
|
|
python3 -c "import json; [print(w['name']) for w in json.load(open('copybot.paper.json'))['wallets']]"
|
|||
|
|
|
|||
|
|
# bench 观察区的(backtest.json 里有但 copybot.paper.json 里没有的)
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
live = {w['wallet'].lower() for w in json.load(open('copybot.paper.json'))['wallets']}
|
|||
|
|
bt = json.load(open('backtest.json'))
|
|||
|
|
bench = [w.get('name', w['wallet'][:10]) for w in bt['wallets'] if w['wallet'].lower() not in live]
|
|||
|
|
print('bench:', ', '.join(bench))
|
|||
|
|
"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第 4 步:决策(5 个维度)
|
|||
|
|
|
|||
|
|
对照 5 个维度做决策:
|
|||
|
|
|
|||
|
|
| 维度 | 怎么看 | 晋升条件 | 降级条件 |
|
|||
|
|
|------|--------|---------|---------|
|
|||
|
|
| **前瞻盈亏** | 第 2 步的 per-wallet pnl | 正盈亏 | 负盈亏(连续 2 周) |
|
|||
|
|
| **copy_pnl** | `cat watch_sharps.json \| grep copy_pnl` | 排名靠前 | 跌出 watch_sharps |
|
|||
|
|
| **信念胜率** | `cat watch_sharps.json \| grep conv_win` | >60% | <30% |
|
|||
|
|
| **季节性** | 看钱包的 niche 有没有赛季/事件 | 赛季开始 | 赛季结束 |
|
|||
|
|
| **floor 稳定** | `cat copybot.paper.json \| grep floor` | 正常 | p80 漂移太大需 pin |
|
|||
|
|
|
|||
|
|
### 第 5 步:执行变更
|
|||
|
|
|
|||
|
|
**晋升**(bench -> live):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /opt/winning-wallet-finder
|
|||
|
|
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
# 把 bench 钱包加到 copybot.paper.json 和 config.live.json
|
|||
|
|
promote_name = 'AIcAIc' # 改成你要晋升的钱包
|
|||
|
|
|
|||
|
|
# 从 backtest.json 查地址
|
|||
|
|
bt = json.load(open('live/backtest.json'))
|
|||
|
|
promote = next((w for w in bt['wallets'] if w.get('name') == promote_name), None)
|
|||
|
|
if not promote:
|
|||
|
|
print(f'{promote_name} not found in backtest.json')
|
|||
|
|
exit(1)
|
|||
|
|
|
|||
|
|
# 加到 paper 和 live
|
|||
|
|
for path in ['live/copybot.paper.json', 'config.live.json']:
|
|||
|
|
cfg = json.load(open(path))
|
|||
|
|
if not any(w['wallet'].lower() == promote['wallet'].lower() for w in cfg['wallets']):
|
|||
|
|
w = {'wallet': promote['wallet'], 'name': promote['name'],
|
|||
|
|
'class': 'volume', 'floor': 25.0}
|
|||
|
|
cfg['wallets'].append(w)
|
|||
|
|
json.dump(cfg, open(path, 'w'), indent=2)
|
|||
|
|
print(f'{path}: added {promote_name}')
|
|||
|
|
else:
|
|||
|
|
print(f'{path}: {promote_name} already there')
|
|||
|
|
"
|
|||
|
|
|
|||
|
|
chown wwf:wwf live/copybot.paper.json config.live.json
|
|||
|
|
systemctl restart copybot-paper copybot-live
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**降级**(live -> bench):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /opt/winning-wallet-finder
|
|||
|
|
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
demote_name = 'BikesAreTheBikes' # 改成你要降级的钱包
|
|||
|
|
|
|||
|
|
for path in ['live/copybot.paper.json', 'config.live.json']:
|
|||
|
|
cfg = json.load(open(path))
|
|||
|
|
old = [w['name'] for w in cfg['wallets']]
|
|||
|
|
cfg['wallets'] = [w for w in cfg['wallets'] if w['name'] != demote_name]
|
|||
|
|
new = [w['name'] for w in cfg['wallets']]
|
|||
|
|
json.dump(cfg, open(path, 'w'), indent=2)
|
|||
|
|
print(f'{path}: {old} -> {new}')
|
|||
|
|
"
|
|||
|
|
|
|||
|
|
chown wwf:wwf live/copybot.paper.json config.live.json
|
|||
|
|
systemctl restart copybot-paper copybot-live
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 降级的钱包留在 backtest.json 里继续观察(不用删),下周 review 还能看到它的前瞻表现。
|
|||
|
|
|
|||
|
|
### 第 6 步:钉死 floor_pin(如果需要)
|
|||
|
|
|
|||
|
|
如果某个钱包的 p80 漂移太大(sync_floors.py 警告 `⚠ floor X->Y`),钉死它:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 -c "
|
|||
|
|
import json
|
|||
|
|
pin_name = 'Kruto2027' # 要钉的钱包
|
|||
|
|
pin_value = 80.0 # 钉死的值
|
|||
|
|
|
|||
|
|
for path in ['live/copybot.paper.json', 'config.live.json']:
|
|||
|
|
cfg = json.load(open(path))
|
|||
|
|
for w in cfg['wallets']:
|
|||
|
|
if w['name'] == pin_name:
|
|||
|
|
w['floor'] = pin_value
|
|||
|
|
w['floor_pin'] = pin_value
|
|||
|
|
print(f'{path}: {pin_name} floor_pin = {pin_value}')
|
|||
|
|
json.dump(cfg, open(path, 'w'), indent=2)
|
|||
|
|
"
|
|||
|
|
|
|||
|
|
chown wwf:wwf live/copybot.paper.json config.live.json
|
|||
|
|
systemctl restart copybot-paper copybot-live
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 当前状态
|
|||
|
|
|
|||
|
|
### live 跟单(8 个)
|
|||
|
|
|
|||
|
|
| 钱包 | floor | floor_pin | niche |
|
|||
|
|
|------|-------|-----------|-------|
|
|||
|
|
| Kruto2027 | $80 | ✅ 80 | 综合 |
|
|||
|
|
| 0xbadaf319 | $42 | - | 综合 |
|
|||
|
|
| 1kto1m | $332 | - | BTC Up/Down |
|
|||
|
|
| Vahan88 | $245 | - | 板球/体育 |
|
|||
|
|
| LSB1 | $315 | - | 瑞典足球 |
|
|||
|
|
| EdwardIN | $25 | - | 待观察 |
|
|||
|
|
| lma0o0o0o | $25 | - | 待观察 |
|
|||
|
|
| Coteykens | $25 | - | 待观察 |
|
|||
|
|
|
|||
|
|
### bench 观察(8 个)
|
|||
|
|
|
|||
|
|
| 钱包 | 状态 | 备注 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| gkmgkldfmg | 被淘汰 | daily.sh 已移出 watch_sharps |
|
|||
|
|
| AIcAIc | 观察 | 作者 07-16 重新加入(电竞赛季) |
|
|||
|
|
| BikesAreTheBikes | 观察 | 信念胜率 21% 偏低 |
|
|||
|
|
| imwalkinghere | 永久排除 | 5min BTC 市场,结算比跟单快 |
|
|||
|
|
| leegunner | 新候选 | 作者 bench rev 5 新加 |
|
|||
|
|
| oliman2 | 新候选 | 作者 bench rev 5 新加 |
|
|||
|
|
| JuiceFarm | 新候选 | 作者 bench rev 5 新加 |
|
|||
|
|
| 0xb0E43B | 新候选 | 作者 bench rev 5 新加 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1Panel 快速命令
|
|||
|
|
|
|||
|
|
**每周 review 一键跑回测 + 看排名**:
|
|||
|
|
```bash
|
|||
|
|
cd /opt/winning-wallet-finder/live && python3 portfolio.py --days 30 && python3 -c "
|
|||
|
|
import json
|
|||
|
|
d = json.load(open('portfolio.json'))
|
|||
|
|
wallets = {}
|
|||
|
|
for b in d.get('bets', []):
|
|||
|
|
name = b.get('name', '?')
|
|||
|
|
if name not in wallets:
|
|||
|
|
wallets[name] = {'n': 0, 'pnl': 0, 'wins': 0}
|
|||
|
|
wallets[name]['n'] += 1
|
|||
|
|
wallets[name]['pnl'] += b.get('pnl', 0)
|
|||
|
|
if b.get('pnl', 0) > 0:
|
|||
|
|
wallets[name]['wins'] += 1
|
|||
|
|
print('=== 前瞻盈亏排名 ===')
|
|||
|
|
for name, stats in sorted(wallets.items(), key=lambda x: x[1]['pnl'], reverse=True):
|
|||
|
|
wr = stats['wins']/stats['n']*100 if stats['n'] > 0 else 0
|
|||
|
|
print(f'{name:20s} {stats[\"n\"]:3d} bets pnl \${stats[\"pnl\"]:+8.2f} 胜率 {wr:.0f}%')
|
|||
|
|
"
|
|||
|
|
```
|