59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
import pandas as pd
|
|
|
|
from app.core.signal_taxonomy import signal_code
|
|
from app.services.altcoin_confirm import detect_box_breakout_pullback_4h
|
|
|
|
|
|
def _bar(ts, open_, high, low, close, volume):
|
|
return {
|
|
"timestamp": ts,
|
|
"open": open_,
|
|
"high": high,
|
|
"low": low,
|
|
"close": close,
|
|
"volume": volume,
|
|
}
|
|
|
|
|
|
def _box_breakout_rows(fail=False):
|
|
start = datetime(2026, 5, 20)
|
|
rows = []
|
|
for i in range(32):
|
|
base = 1.03 + (i % 5) * 0.006
|
|
rows.append(_bar(start + timedelta(hours=4 * i), base, 1.095 + (i % 3) * 0.002, 0.985 - (i % 2) * 0.002, base + 0.004, 1000 + (i % 4) * 30))
|
|
|
|
rows.append(_bar(start + timedelta(hours=4 * 32), 1.085, 1.16, 1.08, 1.135, 2600))
|
|
if fail:
|
|
rows.append(_bar(start + timedelta(hours=4 * 33), 1.13, 1.135, 1.00, 1.055, 1800))
|
|
else:
|
|
rows.append(_bar(start + timedelta(hours=4 * 33), 1.13, 1.145, 1.092, 1.118, 1600))
|
|
|
|
for i in range(12):
|
|
close = (1.04 + i * 0.002) if fail else (1.12 + i * 0.008)
|
|
rows.append(_bar(start + timedelta(hours=4 * (34 + i)), close - 0.01, close + 0.018, close - 0.025, close, 1300 + i * 40))
|
|
return rows
|
|
|
|
|
|
def test_detect_box_breakout_pullback_4h_finds_box_top_retest():
|
|
df = pd.DataFrame(_box_breakout_rows())
|
|
|
|
result = detect_box_breakout_pullback_4h(df)
|
|
|
|
assert result["detected"] is True
|
|
assert result["score"] >= 7
|
|
assert result["quality"] in {"良好", "优质"}
|
|
assert result["entry_zone"] > 1.08
|
|
assert result["stop_level"] < result["entry_zone"]
|
|
assert "箱体" in result["signals"][0]
|
|
assert signal_code(result["signals"][0]) == "box_breakout_pullback_4h"
|
|
|
|
|
|
def test_detect_box_breakout_pullback_4h_rejects_failed_retest():
|
|
df = pd.DataFrame(_box_breakout_rows(fail=True))
|
|
|
|
result = detect_box_breakout_pullback_4h(df)
|
|
|
|
assert result["detected"] is False
|