feat(data): mirror ETH spot+DVOL in dvol_history dal market_snapshot
Popola dvol_history dentro la stessa transazione di market_snapshots, così lo storico è disponibile anche in modalità data-only (STRATEGY=false). Evita il warm-up vuoto di return_4h quando si abilita la strategia: il monitor_cycle trova subito i campioni locali invece di dipendere dal fallback Deribit get_historical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Any
|
|||||||
|
|
||||||
from cerbero_bite.clients._exceptions import McpError
|
from cerbero_bite.clients._exceptions import McpError
|
||||||
from cerbero_bite.state import connect, transaction
|
from cerbero_bite.state import connect, transaction
|
||||||
from cerbero_bite.state.models import MarketSnapshotRecord
|
from cerbero_bite.state.models import DvolSnapshot, MarketSnapshotRecord
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cerbero_bite.runtime.dependencies import RuntimeContext
|
from cerbero_bite.runtime.dependencies import RuntimeContext
|
||||||
@@ -181,6 +181,21 @@ async def collect_market_snapshot(
|
|||||||
try:
|
try:
|
||||||
with transaction(conn):
|
with transaction(conn):
|
||||||
ctx.repository.record_market_snapshot(conn, record)
|
ctx.repository.record_market_snapshot(conn, record)
|
||||||
|
# Mirror ETH spot+DVOL into dvol_history so monitor_cycle's
|
||||||
|
# return_4h lookup has local samples even in data-only mode.
|
||||||
|
if (
|
||||||
|
record.asset == "ETH"
|
||||||
|
and record.spot is not None
|
||||||
|
and record.dvol is not None
|
||||||
|
):
|
||||||
|
ctx.repository.record_dvol_snapshot(
|
||||||
|
conn,
|
||||||
|
DvolSnapshot(
|
||||||
|
timestamp=record.timestamp,
|
||||||
|
dvol=record.dvol,
|
||||||
|
eth_spot=record.spot,
|
||||||
|
),
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
persisted += 1
|
persisted += 1
|
||||||
|
|||||||
@@ -164,3 +164,48 @@ async def test_returns_zero_for_empty_assets(tmp_path: Path) -> None:
|
|||||||
ctx = _ctx(tmp_path)
|
ctx = _ctx(tmp_path)
|
||||||
n = await collect_market_snapshot(ctx, assets=(), now=_now())
|
n = await collect_market_snapshot(ctx, assets=(), now=_now())
|
||||||
assert n == 0
|
assert n == 0
|
||||||
|
|
||||||
|
|
||||||
|
def _read_dvol_history(ctx: MagicMock) -> list[dict]:
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
conn = connect(ctx.db_path)
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
try:
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT * FROM dvol_history ORDER BY timestamp"
|
||||||
|
).fetchall()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_eth_snapshot_mirrors_into_dvol_history(tmp_path: Path) -> None:
|
||||||
|
ctx = _ctx(tmp_path)
|
||||||
|
await collect_market_snapshot(ctx, assets=("ETH", "BTC"), now=_now())
|
||||||
|
rows = _read_dvol_history(ctx)
|
||||||
|
assert len(rows) == 1
|
||||||
|
assert Decimal(str(rows[0]["dvol"])) == Decimal("55")
|
||||||
|
assert Decimal(str(rows[0]["eth_spot"])) == Decimal("3000")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_btc_only_snapshot_does_not_touch_dvol_history(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
ctx = _ctx(tmp_path)
|
||||||
|
await collect_market_snapshot(ctx, assets=("BTC",), now=_now())
|
||||||
|
assert _read_dvol_history(ctx) == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_eth_snapshot_skips_dvol_history_when_dvol_missing(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
ctx = _ctx(tmp_path)
|
||||||
|
ctx.deribit.latest_dvol = AsyncMock(side_effect=RuntimeError("no dvol"))
|
||||||
|
await collect_market_snapshot(ctx, assets=("ETH",), now=_now())
|
||||||
|
# market_snapshots row still persisted, but dvol_history must stay empty
|
||||||
|
# because its schema enforces NOT NULL on dvol/eth_spot.
|
||||||
|
assert _read_dvol_history(ctx) == []
|
||||||
|
|||||||
Reference in New Issue
Block a user