Component · L4

Audit Log

github.com/juanmmm21/trade-audit-logger

Millisecond-precision audit trail for trades, orders, decisions and system errors.

Lines
665
Files
16
Test files
4
Commits
11
Started
29 Jun 2026
Last commit
6 Jul 2026
  • Python 100%

● Where it sits

Layer 4 of Quant — risk & audit.

Persistence service that records every decision, submitted order, executed order, and system error with millisecond precision for forensic audits. Eleventh module of the quant-core-infra ecosystem.

Repository: github.com/juanmmm21/trade-audit-logger

What it is and what problem it solves

In financial systems, absolute traceability is mandatory. When a strategy fails or an order is rejected, you need to reconstruct the full chain: signal → risk → routing → fill → error.

This module centralizes that trail in SQLite with forensic queries by symbol, event type, correlation, and time window.


Role in quant-core-infra

alpha-signal-generator ──► signal_decision ──┐
risk-management-engine ──► risk_check ─────┤
order-routing-gateway ──► routing_result ──┼──► trade-audit-logger (SQLite)
event-driven-backtester ──► order_filled ──┤              │
quant-metrics-calculator ──► metrics ──────┘              ▼
                                               quant-terminal-web / forensics

It's the ecosystem's immutable operational record.


Purpose

Demonstrates:

  • SQLite persistence with indexes for forensic queries
  • ISO8601 timestamps with millisecond precision
  • Asynchronous logger with queue and periodic flush
  • Batch JSONL ingestion and query CLI
  • Event model extensible via AuditEventType

Event types

TypeTypical source
signal_decisionalpha-signal-generator
risk_checkrisk-management-engine
order_submittedStrategy / backtester
order_filledevent-driven-backtester, gateway
routing_resultorder-routing-gateway
system_errorAny component
metrics_computedquant-metrics-calculator

Architecture

JSONL / runtime events
         │
         ▼
 AsyncAuditLogger (asyncio queue)
         │ periodic flush
         ▼
   SqliteAuditStore
         │
         ▼
   audit_events table

Components

ModuleResponsibility
sqlite_store.pyDDL, append, query, stats
async_logger.pyAsync queue + flush worker
ingest.pyJSONL parsing → AuditRecord
pipeline.pyEnd-to-end ingestion, query, and stats

Requirements

  • Python 3.11+
  • SQLite (stdlib)

Installation

cd trade-audit-logger
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

CLI usage

Import events

trade-audit-logger ingest \
  --input samples/audit_events.jsonl \
  --db data/audit.db

Query errors

trade-audit-logger query \
  --db data/audit.db \
  --event-type system_error \
  --symbol BTCUSDT

Statistics

trade-audit-logger stats --db data/audit.db

JSONL format

{
  "event_id": "evt-1",
  "event_type": "order_filled",
  "symbol": "BTCUSDT",
  "correlation_id": "ord-1",
  "occurred_at": "2024-01-01T12:00:00.750Z",
  "severity": "info",
  "payload": {
    "fill_price": "1000.5",
    "quantity": "0.01",
    "commission": "0.1"
  }
}

correlation_id links events from the same execution (run, order, or session).


Programmatic usage

import asyncio
from datetime import UTC, datetime

from trade_audit_logger import (
    AsyncAuditLogger,
    AuditEventType,
    AuditRecord,
    AuditSeverity,
    LoggerConfig,
    run_ingest_pipeline,
)

async def main() -> None:
    config = LoggerConfig(database_path="data/audit.db")
    logger = AsyncAuditLogger(config)
    await logger.start()
    await logger.log_event(
        AuditRecord(
            event_id="evt-live-1",
            event_type=AuditEventType.ORDER_SUBMITTED,
            symbol="BTCUSDT",
            correlation_id="ord-42",
            occurred_at=datetime.now(tz=UTC),
            severity=AuditSeverity.INFO,
            payload={"side": "buy", "quantity": "0.01"},
        )
    )
    await logger.stop()

asyncio.run(main())

Development

pytest -q
ruff check src tests
mypy src

Troubleshooting

SymptomLikely causeSolution
Duplicate event skippedevent_id already existsUse unique IDs per event
async audit logger is not runninglog_event called before start()Call await logger.start() first
Empty queryFilters too restrictiveCheck --from, --to, and --event-type

Roadmap

  • PostgreSQL backend for multi-node deployment
  • Automatic retention and date-based partitioning
  • Export to Parquet for offline analysis
  • Webhook/streaming to quant-terminal-web

License

MIT

esc
↑↓ navigate↵ open