Algorithmic Trading Rules Analysis

Financial ServicesFormal VerificationPython to IML

Overview

In this example, we analyze a complex algorithmic trading system with hundreds of business rules governing when and how trades can execute. We use Imandra CodeLogician to detect redundant rules, verify critical properties, and gain complete confidence in the system's behavior.

Why This Matters

Traditional testing cannot prove rules are redundant or correct across all possible inputs. Formal verification provides mathematical certainty—not just 99.9% confidence, but 100%.


Background

The Trading System

This example models an algorithmic trading execution system with several key components:

Algo Types: Different execution strategies

  • BENCHMARK_TIME - Execute over time benchmarked to market
  • BENCHMARK_VOLUME - Execute benchmarked to market volume
  • BENCHMARK_CLOSE - Execute with focus on closing price
  • BENCHMARK_CLOSE_ONLY - Execute only during market close
  • LIQUIDITY_SEEK_PASSIVE - Seek liquidity passively
  • LIQUIDITY_SEEK_AGGRESSIVE - Seek liquidity aggressively

Trading Phases: When trading can occur

  • CONTINUOUS - Normal market hours
  • CLOSE - Market closing auction

Liquidity Destinations: Where orders are sent

  • Principal destinations (1, 2, Close)
  • Primary destinations (1, 2, Close)
  • Independent destinations (1, 2, Close)

The Python Model

The team built a comprehensive Python model with enums, dataclasses, and validation functions:

from enum import Enum
from dataclasses import dataclass
 
class AlgoType(Enum):
    BENCHMARK_TIME = 'benchmark time'
    BENCHMARK_VOLUME = 'benchmark volume'
    BENCHMARK_CLOSE = 'benchmark close'
    BENCHMARK_CLOSE_ONLY = 'benchmark close - close only'
    LIQUIDITY_SEEK_PASSIVE = 'liquidity seek - passive'
    LIQUIDITY_SEEK_AGGRESSIVE = 'liquidity seek - aggressive'
 
class TradingPhase(Enum):
    CONTINUOUS = 'Continuous'
    CLOSE = 'Close'
 
class ClientID(Enum):
    ABC = 'abc'
    DEF = 'def'
    # ... more clients
    XYZ = 'xyz'
 
def is_trading_phase_eligible_for_algo_type(
    algo_type: AlgoType,
    trading_phase: TradingPhase
) -> bool:
    """Check if a trading phase is eligible for an algo type."""
    eligibility_table = {
        AlgoType.BENCHMARK_TIME: {
            TradingPhase.CONTINUOUS: True,
            TradingPhase.CLOSE: True
        },
        AlgoType.BENCHMARK_CLOSE_ONLY: {
            TradingPhase.CONTINUOUS: False,  # Only CLOSE allowed!
            TradingPhase.CLOSE: True
        },
        # ... other algo types
    }
    return eligibility_table[algo_type][trading_phase]

The Problem

As the trading system evolved, client-specific exceptions were added to handle special cases:

def is_trading_phase_eligible_for_client_and_algo(
    client_id: ClientID,
    algo_type: AlgoType,
    trading_phase: TradingPhase
) -> bool:
    """Check eligibility for specific client and algo combination."""
 
    # ⚠️ Special rule for client XYZ
    if client_id == ClientID.XYZ and algo_type == AlgoType.BENCHMARK_CLOSE_ONLY:
        # Client XYZ can only trade in CLOSE phase with Benchmark Close Only
        return trading_phase == TradingPhase.CLOSE
 
    # For all other cases, use the general rule
    return is_trading_phase_eligible_for_algo_type(algo_type, trading_phase)

The Question

Is this special case actually doing anything?

Looking at the base rule, BENCHMARK_CLOSE_ONLY already restricts all clients to the CLOSE phase only. The XYZ rule might be redundant!

The Testing Problem

Traditional testing can't definitively prove redundancy. You'd need to test:

  • All client IDs (9 values)
  • All algo types (6 values)
  • All trading phases (2 values)
  • = 108 test combinations

But even then, you're only testing specific cases—not proving the property holds universally.


The Solution

Formal Verification with CodeLogician provides mathematical proof, not just empirical testing.

The Workflow

