Skip to content

Commit 285adbd

Browse files
committed
rescue VWAP Session Trader by measuring the restriction in its name
This preset shipped marked "measured and did NOT hold": negative on all four symbols on the 2026 holdout, -0.240R per trade, with a recommendation to drop it from the product. The reason was written in its own notes and had never been measured — it only traded a New York equities session while crypto trades every hour of every day. Measured. The restriction was what killed it. It threw away half the trades and kept the worse half: session 09:30-16:00: 182 trades, -0.240R, 0 of 4 symbols positive session opened up: 348 trades, +0.263R, 3 of 4 positive Raising the volume multiplier to 1.5 brings the fourth symbol over: +0.267R, 4 of 4, and ahead of the shipping settings in development and validation too. The session filter is kept, not removed, with its window opened to the whole day. Removing it would have deleted the inputs from the generated script — the compiler only emits them when a session is enabled — leaving no way back for a user who wants New York hours. Measured identical to having no session at all on all four periods rather than asserted. The win-rate profile keeps reward 4 and replaces the 1.5R/1R trail with a tight 1R/0.5R one: hit rate 43.3% to 56.4%, expectancy up with it, and positive in July for the first time on this preset. Read on the chart across four symbols: 446 trades, 56.3% win, +74.69R, with BNB matching the measurement to +0.069R against +0.070R. Renamed to VWAP Reclaim, because the old name described a restriction it no longer applies. The plain-language text stops calling a 24-hour window a restriction and says the filter is available instead. Separately: every preset now opens on the win-rate profile rather than the money one. Both stay compiled in and the money profile is one dropdown away, but a first impression of a 12-25% hit rate reads as a broken indicator long before it reads as a wide reward target. Two tests used this preset as their vehicle for session behaviour — timezone handling, and a session being a veto rather than a scored filter. Both build their own config now: the feature is still in the product, it just no longer has a preset to ride on.
1 parent c9777a7 commit 285adbd

14 files changed

Lines changed: 399 additions & 64 deletions

app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export default function Home() {
130130
<div className="profile-choice">
131131
<SelectField
132132
label="Profile"
133-
value={publicConfig.activeProfile ?? "money"}
133+
value={publicConfig.activeProfile ?? "win_rate"}
134134
onChange={(v) => chooseTradeProfile(v as StrategyConfig["activeProfile"])}
135135
options={[["money", "Money — fewer, larger wins"], ["win_rate", "Win rate — more, smaller wins"]]}
136136
/>

lib/behavior-plan.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ const triggerPlan = (c: StrategyConfig): PlanTrigger => {
107107
}
108108
};
109109

110+
// A window that starts at midnight and runs to the last minute of the day restricts nothing.
111+
// Pine's session parser only accepts hours 00-23, so 0000-2359 is how a full day is written and
112+
// 0000-2400 would be rejected; both are treated as open here so neither spelling misdescribes
113+
// itself.
114+
const coversWholeDay = (session: string): boolean => /^0000-(2359|2400)$/.test(session);
115+
110116
const spotExitLabel = (mode: StrategyConfig["spotExitMode"]): string => {
111117
switch (mode) {
112118
case "trend_break": return "price crosses below the long moving average";
@@ -207,9 +213,14 @@ export function buildBehaviorPlan(c: StrategyConfig): BehaviorPlan {
207213
longExpression: "htfBull",
208214
shortExpression: "htfBear"
209215
});
216+
// A session set to the whole day is still a live filter — it stays in the compiled script so
217+
// the reader can narrow it — but calling it a restriction would describe a limit that is not
218+
// being applied. VWAP Reclaim ships exactly that way: the inputs are there, the window is open.
210219
if (c.execution.sessionEnabled) filters.push({
211220
id: "session",
212-
label: `inside exchange-time session ${c.execution.session}`,
221+
label: coversWholeDay(c.execution.session)
222+
? "inside the trading session, which is set to every hour by default"
223+
: `inside exchange-time session ${c.execution.session}`,
213224
longExpression: "sessionOk",
214225
shortExpression: "sessionOk"
215226
});

lib/compiler-v27.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ export function compilePine(config: StrategyConfig): string {
7070
}
7171
if (!applied) return code;
7272

73-
code = insertSelector(code, config.activeProfile === "win_rate" ? WIN_RATE : MONEY);
73+
// The win-rate profile is what a script opens with unless the author picked otherwise.
74+
// Both are measured and both are compiled in, so this is only about which one a reader meets
75+
// first — and the money profile meets them with a hit rate near 20%, which reads as a broken
76+
// indicator long before it reads as a wide reward target. The reviewed presets bear that out:
77+
// every one of the four locked so far has a win-rate profile clearing 49%, while their money
78+
// profiles sit between 12% and 25%.
79+
code = insertSelector(code, config.activeProfile === "money" ? MONEY : WIN_RATE);
7480
return withProfileRow(code, config);
7581
}
7682

