The Problem No One Talks About
You've built a solid algo trading system. FIX messages flow perfectly. Executions are sub-millisecond. Life is good.
Then the auditor calls.
"Can you prove this order wasn't modified after the fact?"
"Can you show the complete chain from signal to execution?"
"How do we know these logs are complete?"
Your answer: "Trust us, we have logs."
Their response: π
Why Traditional Logging Falls Short
Here's the uncomfortable truth about trading system logs:
βββββββββββββββββββββββββββββββββββββββββββ
β Traditional Logging β
βββββββββββββββββββββββββββββββββββββββββββ€
β β Admin can modify logs β
β β No proof of completeness β
β β Timestamp β proof of time β
β β Auditor must trust submitter β
β β Deletion is undetectable β
βββββββββββββββββββββββββββββββββββββββββββ
MiFID II RTS 25 requires clock sync to 100ΞΌs. But even if your timestamps are perfect, there's no cryptographic proof that:
- The log wasn't modified
- No entries were deleted
- The sequence is complete
The Sidecar Solution
What if you could add cryptographic proof to every trading event without touching your FIX engine?
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Trading System β
β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Algo ββββββΆβ Order ββββββΆβ FIX ββββββββΆβ Venue
β β Engine β β Manager β β Engine β β
β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
β β β β β
β β βββββββββββββ΄βββββββββββββββββ β
β β β Event Tap (async copy) β
β βΌ βΌ β
β βββββββββββββββββββββββββββββββββββββββ β
β β VCP Sidecar Process β β
β β βββββββββββ βββββββββββ βββββββββ β β
β β β Collect ββΆβ Hash ββΆβ Sign β ββββΆ Auditor β
β β β Events β β Chain β βEd25519β β β
β β βββββββββββ βββββββββββ βββββββββ β β
β βββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key: FIX messages flow unchanged. Zero latency impact.
The sidecar:
- Receives event copies asynchronously (no blocking)
- Hashes each event with SHA-256
- Chains to previous hash (tamper detection)
- Signs with Ed25519 (non-repudiation)
- Anchors batches in Merkle trees (efficient verification)
Show Me The Code
Here's what a FIX ExecutionReport looks like alongside its VCP audit event:
FIX Message (unchanged)
8=FIX.4.4|9=256|35=8|49=BROKER|56=CLIENT|
11=ORD-2025-001|37=EXE-12345|17=EXEC-67890|
150=F|39=2|55=XAUUSD|54=1|38=100|44=2650.50|
14=100|151=0|31=2650.45|32=100|6=2650.45|
60=20251222-14:30:05.120|10=078|
VCP Audit Event (parallel)
{
"header": {
"event_id": "01934e3a-7b2c-7f93-8f2a-1234567890ab",
"trace_id": "01934e3a-6a1b-7c82-9d1b-0987654321dc",
"timestamp": "2025-12-22T14:30:05.120000000Z",
"event_type": "EXE",
"clock_sync": "PTP_SYNCED",
"symbol": "XAUUSD"
},
"payload": {
"order_id": "EXE-12345",
"side": "BUY",
"price": "2650.50",
"quantity": "100",
"filled_qty": "100"
},
"security": {
"event_hash": "sha256:8f2a7b3c4d5e6f...",
"prev_hash": "sha256:a1b2c3d4e5f6...",
"signature": "ed25519:7g8h9i0j..."
}
}
The prev_hash links to the previous event. Break the chain? Instantly detectable.
The Hash Chain Explained
Event 1 (SIG) Event 2 (ORD) Event 3 (EXE)
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β hash: a1b2c3 β β hash: d4e5f6 β β hash: g7h8i9 β
β prev: 000000 ββββββββΆβ prev: a1b2c3 ββββββββΆβ prev: d4e5f6 β
β sig: xxxxx β β sig: yyyyy β β sig: zzzzz β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββ
β
Merkle Root: m1n2o3
(anchored periodically)
Try to:
- Delete Event 2? Chain breaks at Event 3.
- Modify Event 1? Hash changes, Event 2's prev_hash doesn't match.
- Insert fake event? Signature verification fails.
Mapping to FIX Tags
For implementations that want audit references embedded in FIX messages (optional):
| Tag | Name | Description |
|---|---|---|
| 20001 | AuditEventHash | SHA-256 hash (first 16 chars) |
| 20002 | AuditPrevHash | Link to previous event |
| 20003 | AuditTraceID | UUID v7 linking SIGβORDβEXE |
| 20004 | ClockSyncStatus | 0=Unknown, 1=NTP, 2=PTP, 3=GPS |
| 20005 | AuditMerkleRoot | Batch integrity proof |
Tags 20001-20999 are user-defined per FIX spec. Perfect for PoC without formal approval.
Why Not Just Use Blockchain?
I hear you. "Just put it on Ethereum!"
Problems:
- Latency: 12+ seconds per block vs. nanosecond trading
- Cost: Gas fees add up fast at 10K+ events/day
- Privacy: You don't want order flow on a public chain
- Complexity: Your ops team will love you (/s)
The sidecar approach:
- Local hash chains (instant)
- Periodic Merkle root anchoring (optional, batched)
- Private by default
- Runs alongside existing infra
What About Regulations?
| Regulation | Requirement | How This Helps |
|---|---|---|
| MiFID II RTS 25 | Clock sync β€100ΞΌs |
clock_sync field proves sync status |
| MiFID II RTS 6 | Algo audit trails | Complete SIGβORDβEXE chain |
| EU AI Act Art. 12 | Automatic logging | Cryptographic completeness proof |
| SEC 17a-4 | Tamper-evident records | Hash chain + signatures |
The EU AI Act deadline for high-risk AI systems is August 2027. Algo trading systems? Likely in scope.
Getting Started
1. Minimal Python Implementation
import hashlib
import json
from datetime import datetime, timezone
def create_event(event_type: str, payload: dict, prev_hash: str) -> dict:
header = {
"event_id": generate_uuid_v7(),
"timestamp": datetime.now(timezone.utc).isoformat(),
"event_type": event_type,
"clock_sync": "NTP_SYNCED"
}
# Canonical JSON for consistent hashing
canonical = json.dumps({"header": header, "payload": payload},
sort_keys=True, separators=(',', ':'))
event_hash = hashlib.sha256(canonical.encode()).hexdigest()
return {
"header": header,
"payload": payload,
"security": {
"event_hash": event_hash,
"prev_hash": prev_hash
}
}
# Chain events
prev = "0" * 64
sig_event = create_event("SIG", {"algo": "momentum-v2", "confidence": 0.87}, prev)
prev = sig_event["security"]["event_hash"]
ord_event = create_event("ORD", {"symbol": "XAUUSD", "side": "BUY", "qty": 100}, prev)
prev = ord_event["security"]["event_hash"]
exe_event = create_event("EXE", {"fill_price": 2650.45, "fill_qty": 100}, prev)
2. Hook Into Your FIX Engine
Most FIX engines have callback hooks. QuickFIX example:
void Application::onMessage(const FIX44::ExecutionReport& msg,
const FIX::SessionID& session) {
// Normal processing
processExecution(msg);
// Async emit to VCP sidecar (non-blocking)
vcpSidecar.emitAsync("EXE", extractPayload(msg));
}
Zero impact on your critical path.
The Full Spec
Everything here is based on an open specification:
- Spec: VCP v1.0
- IETF Draft: draft-kamimura-scitt-vcp
- GitHub: github.com/veritaschain
- License: CC BY 4.0 (spec), Apache 2.0 (code)
No vendor lock-in. No proprietary formats. Just cryptographic proof.
TL;DR
| What | How |
|---|---|
| Problem | Trading logs can be modified, deleted, faked |
| Solution | Hash chain + signatures in sidecar process |
| Impact on FIX | Zero. Messages flow unchanged. |
| Impact on latency | Zero. Async event tap. |
| Verification | Anyone can verify without trusting submitter |
The next time an auditor asks "can you prove it?", your answer changes from "trust us" to "here's the cryptographic proof."
Questions?
- Technical: technical@veritaschain.org
For reference, the materials are available here:
https://github.com/veritaschain/vcp-docs/tree/main/standards
If you're building algo trading systems and thinking about audit trails, I'd love to hear what challenges you're facing. Drop a comment below. π
Top comments (0)