#!/usr/bin/env python3 """Все числа TW-040 одним прогоном. Тема Y: чего дневной бар не может ответить. ЗАЧЕМ ЭТОТ ФАЙЛ ЛЕЖИТ В ПРОЕКТЕ. Канал обещает: всё измерено, а не заявлено. Этот урок — о собственном пределе курса, и его числа обязаны совпадать с уроками, откуда они взяты. Поэтому: * СТОП ДНЕВНОГО СЕТАПА пересчитывается здесь же импортом расчёта урока «крипта против акций» (TW-026, `samesetup.py`) — медиана стопа в процентах цены при стопе на половину дневного хода; * ЧИСЛА ДРУГИХ УРОКОВ берутся из их снимков `results.txt`, но только свежих: снимок не старше своего скрипта и общего учёта (`tw_accounting.py`). Устаревший снимок останавливает расчёт — урок не имеет права повторять прошлую версию чужого числа; * ИЗДЕРЖКИ — та же формула, что в TW-026: круг в процентах позиции, делённый на стоп в процентах цены. Круг — 0,05 % на акциях и 0,20 % на крипте (константы того же расчёта). Стоп в k раз теснее — издержки в долях R в k раз больше: это арифметика, а не замер. ДАННЫЕ. Снимки этюдов TW-018, TW-031, TW-035; котировки — через TW-026. """ from __future__ import annotations import importlib.util import re import statistics as st import sys from pathlib import Path HERE = Path(__file__).resolve().parent ASSETS = HERE.parent OUT = HERE / "results.txt" ACCOUNTING = HERE.parents[2] / "shared" / "pipeline" / "tw_accounting.py" TIGHTER = [1, 2, 4, 5, 8] # во сколько раз стоп теснее дневного TRADES_A_WEEK = 2 lines: list[str] = [] def say(s: str = "") -> None: print(s) lines.append(s) def fresh(study: str, script: str) -> str: """Текст снимка этюда — только если он не старше своего скрипта.""" d = ASSETS / f"{study}-study" res, src = d / "results.txt", d / script t_res = res.stat().st_mtime uses_acc = "tw_accounting" in src.read_text(encoding="utf-8") older = [p.name for p in ([src] + ([ACCOUNTING] if uses_acc else [])) if p.stat().st_mtime > t_res] if older: sys.exit(f"Снимок {study} старше {', '.join(older)} — перезапустить {script} и повторить.") return res.read_text(encoding="utf-8") def grab(text: str, pattern: str, study: str) -> re.Match: m = re.search(pattern, text, re.M) if not m: sys.exit(f"В снимке {study} не нашлась строка по образцу «{pattern}» — снимок изменил форму.") return m def main() -> int: for s in (sys.stdout, sys.stderr): if hasattr(s, "reconfigure"): s.reconfigure(encoding="utf-8", errors="replace") t18 = fresh("TW-018", "readbar.py") t31 = fresh("TW-031", "styles.py") t35 = fresh("TW-035", "journal.py") say("ИСТОЧНИКИ — снимки свежие (не старше своих скриптов)") say(" TW-018 readbar.py · TW-031 styles.py · TW-035 journal.py · TW-026 samesetup.py (пересчёт)") say("") say("1 · ЧЕГО ДНЕВНОЙ БАР НЕ ХРАНИТ") rev = grab(t18, r"знак дня перевернулся после открытия:\s*(\d+)%", "TW-018") say(f" дней акций, где знак дня перевернулся после открытия: {rev.group(1)}% (TW-018)") fast = grab(t31, r"^\s*быстрый\s+(\d+)\s+\d+\s+\d+%\s+\S+\s+\d+\s+\S+\s+\d+%\s+(\d+)%", "TW-031") say(f" быстрый способ (TW-031): {fast.group(1)} сделок, решилось за день {fast.group(2)}%") both = grab(t31, r"задеты И стоп, И цель:\s*(\d+) из (\d+) \((\d+)%\)", "TW-031") say(f" в день входа задеты и стоп, и цель: {both.group(1)} из {both.group(2)} ({both.group(3)}%) — порядок неизвестен") say("") say("2 · СКОЛЬКО СДЕЛОК НУЖНО, ЧТОБЫ РАЗЛИЧИТЬ МАЛОЕ") sig = grab(t35, r"Здесь σ = ([\d.]+) R", "TW-035") say(f" разброс одной сделки (TW-035): {sig.group(1)} R") for m in re.finditer(r"разница ([\d.]+) R → ([\d\s]+?) сделок в каждой группе\s+то есть ([\d\s]+?) всего", t35): total = int(m.group(3).replace(" ", "").replace(" ", "")) years = total / (TRADES_A_WEEK * 52) say(f" разница {m.group(1)} R → {total:,} сделок всего; при {TRADES_A_WEEK} сделках в неделю — {years:.1f} лет") say("") say("3 · ИЗДЕРЖКИ В ДОЛЯХ R, КОГДА СТОП ТЕСНЕЕ") spec = importlib.util.spec_from_file_location("samesetup", ASSETS / "TW-026-study" / "samesetup.py") mk = importlib.util.module_from_spec(spec) spec.loader.exec_module(mk) rows = {n: mk.bars(n) for n in mk.STOCKS + mk.CRYPTO} start = min(r[0]["d"][:10] for n, r in rows.items() if n in mk.CRYPTO) rows = {n: [b for b in r if b["d"][:10] >= start] for n, r in rows.items()} say(" стоп дневного сетапа — половина дневного хода за уровнем (TW-026), медиана по сделкам") say(f" {'':10}{'стоп, %':>9}{'круг, %':>9}" + "".join(f"{'×' + str(k):>9}" for k in TIGHTER)) for n, r in rows.items(): stop = st.median(t["stop_pct"] for t in mk.trades(r, mk.adr(r))) fee = (mk.FEE_CRYPTO if n in mk.CRYPTO else mk.FEE_STOCK) * 100 cells = "".join(f"{fee / (stop / k):8.2f}R" for k in TIGHTER) say(f" {n:10}{stop:8.2f}%{fee:8.2f}%{cells}") say(" ×5 — стоп в десятую долю хода, как в ролике о буфере (K-02), против половины хода дневного сетапа.") say("") say("4 · ВЫВОД") say(" Дневной бар отвечает на вопросы о сделках, которые живут днями, и о том,") say(" что известно до сделки: издержки, размер. Сделки, которые решаются внутри") say(f" дня, он измерить не может ({fast.group(2)}% быстрых), разницу в десятую долю R —") say(" тоже (нужны тысячи сделок). А перенос того же метода на тесный стоп") say(" умножает издержки в R во столько раз, во сколько стоп теснее.") OUT.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"\nнаписано: {OUT}") return 0 if __name__ == "__main__": raise SystemExit(main())