refactor: clean up imports and improve code formatting across multiple files
CI / lint-test-build (push) Successful in 2m21s
CI / lint-test-build (push) Successful in 2m21s
This commit is contained in:
+12
-24
@@ -6,51 +6,40 @@ from collections.abc import AsyncIterator
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import cast
|
|
||||||
from urllib.parse import parse_qs
|
|
||||||
|
|
||||||
import orjson
|
|
||||||
from fastapi import APIRouter, Depends, Request, Response
|
from fastapi import APIRouter, Depends, Request, Response
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
from arbitrade.alerting.notifier import SupportsAlerts, SupportsAlertStatus
|
|
||||||
from arbitrade.api.auth import require_dashboard_auth
|
from arbitrade.api.auth import require_dashboard_auth
|
||||||
from arbitrade.api.control_state import DashboardControlState
|
|
||||||
from arbitrade.config.pairing_sync import sync_pairings_from_kraken
|
from arbitrade.config.pairing_sync import sync_pairings_from_kraken
|
||||||
from arbitrade.config.service import ConfigPairing
|
|
||||||
from arbitrade.dashboard.context import (
|
from arbitrade.dashboard.context import (
|
||||||
_backtesting_panel_context,
|
_backtesting_panel_context,
|
||||||
_recent_backtest_reports,
|
_recent_backtest_reports,
|
||||||
)
|
)
|
||||||
from arbitrade.dashboard.dashboard import (
|
from arbitrade.dashboard.dashboard import (
|
||||||
_alert_status_snapshot,
|
_alert_status_snapshot,
|
||||||
_dashboard_backtesting_job_handler,
|
_dashboard_audit,
|
||||||
_dashboard_backtesting_job_export,
|
|
||||||
_dashboard_backtesting_handler,
|
_dashboard_backtesting_handler,
|
||||||
|
_dashboard_backtesting_job_export,
|
||||||
|
_dashboard_backtesting_job_handler,
|
||||||
|
_dashboard_charts,
|
||||||
|
_dashboard_config_context,
|
||||||
|
_dashboard_controls,
|
||||||
_dashboard_ctl_cfg,
|
_dashboard_ctl_cfg,
|
||||||
|
_dashboard_ctl_kill,
|
||||||
_dashboard_ctl_start,
|
_dashboard_ctl_start,
|
||||||
_dashboard_ctl_stop,
|
_dashboard_ctl_stop,
|
||||||
_dashboard_ctl_kill,
|
|
||||||
_dashboard_metrics,
|
_dashboard_metrics,
|
||||||
_dashboard_overview,
|
_dashboard_overview,
|
||||||
_dashboard_config_context,
|
|
||||||
_dashboard_charts,
|
|
||||||
_dashboard_controls,
|
|
||||||
_dashboard_audit,
|
|
||||||
_dashboard_response,
|
|
||||||
_dashboard_pairings_response,
|
_dashboard_pairings_response,
|
||||||
|
_dashboard_response,
|
||||||
_pairing_repo,
|
_pairing_repo,
|
||||||
_toggle_pairing
|
_toggle_pairing,
|
||||||
)
|
)
|
||||||
from arbitrade.detection.graph import CurrencyGraph, TriangularCycle
|
|
||||||
from arbitrade.storage.pg_store import PgStore
|
|
||||||
from arbitrade.storage.repositories import (
|
from arbitrade.storage.repositories import (
|
||||||
AuditRecord,
|
|
||||||
AuditRepository,
|
|
||||||
BacktestJobRepository,
|
BacktestJobRepository,
|
||||||
ConfigPairingRepository,
|
ConfigPairingRepository,
|
||||||
KrakenAccountSnapshotRepository,
|
|
||||||
LogRepository,
|
LogRepository,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,8 +49,7 @@ public_router = APIRouter()
|
|||||||
|
|
||||||
def _resolve_templates_directory() -> str:
|
def _resolve_templates_directory() -> str:
|
||||||
# Support source layout, Docker runtime (/app), and installed package data.
|
# Support source layout, Docker runtime (/app), and installed package data.
|
||||||
source_layout_path = Path(
|
source_layout_path = Path(__file__).resolve().parents[3] / "web" / "templates"
|
||||||
__file__).resolve().parents[3] / "web" / "templates"
|
|
||||||
if source_layout_path.is_dir():
|
if source_layout_path.is_dir():
|
||||||
return str(source_layout_path)
|
return str(source_layout_path)
|
||||||
|
|
||||||
@@ -70,8 +58,7 @@ def _resolve_templates_directory() -> str:
|
|||||||
return str(docker_runtime_path)
|
return str(docker_runtime_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
package_path = resources.files(
|
package_path = resources.files("arbitrade").joinpath("web", "templates")
|
||||||
"arbitrade").joinpath("web", "templates")
|
|
||||||
if package_path.is_dir():
|
if package_path.is_dir():
|
||||||
return str(package_path)
|
return str(package_path)
|
||||||
except (ModuleNotFoundError, AttributeError):
|
except (ModuleNotFoundError, AttributeError):
|
||||||
@@ -375,6 +362,7 @@ async def health() -> JSONResponse:
|
|||||||
|
|
||||||
# ── Pairing API ─────────────────────────────────────────────────────────────
|
# ── Pairing API ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@router.get("/dashboard/api/pairings", response_class=JSONResponse)
|
@router.get("/dashboard/api/pairings", response_class=JSONResponse)
|
||||||
async def dashboard_api_pairings(
|
async def dashboard_api_pairings(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
@@ -1,25 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
from fastapi import Request
|
||||||
from asyncio import Lock
|
|
||||||
from collections.abc import AsyncIterator
|
|
||||||
from datetime import UTC, datetime
|
|
||||||
from importlib import resources
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import cast
|
|
||||||
from urllib.parse import parse_qs
|
|
||||||
|
|
||||||
import orjson
|
|
||||||
from fastapi import APIRouter, Depends, Request, Response
|
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
|
||||||
from fastapi.templating import Jinja2Templates
|
|
||||||
from arbitrade.storage.repositories import (
|
from arbitrade.storage.repositories import (
|
||||||
AuditRecord,
|
|
||||||
AuditRepository,
|
|
||||||
BacktestJobRepository,
|
BacktestJobRepository,
|
||||||
ConfigPairingRepository,
|
|
||||||
KrakenAccountSnapshotRepository,
|
|
||||||
LogRepository,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,31 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from asyncio import Lock
|
|
||||||
from collections.abc import AsyncIterator
|
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
from httpcore import request
|
|
||||||
import orjson
|
import orjson
|
||||||
from fastapi import APIRouter, Depends, Request, Response
|
from fastapi import APIRouter, Depends, Request, Response
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
from arbitrade.alerting.notifier import SupportsAlerts, SupportsAlertStatus
|
from arbitrade.alerting.notifier import SupportsAlerts, SupportsAlertStatus
|
||||||
from arbitrade.api.auth import require_dashboard_auth
|
from arbitrade.api.auth import require_dashboard_auth
|
||||||
from arbitrade.api.control_state import DashboardControlState
|
from arbitrade.api.control_state import DashboardControlState
|
||||||
from arbitrade.api.routes import (
|
from arbitrade.config.service import ConfigPairing
|
||||||
|
from arbitrade.dashboard.context import (
|
||||||
_backtesting_panel_context,
|
_backtesting_panel_context,
|
||||||
)
|
)
|
||||||
from arbitrade.config.pairing_sync import sync_pairings_from_kraken
|
|
||||||
from arbitrade.config.service import ConfigPairing
|
|
||||||
|
|
||||||
from arbitrade.api.control_state import DashboardControlState
|
|
||||||
from arbitrade.storage.pg_store import PgStore
|
from arbitrade.storage.pg_store import PgStore
|
||||||
from arbitrade.detection.graph import CurrencyGraph, TriangularCycle
|
|
||||||
from arbitrade.storage.repositories import (
|
from arbitrade.storage.repositories import (
|
||||||
AuditRecord,
|
AuditRecord,
|
||||||
AuditRepository,
|
AuditRepository,
|
||||||
BacktestJobRepository,
|
BacktestJobRepository,
|
||||||
ConfigPairingRepository,
|
ConfigPairingRepository,
|
||||||
KrakenAccountSnapshotRepository,
|
KrakenAccountSnapshotRepository,
|
||||||
LogRepository,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
router = APIRouter(dependencies=[Depends(require_dashboard_auth)])
|
router = APIRouter(dependencies=[Depends(require_dashboard_auth)])
|
||||||
@@ -42,8 +34,7 @@ public_router = APIRouter()
|
|||||||
|
|
||||||
def _resolve_templates_directory() -> str:
|
def _resolve_templates_directory() -> str:
|
||||||
# Support source layout, Docker runtime (/app), and installed package data.
|
# Support source layout, Docker runtime (/app), and installed package data.
|
||||||
source_layout_path = Path(
|
source_layout_path = Path(__file__).resolve().parents[3] / "web" / "templates"
|
||||||
__file__).resolve().parents[3] / "web" / "templates"
|
|
||||||
if source_layout_path.is_dir():
|
if source_layout_path.is_dir():
|
||||||
return str(source_layout_path)
|
return str(source_layout_path)
|
||||||
|
|
||||||
@@ -52,8 +43,7 @@ def _resolve_templates_directory() -> str:
|
|||||||
return str(docker_runtime_path)
|
return str(docker_runtime_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
package_path = resources.files(
|
package_path = resources.files("arbitrade").joinpath("web", "templates")
|
||||||
"arbitrade").joinpath("web", "templates")
|
|
||||||
if package_path.is_dir():
|
if package_path.is_dir():
|
||||||
return str(package_path)
|
return str(package_path)
|
||||||
except (ModuleNotFoundError, AttributeError):
|
except (ModuleNotFoundError, AttributeError):
|
||||||
@@ -242,11 +232,9 @@ async def _dashboard_pairings_response(
|
|||||||
if source:
|
if source:
|
||||||
pairings = [p for p in pairings if p.source.lower() == source.lower()]
|
pairings = [p for p in pairings if p.source.lower() == source.lower()]
|
||||||
if base:
|
if base:
|
||||||
pairings = [p for p in pairings if p.base_asset.lower() ==
|
pairings = [p for p in pairings if p.base_asset.lower() == base.lower()]
|
||||||
base.lower()]
|
|
||||||
if quote:
|
if quote:
|
||||||
pairings = [p for p in pairings if p.quote_asset.lower() ==
|
pairings = [p for p in pairings if p.quote_asset.lower() == quote.lower()]
|
||||||
quote.lower()]
|
|
||||||
|
|
||||||
# Sort
|
# Sort
|
||||||
reverse = order.lower() == "desc"
|
reverse = order.lower() == "desc"
|
||||||
@@ -391,8 +379,7 @@ async def _dashboard_overview(request: Request) -> dict[str, object]:
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
""")
|
""")
|
||||||
if acct_row is not None:
|
if acct_row is not None:
|
||||||
fee_tier = str(
|
fee_tier = str(acct_row["fee_tier"]) if acct_row["fee_tier"] is not None else "—"
|
||||||
acct_row["fee_tier"]) if acct_row["fee_tier"] is not None else "—"
|
|
||||||
maker_fee = (
|
maker_fee = (
|
||||||
f"{float(acct_row['maker_fee']):.4%}"
|
f"{float(acct_row['maker_fee']):.4%}"
|
||||||
if acct_row["maker_fee"] is not None
|
if acct_row["maker_fee"] is not None
|
||||||
@@ -420,8 +407,7 @@ async def _dashboard_overview(request: Request) -> dict[str, object]:
|
|||||||
try:
|
try:
|
||||||
parsed = json.loads(balances_raw)
|
parsed = json.loads(balances_raw)
|
||||||
if isinstance(parsed, dict):
|
if isinstance(parsed, dict):
|
||||||
non_zero = {k: float(v)
|
non_zero = {k: float(v) for k, v in parsed.items() if float(v) > 0.0}
|
||||||
for k, v in parsed.items() if float(v) > 0.0}
|
|
||||||
if non_zero:
|
if non_zero:
|
||||||
balances_value = "<br>".join(
|
balances_value = "<br>".join(
|
||||||
f"{v:.6g} {k}" for k, v in sorted(non_zero.items())
|
f"{v:.6g} {k}" for k, v in sorted(non_zero.items())
|
||||||
@@ -442,8 +428,7 @@ async def _dashboard_overview(request: Request) -> dict[str, object]:
|
|||||||
"trade_ref": str(r["trade_ref"]),
|
"trade_ref": str(r["trade_ref"]),
|
||||||
"status": str(r["status"]),
|
"status": str(r["status"]),
|
||||||
"started_at": (
|
"started_at": (
|
||||||
r["started_at"].isoformat() if isinstance(
|
r["started_at"].isoformat() if isinstance(r["started_at"], datetime) else "—"
|
||||||
r["started_at"], datetime) else "—"
|
|
||||||
),
|
),
|
||||||
"cycle": str(r["cycle"]) if r["cycle"] is not None else "—",
|
"cycle": str(r["cycle"]) if r["cycle"] is not None else "—",
|
||||||
}
|
}
|
||||||
@@ -457,8 +442,7 @@ async def _dashboard_overview(request: Request) -> dict[str, object]:
|
|||||||
f"{float(r['est_profit']):.2f} USD" if r["est_profit"] is not None else "—"
|
f"{float(r['est_profit']):.2f} USD" if r["est_profit"] is not None else "—"
|
||||||
),
|
),
|
||||||
"detected_at": (
|
"detected_at": (
|
||||||
r["detected_at"].isoformat() if isinstance(
|
r["detected_at"].isoformat() if isinstance(r["detected_at"], datetime) else "—"
|
||||||
r["detected_at"], datetime) else "—"
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
for r in latest_opportunities
|
for r in latest_opportunities
|
||||||
@@ -499,10 +483,8 @@ async def _dashboard_charts(request: Request) -> dict[str, object]:
|
|||||||
labels.append(row["detected_at"].isoformat())
|
labels.append(row["detected_at"].isoformat())
|
||||||
else:
|
else:
|
||||||
labels.append(f"opportunity-{index + 1}")
|
labels.append(f"opportunity-{index + 1}")
|
||||||
np = [float(row["net_pct"]) if row["net_pct"]
|
np = [float(row["net_pct"]) if row["net_pct"] is not None else 0.0 for row in cr]
|
||||||
is not None else 0.0 for row in cr]
|
ep = [float(row["est_profit"]) if row["est_profit"] is not None else 0.0 for row in cr]
|
||||||
ep = [float(row["est_profit"]) if row["est_profit"]
|
|
||||||
is not None else 0.0 for row in cr]
|
|
||||||
cycles = [str(row["cycle"]) for row in cr]
|
cycles = [str(row["cycle"]) for row in cr]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -576,8 +558,7 @@ async def _dashboard_config_context(request: Request) -> dict[str, object]:
|
|||||||
max_consecutive_failures_value = (
|
max_consecutive_failures_value = (
|
||||||
str(rs.max_consecutive_failures) if rs.max_consecutive_failures is not None else ""
|
str(rs.max_consecutive_failures) if rs.max_consecutive_failures is not None else ""
|
||||||
)
|
)
|
||||||
strategy_stat_arb_enabled = bool(
|
strategy_stat_arb_enabled = bool(getattr(rs, "strategy_enable_stat_arb_experiment", False))
|
||||||
getattr(rs, "strategy_enable_stat_arb_experiment", False))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
# Runtime
|
# Runtime
|
||||||
@@ -698,8 +679,7 @@ def _dashboard_controls(request: Request) -> dict[str, object]:
|
|||||||
alerts_last_channel_results = [
|
alerts_last_channel_results = [
|
||||||
str(item) for item in cast(list[object], alert_status.get("last_channel_results", []))
|
str(item) for item in cast(list[object], alert_status.get("last_channel_results", []))
|
||||||
]
|
]
|
||||||
strategy_stat_arb_enabled = bool(
|
strategy_stat_arb_enabled = bool(getattr(rs, "strategy_enable_stat_arb_experiment", False))
|
||||||
getattr(rs, "strategy_enable_stat_arb_experiment", False))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"execution_status": "running" if ctl.is_running else "stopped",
|
"execution_status": "running" if ctl.is_running else "stopped",
|
||||||
@@ -755,8 +735,7 @@ async def _dashboard_backtesting_handler(request: Request) -> HTMLResponse:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
custom_fee_rate = (
|
custom_fee_rate = (
|
||||||
float(defaults["custom_fee_rate"]
|
float(defaults["custom_fee_rate"]) if defaults["custom_fee_rate"].strip() else None
|
||||||
) if defaults["custom_fee_rate"].strip() else None
|
|
||||||
)
|
)
|
||||||
fee_rate = await _fee_rate_for_profile(
|
fee_rate = await _fee_rate_for_profile(
|
||||||
defaults["fee_profile"], custom_fee_rate, request=request
|
defaults["fee_profile"], custom_fee_rate, request=request
|
||||||
@@ -767,8 +746,7 @@ async def _dashboard_backtesting_handler(request: Request) -> HTMLResponse:
|
|||||||
if not symbols_str.strip():
|
if not symbols_str.strip():
|
||||||
pairing_repo = ConfigPairingRepository(request.app.state.store)
|
pairing_repo = ConfigPairingRepository(request.app.state.store)
|
||||||
enabled = await pairing_repo.list_pairings(enabled_only=True)
|
enabled = await pairing_repo.list_pairings(enabled_only=True)
|
||||||
symbols_str = ",".join(
|
symbols_str = ",".join(f"{p.base_asset}/{p.quote_asset}" for p in enabled)
|
||||||
f"{p.base_asset}/{p.quote_asset}" for p in enabled)
|
|
||||||
|
|
||||||
config_dict: dict[str, object] = {
|
config_dict: dict[str, object] = {
|
||||||
"source": defaults["source"],
|
"source": defaults["source"],
|
||||||
@@ -882,8 +860,7 @@ async def _dashboard_backtesting_job_export(request: Request, job_id: str) -> Re
|
|||||||
return Response(
|
return Response(
|
||||||
content=orjson.dumps(payload).decode("utf-8"),
|
content=orjson.dumps(payload).decode("utf-8"),
|
||||||
media_type="application/x-jsonlines",
|
media_type="application/x-jsonlines",
|
||||||
headers={
|
headers={"Content-Disposition": f"attachment; filename=backtest_{job_id[:8]}.jsonl"},
|
||||||
"Content-Disposition": f"attachment; filename=backtest_{job_id[:8]}.jsonl"},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ async def run_log_archive(store: PgStore, retention_days: int = _RETENTION_DAYS)
|
|||||||
repo = LogArchiveRepository(store)
|
repo = LogArchiveRepository(store)
|
||||||
count = await repo.archive_before(cutoff)
|
count = await repo.archive_before(cutoff)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
_LOG.info("log_archive_complete",
|
_LOG.info("log_archive_complete", cutoff=cutoff.isoformat(), archived=count)
|
||||||
cutoff=cutoff.isoformat(), archived=count)
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,12 @@ class AuditRepository:
|
|||||||
|
|
||||||
async def insert(self, record: AuditRecord) -> None:
|
async def insert(self, record: AuditRecord) -> None:
|
||||||
async with self._store.pool.acquire() as conn:
|
async with self._store.pool.acquire() as conn:
|
||||||
|
payload = None
|
||||||
|
if record.payload is not None:
|
||||||
|
try:
|
||||||
|
payload = orjson.dumps(record.payload).decode("utf-8")
|
||||||
|
except Exception:
|
||||||
|
payload = None
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO audit_events (
|
INSERT INTO audit_events (
|
||||||
@@ -258,8 +264,7 @@ class AuditRepository:
|
|||||||
record.actor,
|
record.actor,
|
||||||
record.event_type,
|
record.event_type,
|
||||||
record.decision,
|
record.decision,
|
||||||
(None if record.payload is None else orjson.dumps(
|
payload,
|
||||||
record.payload).decode("utf-8")),
|
|
||||||
record.correlation_id,
|
record.correlation_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -279,6 +284,9 @@ class AuditRepository:
|
|||||||
for row in rows:
|
for row in rows:
|
||||||
payload: dict[str, Any] | None = None
|
payload: dict[str, Any] | None = None
|
||||||
raw_payload = row["payload"]
|
raw_payload = row["payload"]
|
||||||
|
correlation_id = None
|
||||||
|
if row["correlation_id"] is not None:
|
||||||
|
correlation_id = str(row["correlation_id"])
|
||||||
if isinstance(raw_payload, str) and raw_payload:
|
if isinstance(raw_payload, str) and raw_payload:
|
||||||
decoded = orjson.loads(raw_payload)
|
decoded = orjson.loads(raw_payload)
|
||||||
if isinstance(decoded, dict):
|
if isinstance(decoded, dict):
|
||||||
@@ -291,10 +299,7 @@ class AuditRepository:
|
|||||||
event_type=str(row["event_type"]),
|
event_type=str(row["event_type"]),
|
||||||
decision=str(row["decision"]),
|
decision=str(row["decision"]),
|
||||||
payload=payload,
|
payload=payload,
|
||||||
correlation_id=(
|
correlation_id=correlation_id,
|
||||||
str(row["correlation_id"]
|
|
||||||
) if row["correlation_id"] is not None else None
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -354,6 +359,9 @@ class RuntimeStateRepository:
|
|||||||
|
|
||||||
balances: dict[str, Any] | None = None
|
balances: dict[str, Any] | None = None
|
||||||
raw_balances = row["last_known_balances"]
|
raw_balances = row["last_known_balances"]
|
||||||
|
kill_switch_reason = None
|
||||||
|
if row["kill_switch_reason"] is not None:
|
||||||
|
kill_switch_reason = str(row["kill_switch_reason"])
|
||||||
if isinstance(raw_balances, str) and raw_balances:
|
if isinstance(raw_balances, str) and raw_balances:
|
||||||
decoded = orjson.loads(raw_balances)
|
decoded = orjson.loads(raw_balances)
|
||||||
if isinstance(decoded, dict):
|
if isinstance(decoded, dict):
|
||||||
@@ -363,10 +371,7 @@ class RuntimeStateRepository:
|
|||||||
snapshot_at=row["snapshot_at"],
|
snapshot_at=row["snapshot_at"],
|
||||||
is_running=bool(row["is_running"]),
|
is_running=bool(row["is_running"]),
|
||||||
kill_switch_active=bool(row["kill_switch_active"]),
|
kill_switch_active=bool(row["kill_switch_active"]),
|
||||||
kill_switch_reason=(
|
kill_switch_reason=kill_switch_reason,
|
||||||
str(row["kill_switch_reason"]
|
|
||||||
) if row["kill_switch_reason"] is not None else None
|
|
||||||
),
|
|
||||||
open_trade_count=int(row["open_trade_count"]),
|
open_trade_count=int(row["open_trade_count"]),
|
||||||
last_known_balances=balances,
|
last_known_balances=balances,
|
||||||
note=str(row["note"]) if row["note"] is not None else None,
|
note=str(row["note"]) if row["note"] is not None else None,
|
||||||
@@ -773,11 +778,12 @@ class ConfigBacktestingDefaultsRepository:
|
|||||||
defaults.execution_latency_ms,
|
defaults.execution_latency_ms,
|
||||||
)
|
)
|
||||||
if row:
|
if row:
|
||||||
|
starting_balances = None
|
||||||
|
if row["starting_balances"] is not None:
|
||||||
|
starting_balances = orjson.loads(row["starting_balances"])
|
||||||
|
|
||||||
return ConfigBacktestingDefaults(
|
return ConfigBacktestingDefaults(
|
||||||
starting_balances=(
|
starting_balances=starting_balances,
|
||||||
orjson.loads(row["starting_balances"]
|
|
||||||
) if row["starting_balances"] else None
|
|
||||||
),
|
|
||||||
trade_capital=row["trade_capital"],
|
trade_capital=row["trade_capital"],
|
||||||
min_profit_threshold=row["min_profit_threshold"],
|
min_profit_threshold=row["min_profit_threshold"],
|
||||||
slippage_bps=row["slippage_bps"],
|
slippage_bps=row["slippage_bps"],
|
||||||
@@ -795,11 +801,11 @@ class ConfigBacktestingDefaultsRepository:
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
""")
|
""")
|
||||||
if row:
|
if row:
|
||||||
|
starting_balances = None
|
||||||
|
if row["starting_balances"] is not None:
|
||||||
|
starting_balances = orjson.loads(row["starting_balances"])
|
||||||
return ConfigBacktestingDefaults(
|
return ConfigBacktestingDefaults(
|
||||||
starting_balances=(
|
starting_balances=starting_balances,
|
||||||
orjson.loads(row["starting_balances"]
|
|
||||||
) if row["starting_balances"] else None
|
|
||||||
),
|
|
||||||
trade_capital=row["trade_capital"],
|
trade_capital=row["trade_capital"],
|
||||||
min_profit_threshold=row["min_profit_threshold"],
|
min_profit_threshold=row["min_profit_threshold"],
|
||||||
slippage_bps=row["slippage_bps"],
|
slippage_bps=row["slippage_bps"],
|
||||||
@@ -833,11 +839,11 @@ class ConfigBacktestingDefaultsRepository:
|
|||||||
defaults.execution_latency_ms,
|
defaults.execution_latency_ms,
|
||||||
)
|
)
|
||||||
if row:
|
if row:
|
||||||
|
starting_balances = None
|
||||||
|
if row["starting_balances"] is not None:
|
||||||
|
starting_balances = orjson.loads(row["starting_balances"])
|
||||||
return ConfigBacktestingDefaults(
|
return ConfigBacktestingDefaults(
|
||||||
starting_balances=(
|
starting_balances=starting_balances,
|
||||||
orjson.loads(row["starting_balances"]
|
|
||||||
) if row["starting_balances"] else None
|
|
||||||
),
|
|
||||||
trade_capital=row["trade_capital"],
|
trade_capital=row["trade_capital"],
|
||||||
min_profit_threshold=row["min_profit_threshold"],
|
min_profit_threshold=row["min_profit_threshold"],
|
||||||
slippage_bps=row["slippage_bps"],
|
slippage_bps=row["slippage_bps"],
|
||||||
@@ -900,20 +906,20 @@ class KrakenAccountSnapshotRepository:
|
|||||||
""")
|
""")
|
||||||
if row is None:
|
if row is None:
|
||||||
return None
|
return None
|
||||||
|
trade_balance_raw = None
|
||||||
|
fee_schedule_raw = None
|
||||||
|
if row["trade_balance_raw"] is not None:
|
||||||
|
trade_balance_raw = orjson.loads(row["trade_balance_raw"])
|
||||||
|
if row["fee_schedule_raw"] is not None:
|
||||||
|
fee_schedule_raw = orjson.loads(row["fee_schedule_raw"])
|
||||||
return KrakenAccountSnapshot(
|
return KrakenAccountSnapshot(
|
||||||
snapshot_at=row["snapshot_at"],
|
snapshot_at=row["snapshot_at"],
|
||||||
fee_tier=row["fee_tier"],
|
fee_tier=row["fee_tier"],
|
||||||
maker_fee=row["maker_fee"],
|
maker_fee=row["maker_fee"],
|
||||||
taker_fee=row["taker_fee"],
|
taker_fee=row["taker_fee"],
|
||||||
thirty_day_volume=row["thirty_day_volume"],
|
thirty_day_volume=row["thirty_day_volume"],
|
||||||
trade_balance_raw=(
|
trade_balance_raw=trade_balance_raw,
|
||||||
orjson.loads(row["trade_balance_raw"]
|
fee_schedule_raw=fee_schedule_raw,
|
||||||
) if row["trade_balance_raw"] else None
|
|
||||||
),
|
|
||||||
fee_schedule_raw=(
|
|
||||||
orjson.loads(row["fee_schedule_raw"]
|
|
||||||
) if row["fee_schedule_raw"] else None
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1073,6 +1079,12 @@ class LogRepository:
|
|||||||
|
|
||||||
async def insert(self, record: LogRecord) -> None:
|
async def insert(self, record: LogRecord) -> None:
|
||||||
async with self._store.pool.acquire() as conn:
|
async with self._store.pool.acquire() as conn:
|
||||||
|
context = None
|
||||||
|
if record.context:
|
||||||
|
try:
|
||||||
|
context = orjson.dumps(record.context).decode("utf-8")
|
||||||
|
except Exception:
|
||||||
|
context = None
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO app_logs (recorded_at, level, logger, message, context)
|
INSERT INTO app_logs (recorded_at, level, logger, message, context)
|
||||||
@@ -1082,8 +1094,7 @@ class LogRepository:
|
|||||||
record.level,
|
record.level,
|
||||||
record.logger,
|
record.logger,
|
||||||
record.message,
|
record.message,
|
||||||
orjson.dumps(record.context).decode(
|
context,
|
||||||
"utf-8") if record.context else None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def query(
|
async def query(
|
||||||
|
|||||||
Reference in New Issue
Block a user