LSE GTT Order Expiry Verification

Critical Bug FoundFinancial ServicesFormal VerificationRegulatory Compliance

Overview

In this example, CodeLogician is used to discover a critical bug in an enhanced (by Imandra) version of the London Stock Exchange trading system specification — a subtle race condition that would likely escape traditional testing but could result in exchange violations.

The Bug: MIT201 Specification Violation

Found: GTT orders can expire simultaneously with auction uncross events, violating design requirements.

Impact: Orders incorrectly excluded from auctions they should participate in.

Detection: Traditional testing missed this edge case—formal verification found it immediately with a concrete counterexample.


The Business Requirement

The publically available London Stock Exchange Guide to the Trading System (MIT201) specifies:

MIT201 Specification

"Any GTT orders with an expiry time during any auction call phase will not be expired until after uncrossing has completed and are therefore eligible to participate in that uncrossing."

This is a critical requirement. Violating it means:

  • ❌ Incorrect order handling during auctions
  • ❌ Potential customer losses and complaints
  • ❌ Exchange compliance violations
  • ❌ Reputational damage

The System

Model Components

The trading system implementation (384 lines of Python) includes:

Trading Modes

  • Continuous: Normal trading hours
  • Auction Call: Pre-auction phase with scheduled uncross

GTT Orders

  • Good Till Time: Orders with expiry timestamps
  • Auction Protection: Delay expiry during auctions

Market Order Extension

  • Extension Period: Auction if market orders remain
  • Effective Uncross: Original time + extension

Verification Function

  • conflict_reachable: Detects simultaneous events
  • Property: Uncross and Expiry should be impossible

The Bug Discovery

CodeLogician's Counterexample

Initial State (t=2699)

state = {
time: 2699,
mode: AuctionCallMode { uncross_at: 2700 },
gtt_order: Some { expires_at: 2700 },
market_order_present_after_uncross: true, ← Critical!
market_order_extension_period: 1
}

messages = [Tick, Tick]

Timeline Visualization

sequenceDiagram participant T as Time participant E as GTT Expiry Check participant M as Mode/Uncross Check participant S as State Note over T,S: Initial: t=2699, Auction Mode Note over S: uncross_at=2700, order expires_at=2700 Note over S: market_extension=1 T->>S: Tick 1: t=2699 → 2700 S->>E: Check expiry E->>E: In auction mode?<br/>Extend to max(2700, 2700+1)=2701 E->>E: 2700 >= 2701? NO E-->>S: Order NOT expired ✓ S->>M: Check mode change M->>M: 2700 >= 2700? YES M->>M: Market orders present?<br/>2700 >= 2700+1? NO M-->>S: Stay in auction ✓ Note over T,S: After Tick 1: t=2700, Still in auction T->>S: Tick 2: t=2700 → 2701 S->>E: Check expiry E->>E: In auction mode?<br/>Extend to max(2700, 2700+1)=2701 E->>E: 2701 >= 2701? YES E-->>S: ORDER EXPIRES ❌ S->>M: Check mode change M->>M: 2701 >= 2700? YES M->>M: Market orders present?<br/>2701 >= 2700+1? YES M-->>S: UNCROSS! ❌ Note over T,S: 💥 BOTH EVENTS IN SAME TICK! Note over T,S: Regulatory violation: MIT201 broken

💥 The Conflict

At t=2701:
  • Auction uncrosses (because 2701 ≥ 2700 + extension(1))
  • GTT order expires (because 2701 ≥ extended_expiry(2701))
  • Result: Both events occur simultaneously—MIT201 violation!

Why Traditional Testing Misses This

  • Requires exact timing alignment of order expiry, auction uncross, and market order extension
  • Bug only manifests when extension_period = 1 (would need exhaustive testing)
  • Individual features work correctly—bug emerges from feature interaction
  • Formal verification explores all possible states automatically

Value Demonstrated

Automated Discovery

  • ✓ No manual test case writing
  • ✓ Exhaustive analysis of all executions
  • ✓ Concrete counterexample with exact values
  • ✓ Identified specific fix location

Regulatory Compliance

  • ✓ Mathematical guarantee of rule compliance
  • ✓ Evidence for auditors and regulators
  • ✓ Protection against edge cases
  • ✓ Documentation of guaranteed behavior

Design Validation

  • ✅ Individual features work correctly
  • ❌ Feature interaction had bug
  • ✅ Verification caught it immediately
  • ✅ Clear guidance for fix