graph TB A[Python Code] --> B[CodeLogician Formalizer] --> C[IML Model] C --> D[Verification Goals] --> E[ImandraX Prover] E --> F[Mathematical Proof ✓] E -.->|Counterexamples| C style E fill:#0d9488,stroke:#0f766e,color:#fff style F fill:#059669,stroke:#047857,color:#fff linkStyle 5 stroke:#991b1b,stroke-width:2px

Step 1: Write a verification function in Python:

def verify_xyz_rule_is_redundant(
    client_id: ClientID,
    algo_type: AlgoType,
    trading_phase: TradingPhase
) -> bool:
    """Verify the special case produces identical results to the general rule."""
    if client_id == ClientID.XYZ and algo_type == AlgoType.BENCHMARK_CLOSE_ONLY:
        special_case_result = (trading_phase == TradingPhase.CLOSE)
        general_rule_result = is_trading_phase_eligible_for_algo_type(
            algo_type,
            trading_phase
        )
        return special_case_result == general_rule_result
    return True

Step 2: Use CodeLogician to formalize to IML:

type algo_type =
  | Benchmark_time
  | Benchmark_volume
  | Benchmark_close
  | Benchmark_close_only
  | Liquidity_seek_passive
  | Liquidity_seek_aggressive
 
type trading_phase = Continuous | Close
type client_id = ABC | DEF | GHI | JLK | LMN | OPQ | RST | UVW | XYZ
 
let verify_xyz_rule_is_redundant
    (client_id : client_id)
    (algo_type : algo_type)
    (trading_phase : trading_phase) : bool =
  if client_id = XYZ && algo_type = Benchmark_close_only then
    let special_case_result = (trading_phase = Close) in
    let general_rule_result =
      is_trading_phase_eligible_for_algo_type algo_type trading_phase
    in
    special_case_result = general_rule_result
  else
    true

Step 3: Generate and prove verification goals:

✓ PROVED

∀ client_id, algo_type, trading_phase:
  verify_xyz_rule_is_redundant(client_id, algo_type, trading_phase) = true

Examples in Action

Detecting Redundant Rules

We formally verified that the special case rule for client XYZ with BENCHMARK_CLOSE_ONLY is completely redundant.

Verification Results

Verification Goal 1: PROVED

∀ client_id, algo_type, trading_phase: verify_xyz_rule_is_redundant(...) = true

The special case result always equals the general rule result.

Verification Goal 2: PROVED

∀ trading_phase: eligible_for_client_and_algo(XYZ, BENCHMARK_CLOSE_ONLY, tp) = eligible_for_algo_type(BENCHMARK_CLOSE_ONLY, tp)

The two functions are mathematically equivalent for XYZ + BENCHMARK_CLOSE_ONLY.

Truth Table Comparison

ClientAlgo TypeTrading PhaseSpecial CaseGeneral RuleMatch?
XYZBENCHMARK_CLOSE_ONLYCONTINUOUSFalseFalse
XYZBENCHMARK_CLOSE_ONLYCLOSETrueTrue
ABCBENCHMARK_CLOSE_ONLYCONTINUOUSFalseFalse
ABCBENCHMARK_CLOSE_ONLYCLOSETrueTrue

Recommendation

The special case can be safely removed. The simplified function would be:

def is_trading_phase_eligible_for_client_and_algo(
  client_id: ClientID,
  algo_type: AlgoType,
  trading_phase: TradingPhase

) -> bool: # The general rule handles all cases correctly
return is_trading_phase_eligible_for_algo_type(
algo_type,
trading_phase
)

Step-by-Step Tutorial

Expand: How to Use CodeLogician for Redundancy Detection

Step 1: Identify Suspicious Rules

Look for patterns where specific rules might duplicate base logic:

# Specific rule
if client_id == ClientID.XYZ and algo_type == AlgoType.BENCHMARK_CLOSE_ONLY:
    return trading_phase == TradingPhase.CLOSE
 
# Base rule
AlgoType.BENCHMARK_CLOSE_ONLY: {
    TradingPhase.CONTINUOUS: False,
    TradingPhase.CLOSE: True
}

Question: Does the specific rule add any behavior beyond the base rule?

Step 2: Create a Verification Function

Add a function that compares the specific rule to the general rule:

