It was never in the enabled timeframes, so it held zero bars and produced no levels, but it still carried weight 8 in the scoring table and forced bucket_start to special-case a wall-clock ET anchor whose entire purpose was surviving DST transitions. That was the most intricate logic in session.py, maintained for a timeframe nobody used. Daily is now the only session-anchored bucket, which is a much easier rule to state and to keep correct. The DST parametrised tests go with it; the Sunday open and daily boundary cases remain. Manual-line tests move to 1h, so the weight assertions drop from 8 to 4. The plan document keeps its 4h examples — rewriting a dozen illustrative sentences would churn more than it clarifies — but the timeframe-roles section now records the removal so nothing reads as a spec to build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
42 lines
1.4 KiB
Python
42 lines
1.4 KiB
Python
from app.analysis.levels import Side
|
|
from app.analysis.manual_lines import ManualLine, ManualLineStore
|
|
from app.analysis.confluence import cluster_levels
|
|
from app.analysis.levels import Level, LevelKind
|
|
from app.bars.models import Timeframe
|
|
|
|
|
|
def sample_line():
|
|
return ManualLine("ml_test", Timeframe.H1, Side.RESISTANCE, 100, 5000, -0.01, 200, 300, number=1)
|
|
|
|
|
|
def test_json_persistence_round_trip(tmp_path):
|
|
path = tmp_path / "manual_lines.json"
|
|
store = ManualLineStore(path)
|
|
store.add(sample_line())
|
|
|
|
loaded = ManualLineStore(path)
|
|
assert list(loaded.lines.values()) == [sample_line()]
|
|
|
|
loaded.update("ml_test", {"note": "major swing"})
|
|
assert ManualLineStore(path).lines["ml_test"].note == "major swing"
|
|
loaded.delete("ml_test")
|
|
assert ManualLineStore(path).lines == {}
|
|
|
|
|
|
def test_hourly_line_uses_absolute_time_on_one_minute_chart():
|
|
level = sample_line().to_level()
|
|
instant = 160
|
|
assert level.tf is Timeframe.H1
|
|
assert level.price_at(instant) == 4999.4
|
|
assert level.weight == 4
|
|
|
|
|
|
def test_manual_line_raises_existing_ma_cluster_score():
|
|
ma = Level(
|
|
"ma", LevelKind.MA, Timeframe.D1, Side.RESISTANCE, 12, 1, "1d SMA20",
|
|
100, 5000, 0, None, 0, 100, 100, False, False, 20,
|
|
)
|
|
before = cluster_levels([ma], 160, 4998, 2)[0]
|
|
after = cluster_levels([ma, sample_line().to_level()], 160, 4998, 2)[0]
|
|
assert before.score == 12
|
|
assert after.score == 16
|