Business Impact

❌ Without Formal Verification

Testing Approach:
  • Manual test case design: Days
  • May miss edge cases: Likely
  • Bug found in production: Weeks/Months later
  • Regulatory penalty: $$$
  • Customer compensation: $$$
  • Reputational damage: Priceless

Risk: Hidden bugs can escape to production, causing regulatory violations and financial losses.

✅ With CodeLogician

Formal Verification:
  • Formalization: Minutes (automated)
  • Verification: Minutes (automated)
  • Bug discovery: Immediate
  • Fix validation: Minutes (re-verify)
  • Production incidents: Zero
  • Customer confidence: Maintained

Guarantee: Mathematical proof that critical properties hold for ALL possible inputs.


Step-by-Step: Using CodeLogician

Expand: How to Verify Your Trading System

Step 1: Model Your System in Python

Create a clean Python model with your business logic:

from dataclasses import dataclass
from enum import Enum
from typing import Optional, List
 
# Define your types
class TradingMode(Enum):
    CONTINUOUS = "continuous"
    AUCTION_CALL = "auction_call"
 
@dataclass
class State:
    time: int
    mode: TradingMode
    # ... other fields
 
# Define state transitions
def process_tick(state: State) -> State:
    # Your business logic here
    pass
 
# Define verification property
def conflict_reachable(messages: List, state: State) -> bool:
    """Check if bad state is reachable."""
    final_state = run(state, messages)
    return bad_condition_holds(final_state)

Step 2: Initialize CodeLogician

from code_logician import create_thread, init_state
 
# Create a verification thread
thread_id = create_thread()
 
# Load your Python code
with open("your_model.py", "r") as f:
    src_code = f.read()
 
# Initialize formalization
init_state(thread_id, src_code=src_code, src_lang="python")

Step 3: Automated Formalization

from code_logician import agent_formalizer
 
# Run automatic Python → IML transformation
result = agent_formalizer(thread_id, no_refactor=False)
 
# Check formalization status
if result["status"] == "TRANSPARENT":
    print("✅ Model fully formalized")
else:
    print("⚠️ Manual review needed")

Step 4: Generate Verification Goals

from code_logician import gen_vgs
 
# Describe what you want to verify
gen_vgs(
    thread_id,
    description="""
    Verify that the critical business property holds:
 
    For all valid initial states and message sequences,
    the conflict_reachable function should return false.
 
    Specifically:
    1. Check that valid states exist (VG1)
    2. Prove that conflict is impossible (VG2)
    """
)

Step 5: Analyze Results

CodeLogician will generate and check verification goals:

Case 1: Property Holds ✅

VG2: PROVED
∀ msgs, s: is_valid_state s ⟹ ¬(conflict_reachable msgs s)

No counterexample exists. Property is guaranteed.

Case 2: Bug Found ❌

VG2: REFUTED
Counterexample:
  msgs = [Tick, Tick]
  s = { time: 2699, mode: Auction {...}, ... }

This scenario violates the property!

Step 6: Fix and Re-verify

If a bug is found:

  1. Analyze the counterexample - Understand the scenario
  2. Identify root cause - Why does the bug occur?
  3. Implement fix - Update your Python code
  4. Re-run verification - Confirm fix with mathematical proof
# Update your model with the fix
# Then re-verify
init_state(thread_id, src_code=fixed_code, src_lang="python")
agent_formalizer(thread_id)
gen_vgs(thread_id, description="Same VG - verify fix works")
 
# Expected: ✅ PROVED (no counterexample)

Key Insights

Iterative Process:

Verification often finds bugs. That's the point! Fix them iteratively with concrete counterexamples guiding you.

Mathematical Certainty:

When verification succeeds, you have proof—not just confidence—that your property holds for ALL inputs.

CI/CD Integration:

Add verification to your pipeline to catch regressions automatically before they reach production.


Resources

Download the Example


Key Takeaways

Real Bug Discovery

Found actual regulatory compliance violation that would escape traditional testing.

Automated Analysis

No manual test writing—exhaustive analysis of all possible executions.

Concrete Scenario

Exact counterexample with initial state values and message sequence.

Clear Fix

Identified exact problem and provided corrected implementation.

Feature Interaction

Bug emerged from interaction of two independently correct features.

Financial Domain

High-stakes compliance scenario with real regulatory consequences.

Verify your critical trading systems

Discover bugs before they reach production. Get mathematical proof of correctness.