def verify_xyz_rule_is_redundant(
    client_id: ClientID,
    algo_type: AlgoType,
    trading_phase: TradingPhase
) -> bool:
    """
    Verify that the special case produces identical results
    to the general rule.
    """
    if client_id == ClientID.XYZ and algo_type == AlgoType.BENCHMARK_CLOSE_ONLY:
        special_case_result = (trading_phase == TradingPhase.CLOSE)
        general_rule_result = is_trading_phase_eligible_for_algo_type(
            algo_type,
            trading_phase
        )
        return special_case_result == general_rule_result
    return True  # Not testing this case

Step 3: Use CodeLogician to Formalize

from code_logician import create_thread, init_state, agent_formalizer
 
# Create a thread
thread_id = create_thread()
 
# Initialize with your Python code
init_state(thread_id, src_code=your_python_code, src_lang="python")
 
# Run automated formalization
agent_formalizer(thread_id, no_refactor=False)

CodeLogician transforms your Python into IML (Imandra Modeling Language):

type client_id = ABC | DEF | GHI | JLK | LMN | OPQ | RST | UVW | XYZ
type algo_type = Benchmark_time | Benchmark_volume | ...
type trading_phase = Continuous | Close
 
let verify_xyz_rule_is_redundant
    (client_id : client_id)
    (algo_type : algo_type)
    (trading_phase : trading_phase) : bool =
  if client_id = XYZ && algo_type = Benchmark_close_only then
    let special_case_result = (trading_phase = Close) in
    let general_rule_result =
      is_trading_phase_eligible_for_algo_type algo_type trading_phase
    in
    special_case_result = general_rule_result
  else
    true

Step 4: Generate Verification Goals

from code_logician import gen_vgs
 
gen_vgs(
    thread_id,
    description="""
    Prove that the special case rule for client XYZ with
    BENCHMARK_CLOSE_ONLY is redundant. Specifically:
    1. Verify that verify_xyz_rule_is_redundant always returns true
    2. Verify that the client-specific function produces identical
       results to the general function
    """
)

Step 5: Review Results

CodeLogician generates and proves verification goals:

✓ Verification Goal 1: PROVED

∀ client_id, algo_type, trading_phase:
    verify_xyz_rule_is_redundant(client_id, algo_type, trading_phase) = true

✓ Verification Goal 2: PROVED

∀ trading_phase:
    is_trading_phase_eligible_for_client_and_algo(XYZ, BENCHMARK_CLOSE_ONLY, trading_phase)
    =
    is_trading_phase_eligible_for_algo_type(BENCHMARK_CLOSE_ONLY, trading_phase)

Key Insights

Mathematical Certainty: We didn't just test a few cases—we proved for all possible values that the special case produces identical results to the general rule.

Why This Matters:

  1. Confidence: 100% certainty the rule is redundant
  2. Documentation: Formal proof serves as permanent documentation
  3. Refactoring Safety: Can remove the rule knowing behavior won't change
  4. No Test Gap: Traditional testing might miss edge cases

General Pattern for Detecting Redundancies

def verify_rule_is_redundant(inputs):
    """
    Check if specific_rule and general_rule produce
    the same result for specific conditions.
    """
    if specific_conditions_apply(inputs):
        specific_result = specific_rule(inputs)
        general_result = general_rule(inputs)
        return specific_result == general_result
    return True

Then verify: ∀ inputs: verify_rule_is_redundant(inputs) = true

When to Use This Technique

Good use cases:

  • Client-specific overrides that may duplicate base rules
  • Geographic/regulatory rules with complex interactions
  • Product-specific business logic
  • State machine transitions with redundant conditions
  • Access control rules with overlapping permissions

Not suitable for:

  • Performance optimizations (fast-path logic)
  • Caching layers
  • Logging/instrumentation code
  • I/O operations

Resources

Download the Notebook

Full Documentation

The analysis includes several detailed documents:

  • Redundant Rule Analysis: Complete formal verification report with truth tables
  • Region Decomposition Results: All 12 possible behaviors documented
  • Property Verification: Proof that Close destinations only map to Close phase
  • Tutorial: Step-by-step guide for applying this technique to your code

Learn More


Key Takeaways

Redundancy Detection

Formal verification can prove rules are redundant with 100% certainty—not just empirical observation from testing.

Complete Coverage

Region decomposition provides mathematical proof that you've analyzed all possible behaviors—no hidden edge cases.

Property Verification

Critical business constraints can be verified universally and permanently—not just for specific test cases.

Safe Refactoring

With formal proofs, you can refactor with complete confidence that behavior won't change.

Ready to try CodeLogician on your code?

Start formalizing and verifying your business rules today.