91 lines
3.0 KiB
Python
91 lines
3.0 KiB
Python
"""Opportunity historical evaluation tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from coinhunter.services import opportunity_evaluation_service
|
|
|
|
|
|
def _rows(start_ms: int, closes: list[float]) -> list[list[float]]:
|
|
rows = []
|
|
for index, close in enumerate(closes):
|
|
open_time = start_ms + index * 3_600_000
|
|
volume = 1_000 + index * 10
|
|
rows.append(
|
|
[
|
|
float(open_time),
|
|
close * 0.99,
|
|
close * 1.02,
|
|
close * 0.98,
|
|
close,
|
|
float(volume),
|
|
float(open_time + 3_599_999),
|
|
close * volume,
|
|
]
|
|
)
|
|
return rows
|
|
|
|
|
|
class OpportunityEvaluationServiceTestCase(unittest.TestCase):
|
|
def test_evaluate_opportunity_dataset_scores_historical_samples(self):
|
|
start_ms = 1_767_225_600_000
|
|
dataset = {
|
|
"metadata": {
|
|
"plan": {
|
|
"intervals": ["1h"],
|
|
"simulation_start": "2026-01-01T04:00:00Z",
|
|
"simulation_end": "2026-01-01T07:00:00Z",
|
|
"simulate_days": 1,
|
|
}
|
|
},
|
|
"klines": {
|
|
"GOODUSDT": {"1h": _rows(start_ms, [100, 101, 102, 103, 104, 106, 108, 109, 110])},
|
|
"BADUSDT": {"1h": _rows(start_ms, [100, 99, 98, 97, 96, 95, 94, 93, 92])},
|
|
},
|
|
}
|
|
config = {
|
|
"market": {"default_quote": "USDT"},
|
|
"opportunity": {
|
|
"entry_threshold": 1.5,
|
|
"watch_threshold": 0.6,
|
|
"evaluation_horizon_hours": 2.0,
|
|
"evaluation_take_profit_pct": 1.0,
|
|
"evaluation_stop_loss_pct": 2.0,
|
|
"evaluation_setup_target_pct": 0.5,
|
|
"evaluation_lookback": 4,
|
|
"top_n": 2,
|
|
},
|
|
}
|
|
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
dataset_path = Path(tmp_dir) / "opportunity-dataset.json"
|
|
dataset_path.write_text(json.dumps(dataset), encoding="utf-8")
|
|
|
|
payload = opportunity_evaluation_service.evaluate_opportunity_dataset(
|
|
config,
|
|
dataset_path=str(dataset_path),
|
|
horizon_hours=2.0,
|
|
take_profit=0.01,
|
|
stop_loss=0.02,
|
|
setup_target=0.005,
|
|
lookback=4,
|
|
top_n=2,
|
|
max_examples=3,
|
|
)
|
|
|
|
self.assertEqual(payload["summary"]["symbols"], ["BADUSDT", "GOODUSDT"])
|
|
self.assertEqual(payload["summary"]["interval"], "1h")
|
|
self.assertGreater(payload["summary"]["count"], 0)
|
|
self.assertIn("by_action", payload)
|
|
self.assertIn("trade_simulation", payload)
|
|
self.assertEqual(payload["rules"]["research_mode"], "disabled: dataset has no point-in-time research snapshots")
|
|
self.assertLessEqual(len(payload["examples"]), 3)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|