Markets in Crypto-Assets (MiCA) Regulation (EU) entered into force in 2023. Articles 67–72 establish strict requirements for operators of algorithmic trading systems — which includes autonomous AI trading bots.
This guide maps MiCA requirements to technical implementation using Kakunin runtime binding.
Requirement: Crypto asset service providers (CASPs) must maintain appropriate governance for algorithmic trading, including:
Implementation with Kakunin:
// MiCA Art. 67: Define governance framework
const tradingBotPolicy = {
agentId: 'trading_bot_eu_v2',
operator: 'acme_trading_ltd',
operatorRegulatorId: 'ESMA-001234', // Regulatory registration
// Authority limits
scope: {
maxTransactionSize: 50000, // EUR
maxDailyVolume: 1000000,
allowedMarkets: ['EUR_USD', 'GBP_EUR'],
allowedRegions: ['eu-west-1'],
tradingHours: {
start: '08:00',
end: '17:00',
tz: 'UTC',
excludeWeekends: true,
},
},
// Risk controls
riskControls: {
killSwitchManual: true, // Operator can kill at any time
killSwitchAutomatic: 'anomaly_0.85', // Auto-revoke if anomaly > 0.85
maxConsecutiveFailures: 3,
circuitBreakerThreshold: 0.75,
},
// Testing schedule
testing: {
basefitTesting: '2026-06-01', // Annual fitness test required
scenarioTesting: 'quarterly', // Quarterly stress tests
penetrationTesting: 'annual',
},
// Governance documentation
governance: {
documentedBy: 'compliance_team@acme',
approvedBy: 'ceo@acme',
lastReview: '2026-05-01',
nextReview: '2026-08-01',
},
};
// Register this policy with Kakunin
const result = await kakunin.governance.registerPolicy(tradingBotPolicy);
console.log(`Policy registered: ${result.policyId}`);
Requirement: Operators must have pre-trade controls that prevent orders violating scope limits before they're sent to the exchange.
Implementation:
// Pre-trade risk check (runs BEFORE signing)
async function preTradeRiskCheck(tradeRequest) {
const scope = await kakunin.governance.getScope('trading_bot_eu_v2');
const baseline = await kakunin.monitoring.getBaseline('trading_bot_eu_v2');
// Check 1: Size limit
if (tradeRequest.size > scope.maxTransactionSize) {
return {
allowed: false,
reason: 'exceeds_scope_size',
limit: scope.maxTransactionSize,
requested: tradeRequest.size,
};
}
// Check 2: Market allowed
if (!scope.allowedMarkets.includes(tradeRequest.market)) {
return {
allowed: false,
reason: 'market_not_allowed',
allowed: scope.allowedMarkets,
requested: tradeRequest.market,
};
}
// Check 3: Daily volume cap
const todayVolume = await kakunin.monitoring.getTodayVolume('trading_bot_eu_v2');
if (todayVolume + tradeRequest.size > scope.maxDailyVolume) {
return {
allowed: false,
reason: 'daily_volume_cap_exceeded',
cap: scope.maxDailyVolume,
current: todayVolume,
requested: tradeRequest.size,
};
}
// Check 4: Trading hours
const now = new Date();
const utcHour = now.getUTCHours();
if (utcHour < 8 || utcHour >= 17) {
return {
allowed: false,
reason: 'outside_trading_hours',
hours: '08:00-17:00 UTC',
};
}
// Check 5: Circuit breaker (anomaly score)
const anomalyScore = await kakunin.monitoring.getCurrentAnomalyScore('trading_bot_eu_v2');
if (anomalyScore > scope.circuitBreakerThreshold) {
return {
allowed: false,
reason: 'circuit_breaker_active',
anomalyScore,
threshold: scope.circuitBreakerThreshold,
};
}
return { allowed: true };
}
Requirement: All trades must be logged with sufficient detail for regulators to audit.
Implementation:
// Post-trade logging (MiCA Art. 69)
async function logTrade(tradeRequest, tradeResponse) {
const auditEntry = {
timestamp: new Date().toISOString(),
// Agent identification
agentId: 'trading_bot_eu_v2',
agentCertSerial: 'f1d4e8c7b2a9f3e6', // From certificate
agentOperator: 'acme_trading_ltd',
// Trade details
orderId: tradeResponse.orderId,
market: tradeRequest.market,
side: tradeRequest.side, // BUY or SELL
size: tradeRequest.size,
executionPrice: tradeResponse.executionPrice,
executionTime: tradeResponse.executionTime,
// Proof of authorization
certificate: tradeRequest.certificate, // X.509 cert
signature: tradeRequest.signature, // Cryptographic proof
signatureVerified: true,
// Pre-trade controls
preTradeCheck: {
withinScope: true,
withinBaseline: true,
anomalyScore: 0.23,
circuitBreakerActive: false,
},
// Counterparty (if known)
counterparty: tradeResponse.counterparty,
counterpartyId: tradeResponse.counterpartyId,
};
// Store immutably (WORM — write-once, read-many)
await kakunin.auditLog.insert(auditEntry);
// Send to regulator reporting system
await submitToFinancialReporting({
reportingId: 'ESMA-EMIR', // European transaction reporting
entry: auditEntry,
});
}
Requirement: The entity deploying the bot (the operator) must be distinct from the entity who authorized it (typically a board decision), and both must be distinct from the technical implementation.
Implementation via Certificate Issuer:
// MiCA Art. 70: Segregation of duties proven via certificate chain
// Step 1: Board decision
const boardResolution = {
passed: '2026-05-01',
approvedBy: ['ceo@acme', 'cfo@acme', 'compliance_lead@acme'],
resolution: 'Authorize deployment of trading_bot_eu_v2 with €50K scope',
};
// Step 2: Operator deploys, but doesn't issue cert
// Kakunin (trusted third party) issues the certificate
const cert = await kakunin.agents.getCertificate(agent.id, {
validityDays: 365,
scope: { /* ... */ },
issuer: 'Kakunin Root CA', // Third-party issuer
signedBy: 'kakunin-root-key', // Not operator's key
});
// Step 3: Regulator can verify
// - Certificate was issued by Kakunin (not self-signed)
// - Board approved the scope
// - Operator deployed it
// - All three roles are separate
Requirement: Maintain permanent, tamper-proof records of every algorithm decision and trade.
Implementation:
// Article 71: Write-once audit log with immutable records
// Every agent action creates an event
async function logAgentEvent(agent, event) {
const logEntry = {
_id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
// Event classification
eventType: 'trade.executed', // or 'anomaly.detected', 'cert.revoked', etc.
// Agent identification
agentId: agent.id,
agentCertificate: agent.certificatePem,
agentSignature: event.signature, // Proves agent authorized this
// Event details
actionType: event.actionType,
payload: event.payload,
riskScore: event.riskScore,
// Pre/post state
stateBefore: event.stateBefore,
stateAfter: event.stateAfter,
// Authorization chain
authorizedBy: agent.id, // Agent certificate
verifiedBy: 'kakunin-signing-service', // Kakunin verified it
approvedBy: null, // Null = not human-approved (automatic)
};
// Write to WORM log (cannot be updated or deleted)
await kakunin.auditLog.insert(logEntry);
// Replicate to external audit system (redundancy)
await externalAuditStorage.write({
path: `/agent-events/${agent.id}/${logEntry._id}`,
data: logEntry,
});
return logEntry._id;
}
Requirement: Regular testing of the algorithm's functioning under various market conditions. Document how the firm responds to incidents (circuit breaker, manual kill, etc.).
Implementation:
// Article 72: Fitness testing & incident procedures
const testingPlan = {
// 1. Annual fitness test (required)
annualFitnessTesting: {
date: '2026-06-01',
scope: [
'Does the algorithm still execute trades correctly?',
'Does the circuit breaker activate on anomalies?',
'Can the operator manually kill the algorithm?',
'Are all audit logs intact and tamper-proof?',
],
results: null, // To be filled after test
},
// 2. Quarterly stress testing
stressTests: [
{
name: 'Flash crash scenario',
description: 'Market drops 30% in 5 seconds. Can agent handle volatility?',
date: '2026-05-31',
passedChecks: [
'Circuit breaker activated at anomaly > 0.75',
'No trades executed above scope limit',
'Audit log complete',
],
},
{
name: 'Counterparty default',
description: 'Major exchange goes offline. Does agent retry safely?',
date: '2026-06-30',
},
{
name: 'Market manipulation',
description: 'Pump-and-dump detected. Agent detects anomaly and stops.',
date: '2026-07-31',
},
],
// 3. Incident response procedures
incidentResponse: {
// Procedure 1: Anomaly detected
anomalyDetected: {
threshold: 0.85,
action: 'Auto-revoke certificate',
notification: 'SMS + email to operator team',
graceperiod: '5 minutes',
escalation: 'If not ACK'd, revoke is final',
},
// Procedure 2: Manual kill switch
manualKillSwitch: {
who: ['ceo@acme', 'cfo@acme', 'compliance_lead@acme'],
howFast: '< 10 seconds from decision to certificate revocation',
log: 'Logged with user identity + timestamp',
},
// Procedure 3: Post-incident audit
postIncidentAudit: {
trigger: 'Any anomaly > 0.75 or manual kill',
timeline: 'Within 24 hours',
scope: [
'What caused the incident?',
'What was the impact ($ trades blocked)?',
'What controls failed?',
'What controls worked?',
'What changes needed?',
],
},
},
};
| Article | Requirement | Implementation | Status |
|---|---|---|---|
| 67 | Governance framework | Board-approved policy, documented scope, testing plan | ✓ |
| 68 | Pre-trade controls | Size, market, time, circuit breaker checks | ✓ |
| 69 | Post-trade transparency | Complete audit log with agent signature | ✓ |
| 70 | Segregation of duties | Third-party cert issuer (Kakunin) proves separation | ✓ |
| 71 | Record-keeping | WORM audit log, immutable event records | ✓ |
| 72 | Testing & response | Annual fitness tests, incident procedures, auto-revocation | ✓ |
# Kubernetes deployment for MiCA compliance
apiVersion: apps/v1
kind: Deployment
metadata:
name: trading-bot-mica
spec:
replicas: 1
selector:
matchLabels:
app: trading-bot-mica
template:
metadata:
labels:
app: trading-bot-mica
annotations:
mica-policy: registered
governance-framework: version-2
spec:
# Pod security: RLS (restrictive Linux security)
securityContext:
runAsNonRoot: true
fsReadOnlyRootFilesystem: true
containers:
# Main trading bot
- name: trading-bot
image: myrepo/trading-bot:v2
env:
- name: KAKUNIN_API_KEY
valueFrom:
secretKeyRef:
name: kakunin-secrets
key: api-key
- name: OPERATOR_ID
value: acme_trading_ltd
volumeMounts:
- name: cert-volume
mountPath: /var/certs
readOnly: true
- name: audit-log-volume
mountPath: /var/audit
readOnly: false
# MiCA enforcer sidecar
- name: mica-enforcer
image: kakunin/mica-enforcer:latest
env:
- name: MICA_POLICY_ID
value: trading_bot_eu_v2_policy
- name: OPERATOR_ID
value: acme_trading_ltd
ports:
- containerPort: 8443
name: mica-api
volumeMounts:
- name: cert-volume
mountPath: /var/certs
readOnly: true
- name: audit-log-volume
mountPath: /var/audit
readOnly: false
volumes:
- name: cert-volume
projected:
sources:
- secret:
name: agent-certificate
items:
- key: cert.pem
path: cert.pem
- key: kms-key-arn.txt
path: kms-key-arn.txt
- name: audit-log-volume
persistentVolumeClaim:
claimName: audit-log-pvc
readOnly: false
# Node affinity: EU regions only (GDPR)
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: region
operator: In
values:
- eu-west-1
- eu-central-1
Ready to deploy? See Quickstart: MiCA-Compliant Trading Bot.