lib/explain.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,14 @@ export function explainConfig(c: StrategyConfig): string[] {
8080
if (plan.spotExit) lines.push(`A spot exit is generated when ${plan.spotExit.label}. The script never creates short entries.`);
8181
if (plan.execution.confirmedBarsOnly) lines.push("Long, short, buy and exit signals only finalize after the chart candle closes.");
8282
if (plan.execution.cooldownBars > 0) lines.push(`After a signal, a ${plan.execution.cooldownBars}-bar cooldown prevents duplicate entries in the same move.`);
83-
if (plan.execution.session) lines.push(`Signals are restricted to the ${plan.execution.session} exchange-time session.`);
83+
if (plan.execution.session) {
84+
// A full-day window is a session filter that restricts nothing, so saying "restricted" would
85+
// describe a limit the script is not applying. It stays in the settings either way, which is
86+
// the part worth telling the reader.
87+
lines.push(/^0000-(2359|2400)$/.test(plan.execution.session)
88+
? "A trading-session filter is available and set to every hour; narrow it in the indicator's settings to trade only part of the day."
89+
: `Signals are restricted to the ${plan.execution.session} exchange-time session.`);
90+
}
8491

8592
if (plan.risk.enabled) {
8693
const parts = [plan.risk.stopLabel, plan.risk.targetLabel].filter((value): value is string => Boolean(value));

lib/presets.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,48 @@ export const presets: StrategyConfig[] = [
8080
execution: { ...defaultConfig.execution, cooldownBars: 3 },
8181
winRateProfile: winRate({ triggerWindow: 5, riskReward: 1.5, trailStartR: 0 })
8282
}),
83-
// holdout 2026: 180 trades, -0.264R per trade. Measured and did NOT hold.
84-
// Win-rate profile, holdout 2026: 210 trades, 37.6% win, -0.020R. Closer to break-even
85-
// than the money profile, but still not a preset that held out of sample.
83+
// LOCKED 26 July 2026 — two structural changes and one exit change, read on the chart on all
84+
// four symbols. The preset that was marked "measured and did NOT hold" is now the most
85+
// consistent in the set. See research/preset-sweep/PRESET-REVIEW-PLAN.md
86+
//
87+
// This preset was marked "measured and did NOT hold": negative on all four symbols on the
88+
// 2026 holdout, -0.240R per trade. The reason turned out to be the one setting its name is
89+
// built around. It only traded a New York equities session, 09:30-16:00, while crypto trades
90+
// every hour of every day, and that restriction had never been measured. It was throwing away
91+
// half the trades and the half it kept was the worse half:
92+
// session on: 182 trades, -0.240R, 0 of 4 symbols positive
93+
// session off: 348 trades, +0.263R, 3 of 4 positive
94+
// Raising the volume multiplier to 1.5 thins what is left and brings the fourth symbol over:
95+
// 272 trades, +0.267R, 4 of 4, and better than the shipping settings in development and
96+
// validation as well as the holdout.
97+
//
98+
// The win-rate profile keeps reward 4 but replaces the 1.5R/1R trail with a tight one that
99+
// arms at 1R and follows half an R behind. That is what makes the profile deserve its name
100+
// here: the hit rate goes from 43.3% to 56.4% and expectancy up with it, and it is the first
101+
// configuration on this preset that is positive in July as well.
102+
//
103+
// Read on the chart, four symbols, Jan-Jul 2026: 446 trades, 56.3% win, +74.69R.
104+
// BNB +0.069R, ETH +0.170R, SOL +0.284R, BTC +0.139R — the measurement had +0.070R on BNB.
105+
//
106+
// Renamed from "VWAP Session Trader": the name described a session restriction the preset no
107+
// longer applies, and a preset name is the first thing the product tells a reader. What it
108+
// does is reclaim VWAP, so that is what it is called.
109+
//
110+
// The session filter is kept rather than removed, with its window opened to the whole day.
111+
// Switching it off would have deleted its inputs from the generated script — the compiler only
112+
// emits them when a session is enabled (compiler-v2) — and a user who wants New York hours
113+
// would have no way back. Measured to be identical to having no session at all, on all four
114+
// periods: 1762 / 1700 / 272 / 34 trades and +0.283R / +0.146R / +0.267R / -0.120R either way.
115+
// Pine's session parser only accepts hours 00-23, so the 24-hour window is spelled 0000-2359.
86116
preset({
87-
presetId: "vwap_session_trader", name: "VWAP Session Trader", style: "intraday",
117+
presetId: "vwap_session_trader", name: "VWAP Reclaim", style: "intraday",
88118
chartTimeframe: "60", triggerWindow: 3, entryTrigger: "vwap_reclaim",
89119
trend: { ...defaultConfig.trend, emaEnabled: true, emaFast: 9, emaSlow: 21, longMaEnabled: false, vwapEnabled: true },
90120
higherTimeframe: { ...defaultConfig.higherTimeframe, enabled: false },
121+
volume: { ...defaultConfig.volume, multiplier: 1.5 },
91122
risk: { ...defaultConfig.risk, riskReward: 6 },
92-
execution: { ...defaultConfig.execution, sessionEnabled: true, session: "0930-1600", sessionTimezone: "America/New_York" },
93-
winRateProfile: winRate({ triggerWindow: 3, riskReward: 4 })
123+
execution: { ...defaultConfig.execution, sessionEnabled: true, session: "0000-2359" },
124+
winRateProfile: winRate({ triggerWindow: 3, riskReward: 4, trailStartR: 1, trailDistanceR: 0.5 })
94125
}),
95126
// The one preset where swing structure beat the daily average on all three partitions at
96127
// once, so it is the one preset that changed. Its daily EMA-200 gate turned after the

research/preset-sweep/PRESET-REVIEW-PLAN.md

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
> ## Yeni oturum buradan başlar
44
>
5-
> **Sıradaki iş:** VWAP Session Trader incelemesi (5. sıra, aşağıdaki tabloda).
5+
> **Sıradaki iş:** 4H Swing Trend incelemesi (6. sıra, aşağıdaki tabloda).
66
>
77
> **İlk üç komut:**
88
> ```
99
> git status --short
1010
> /Users/kohen/bin/safe-npm test
1111
> /Users/kohen/bin/safe-npm run dev -- -H 0.0.0.0
1212
> ```
13-
> 737 test geçmeli. Doğrulama Browser pane ile yapılır, `curl` yasak.
13+
> 742 test geçmeli. Doğrulama Browser pane ile yapılır, `curl` yasak.
1414
>
1515
> **Commit bekleyen değişiklikler stage'de.** Kohen commit'i kendi atar; Claude
1616
> `git commit`, `push`, `pull`, `fetch`, `reset`, `rebase` çalıştırmaz. Stage için
@@ -70,7 +70,7 @@ geçmek için. Bir preset kilitlendikten sonra o preset'e dokunulmaz — yeni bi
7070
tekrar açmayı gerektirirse, o karar ayrıca konuşulur.
7171
7272
**Son güncelleme:** 26 Temmuz 2026
73-
**Kilitlenen:** 4 / 9 ölçülebilir preset
73+
**Kilitlenen:** 5 / 9 ölçülebilir preset
7474
7575
---
7676
@@ -102,8 +102,8 @@ sayıları ölçümle uyuştuktan sonra kilitlenir.
102102
| 2 | Fast EMA Scalper | 21.5 | **KİLİTLENDİ** ✓ |
103103
| 3 | Supertrend Volume | 10.5 | **KİLİTLENDİ** ✓ |
104104
| 4 | Breakout Momentum | 6.6 | **KİLİTLENDİ** ✓ |
105-
| 5 | VWAP Session Trader | 7.1 | **SIRADA** |
106-
| 6 | 4H Swing Trend | 2.2 | bekliyor |
105+
| 5 | VWAP Reclaim | 7.1 | **KİLİTLENDİ** ✓ |
106+
| 6 | 4H Swing Trend | 2.2 | **SIRADA** |
107107
| 7 | Selective Multi-Timeframe | 2.1 | bekliyor |
108108
| 8 | RSI Divergence Reversal | 5.3 | bekliyor |
109109
| 9 | Long-Term Trend Guard | 1.9 | bekliyor |
@@ -631,12 +631,128 @@ bölümde.
631631
632632
---
633633
634-
## 5. VWAP Session Trader — bekliyor
634+
## 5. VWAP Reclaim — ✅ KİLİTLENDİ (26 Temmuz 2026)
635635
636-
`vwap_session_trader` · 60 dakika · tetikleyici penceresi 3 · ATR×2
636+
*Eski adı: VWAP Session Trader. `presetId` değişmedi (`vwap_session_trader`) — diskteki tarama
637+
sonuçları ve bu dosya o kimliğe bağlı, değiştirmek ölçüm geçmişini kopartırdı.*
638+
639+
`vwap_session_trader` · 60 dakika · **seans 24 saat** · **hacim 1.5x** · ATR×2 · pencere 3
640+
641+
- **Para profili:** risk/ödül 6 — *değişmedi*
642+
- **İsabet profili:** risk/ödül 4, trailing **1R'de kurulur, 0.5R takip** — *değişti*
643+
644+
### Bu preset "ölçümü geçemedi" diye duruyordu, sebebi kendi adıydı
645+
646+
Eski notu şöyleydi: *"2026 Ocak-Haziran'da dört sembolde de zararda. Bu preset ölçümü geçemedi.
647+
Karar önerisi: üründen çıkarmak."*
648+
649+
Sebep, aynı notta yazılıydı ama ölçülmemişti: *"Seans kısıtı var (New York 09:30-16:00) —
650+
kripto 7/24 işlem gördüğü için bu kısıt hiç ölçülmedi."*
651+
652+
Ölçüldü. **Kısıt preset'i öldüren şeymiş.**
653+
654+
| | İşlem | 2026 holdout | Artıda sembol |
655+
|---|---|---|---|
656+
| Seans AÇIK (NY 09:30-16:00) | 182 | **−0.240R** | **0/4** |
657+
| Seans KAPALI (7/24) | 348 | **+0.263R** | 3/4 |
658+
659+
İşlemlerin **yarısını atıyordu ve attığı yarı daha iyiydi.** Kripto her saat işlem görürken
660+
New York borsa saatlerine hapsetmek, ölçülmemiş bir varsayımdı.
661+
662+
### Hacim 1.5 dördüncü sembolü de artıya geçiriyor
663+
664+
| Konfig | dev | val | holdout | Temmuz | Artıda |
665+
|---|---|---|---|---|---|
666+
| eski ürün | +0.262R | +0.106R | −0.240R | +0.702R | 0/4 |
667+
| seans yok | +0.201R | +0.102R | +0.263R | −0.200R | 3/4 |
668+
| **seans yok + hacim 1.5** | **+0.283R** ✓ | **+0.146R** ✓ | **+0.267R** ✓ | −0.120R | **4/4** |
669+
670+
Üç dönemde eski ürünü geçiyor. Temmuz'da geçmiyor ama eski ürünün Temmuz'daki +0.702R'si **15
671+
işlemden** geliyor ve bu dosyanın kendi notu "anlamlı değil" diyor.
672+
673+
### Seans kaldırılmadı, 24 saate açıldı
674+
675+
`compiler-v2` seans girdilerini **sadece seans etkinken** üretiyor. Kapatmak, ayarları script'ten
676+
silmek demekti — New York saatlerini isteyen kullanıcının geri dönüşü olmazdı. O yüzden filtre
677+
etkin kaldı, penceresi `0000-2359` yapıldı.
678+
679+
"24 saatlik seans = seans yok" iddiası ölçüldü, dört dönemde de **birebir aynı**:
680+
681+
```
682+
seans 0000-2359 ACIK 1762t %26.3 +0.283R | 1700t %25.0 +0.146R | 272t %26.8 +0.267R | 34t %23.5 -0.120R
683+
seans yok 1762t %26.3 +0.283R | 1700t %25.0 +0.146R | 272t %26.8 +0.267R | 34t %23.5 -0.120R
684+
```
685+
686+
Pine'ın seans ayrıştırıcısı sadece 00-23 saatlerini kabul ediyor, o yüzden 24 saatin dürüst
687+
yazımı `0000-2359`.
688+
689+
### İsabet profili: ödül hedefi aynı, trailing değişti
690+
691+
Yeni yapıya karşı altı çıkış şekli × altı ödül hedefi tarandı. Kazanan, ödül hedefini
692+
oynatmak **değil**, trailing'i sıkmak oldu:
693+
694+
| Ayar | dev | val | **holdout** | Temmuz | Artıda |
695+
|---|---|---|---|---|---|
696+
| rr 4 + trail 1.5/1 (eski) | %43.4 · +0.121R | %41.3 · +0.093R | %43.3 · +0.163R | %44.0 · −0.088R | 4/4 |
697+
| **rr 4 + trail 1R/0.5R** | **%53.7** · +0.135R | **%50.5** · +0.083R | **%56.4** · **+0.184R** | **%56.4** · **+0.119R** | **4/4** |
698+
699+
İsabet 13 puan yükseliyor, beklenti de yükseliyor, ve **Temmuz'da artıda** — bu preset için ilk kez.
700+
701+
Ödül hedefi rr 4'te kaldı ama artık büyük ölçüde işlevsiz: işlemlerin çoğu 1R'de kurulan
702+
trailing'den çıkıyor, hedefe varmıyor.
703+
704+
### Grafikte doğrulama (dört sembol, 2026 Ocak–Temmuz)
705+
706+
| Sembol | Panel | Ölçüm (holdout) |
707+
|---|---|---|
708+
| BNB | 101t · %52.5 · **+0.069R** | **+0.070R** |
709+
| ETH | 107t · %56.1 · +0.170R | +0.177R |
710+
| SOL | 114t · %61.4 · +0.284R | +0.314R |
711+
| BTC | 124t · %54.8 · +0.139R | +0.167R |
712+
| **Toplam** | 446t · **%56.3** · **+74.69R** | %56.4 |
713+
714+
İsabet **0.1 puan** farkla tuttu, BNB neredeyse birebir. Panelin 446 işlemi, ölçümün holdout
715+
392 + Temmuz 55 = 447'siyle örtüşüyor — okuma sırasında tarih üst sınırı daraltılmamıştı,
716+
yani Temmuz da içinde.
717+
718+
### İsim değişti
719+
720+
**VWAP Session Trader → VWAP Reclaim.** Seans kısıtı kalktığına göre eski isim, artık
721+
uygulanmayan bir kısıtı anlatıyordu. Preset ismi ürünün kullanıcıya verdiği ilk bilgi.
722+
723+
Açıklama metni de düzeltildi: 24 saatlik bir seansı "kısıt" diye anlatmak yanlış olurdu.
724+
`lib/behavior-plan.ts` ve `lib/explain.ts` artık açık pencereyi *"a trading-session filter is
725+
available and set to every hour"* diye anlatıyor. İkisi de `0000-2359` ve `0000-2400`
726+
yazımlarını tanıyor.
727+
728+
### Bu incelemede reddedilenler
729+
730+
4 saatlik grafik (dev +0.478R, holdout +0.002R — Breakout Momentum'daki tuzağın aynısı),
731+
üst zaman dilimi açmak (holdout'ta en iyi ama BNB −0.590R), 30 dakikalık grafik (Temmuz'da
732+
tek artıda olan ama dev/val'de belirgin kötü), hacim 0.8/1.25, ATR 2.5/3.
733+
734+
### Açık kalan not
735+
736+
**Setin en tutarlı preset'i oldu.** 446 işlemde %56.3 isabet, dört sembolde de kârda, Temmuz'da
737+
bile artıda. "Üründen çıkarmak" önerilen preset, incelemeden sonra en iyisi çıktı.
738+
739+
Bunu bulan şey ödül hedefi oynatmak değil, **hiç ölçülmemiş yapısal bir varsayımı ölçmek** oldu.
740+
Kural 7'nin neden var olduğunun en net örneği.
741+
742+
### Kilit nasıl korunuyor
743+
744+
`tests/profile-selector.test.ts` — `locked presets` bölümü iki profili, ayrı bir test de ismi,
745+
seansın açık olduğunu, pencerenin `0000-2359` olduğunu ve hacim çarpanını sabitliyor. Seans en
746+
kolay sessizce geri alınabilecek ayar, çünkü zararsız bir varsayılan gibi okunuyor. Değil.
747+
748+
---
749+
750+
## Eski ölçüm kaydı — VWAP Session Trader (kilitleme öncesi)
751+
752+
`vwap_session_trader` · 60 dakika · seans NY 09:30-16:00 · hacim 1.0x · ATR×2 · pencere 3
637753
638754
- **Para profili:** risk/ödül 6
639-
- **İsabet profili:** risk/ödül 4, trailing 1.5/1, pencere 3
755+
- **Eski isabet profili:** risk/ödül 4, trailing 1.5/1, pencere 3
640756
641757
| Dönem | Para profili | İsabet profili |
642758
|---|---|---|
@@ -669,10 +785,13 @@ bölümde.
669785
- Temmuz'da +0.702R görünüyor ama 15 işlem, ve tamamı iki sembolün 2-4 işleminden geliyor.
670786
Anlamlı değil.
671787
- Seans kısıtı var (New York 09:30-16:00) — kripto 7/24 işlem gördüğü için bu kısıt
672-
hiç ölçülmedi, kaldırıldığında ne olacağı bilinmiyor.
788+
hiç ölçülmedi, kaldırıldığında ne olacağı bilinmiyor. **← incelemede ölçüldü, preset'i
789+
öldüren şey buymuş.**
673790
- **Karar önerisi:** üründen çıkarmak veya "ölçüldü, tutmadı" etiketiyle bırakmak.
791+
**← yanlış öneriydi; kısıt kaldırılınca setin en tutarlı preset'i oldu.**
674792
675-
**Durum:** ölçüm hazır, sıra bekliyor.
793+
Bu bölüm kilitleme öncesi durumu kayıt için tutuluyor. Geçerli ayar yukarıdaki kilitli
794+
bölümde.
676795
677796
---
678797

0 commit comments

Comments
 (0)