> ## Documentation Index
> Fetch the complete documentation index at: https://finance.qwedai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# QWED Finance guards for financial AI verification

> The 11 QWED Finance guards: Compliance, Calendar, Derivatives, Message, ISO, Query, Cross, Bond, FX, Risk, and Trading, with verified examples for v3.0.1.

QWED-Finance uses **Neurosymbolic AI** - combining neural (LLM) outputs with symbolic (math/logic) verification.

## 1. Compliance guard (Z3)

**Purpose:** Verify KYC/AML regulatory decisions using formal boolean logic.

```python theme={null}
from qwed_finance import ComplianceGuard

guard = ComplianceGuard()

# AML Threshold Check (BSA/FinCEN)
result = guard.verify_aml_flag(
    amount=15000,
    country_code="US",
    llm_flagged=True
)
# result.compliant = True ✅
# result.proof = "Z3: LLM decision matches regulatory requirement"
```

### Methods

| Method | Description |
| - | - |
| `verify_aml_flag(amount, country_code, llm_flagged)` | Check the CTR threshold (\$10,000) and high-risk countries |
| `verify_kyc_complete(has_id, has_address_proof, has_tax_id, llm_approved)` | Validate KYC document requirements |
| `verify_transaction_limit(amount, daily_limit, daily_total, llm_approved)` | Enforce daily limits |
| `verify_sanctions_check(entity_name, is_on_sanctions_list, llm_approved)` | Check an approval decision against a sanctions-list result |

<Note>
  Since v3.0.0, `verify_aml_flag` fails closed: country codes must be canonical ISO alpha-2 codes, and malformed, non-finite, or missing amounts are rejected instead of passing.
</Note>

### How Z3 works

```python theme={null}
# Z3 proves: IF amount >= 10000 THEN flag_required
from z3 import *
amount = Real('amount')
flag = Bool('flag')
solver = Solver()
solver.add(Implies(amount >= 10000, flag == True))
```

***

## 2. Calendar guard (day counts)

**Purpose:** Deterministic day count conventions for interest calculations, computed with exact `Decimal` arithmetic.

```python theme={null}
from qwed_finance import CalendarGuard, DayCountConvention
from datetime import date

guard = CalendarGuard()

# Verify 30/360 day count
result = guard.verify_day_count(
    start_date=date(2026, 1, 1),
    end_date=date(2026, 7, 1),
    llm_days=180,
    convention=DayCountConvention.THIRTY_360
)
# result.verified = True ✅
```

### Supported conventions

| Convention | Use Case |
| - | - |
| `ACTUAL_ACTUAL` | US Treasury bonds |
| `ACTUAL_360` | T-Bills, Commercial paper |
| `ACTUAL_365` | UK Gilts |
| `THIRTY_360` | Corporate bonds |
| `THIRTY_360_EU` | Eurobonds (30E/360) |

Other methods: `verify_day_count_fraction()`, `verify_accrued_interest()`, `verify_business_day()`, and `get_next_business_day()`.

***

## 3. Derivatives guard (Black-Scholes)

**Purpose:** Options pricing and margin verification using pure calculus.

```python theme={null}
from qwed_finance import DerivativesGuard, OptionType

guard = DerivativesGuard()

# Verify Black-Scholes call price
result = guard.verify_black_scholes(
    spot_price=100,
    strike_price=105,
    time_to_expiry=0.25,
    risk_free_rate=0.05,
    volatility=0.20,
    option_type=OptionType.CALL,
    llm_price="$2.48"
)
# result.verified = True ✅
# result.computed_price = "$2.48"
# result.greeks = {"delta": "0.3772", "gamma": "0.037988", "theta": "-0.0256", "vega": "0.1899", "rho": "0.0881"}
```

### Methods

| Method | Description |
| - | - |
| `verify_black_scholes()` | Options pricing with Greeks |
| `verify_delta()` | Delta calculation |
| `verify_initial_margin()` | Initial margin amount |
| `verify_margin_call()` | Margin call decision |
| `verify_put_call_parity()` | Arbitrage detection |

### Arbitrary-precision arithmetic

<Info>
  **Since the Decimal/mpmath migration:** `DerivativesGuard` uses `mpmath` (30 decimal places) for all transcendental functions — `log`, `exp`, `sqrt`, and `erf` — replacing IEEE-754 `math.*` calls. The standard normal CDF and PDF (`_norm_cdf`, `_norm_pdf`) are now exact to 30 dp, and `verify_margin_call` and `verify_put_call_parity` compare values in `Decimal` space.
</Info>

<Warning>
  **Breaking change — Greeks are now `str`, not `float`.** Each Greek is `Decimal.quantize()`'d and returned as a string to preserve precision across serialization boundaries. Cast explicitly if you need a numeric type:

  ```python theme={null}
  # Before
  greeks["delta"] * notional  # str: an int notional repeats the text ("0.3772" * 2 == "0.37720.3772"), a float raises TypeError

  # After
  from decimal import Decimal
  Decimal(greeks["delta"]) * Decimal(str(notional))  # "0.3772" → exact; str() avoids float noise
  ```
</Warning>

`mpmath` is now a runtime dependency. It was already pulled in transitively by `sympy`, so no extra install step is required.

***

## 4. Message guard (XML schema)

**Purpose:** Validate ISO 20022 and SWIFT messages before transmission.

```python theme={null}
from qwed_finance import MessageGuard, MessageType

guard = MessageGuard()

# Verify pacs.008 payment message
result = guard.verify_iso20022_xml(
    xml_string=pacs008_xml,
    msg_type=MessageType.PACS_008
)
# result.valid = True/False
# result.errors = ["Missing required element: GrpHdr"]
```

### Supported formats

| Format | Description |
| - | - |
| `MessageType.PACS_008` | Customer Credit Transfer |
| `MessageType.PACS_002` | Payment Status Report |
| `MessageType.CAMT_053` | Bank Statement |
| `MessageType.CAMT_054` | Debit/Credit Notification |
| `MessageType.PAIN_001` | Payment Initiation |
| `SwiftMtType.MT103` | SWIFT Single Customer Credit Transfer |
| `SwiftMtType.MT202` | SWIFT Bank Transfer |
| `SwiftMtType.MT940` | SWIFT Customer Statement |
| `SwiftMtType.MT950` | SWIFT Statement Message |

`MessageGuard` also provides `verify_iban()` and `verify_bic()`.

### SWIFT MT validation

```python theme={null}
from qwed_finance import SwiftMtType

# Validate MT103 fields
result = guard.verify_swift_mt(
    mt_string=mt103_message,
    mt_type=SwiftMtType.MT103
)
# Checks Field 20, 32A, 50K, 59, etc.
```

***

## 5. ISOGuard (JSON schema)

**Purpose:** Enforce ISO 20022 compliance for JSON-based Agentic Banking.

```python theme={null}
from qwed_finance import ISOGuard

guard = ISOGuard()

# Verify pacs.008 (JSON format)
result = guard.verify_payment_message(
    message={
        "MsgId": "1234AB",
        "CreDtTm": "2026-01-28T10:00:00",
        "NbOfTxs": 1,
        "TtlIntrBkSttlmAmt": {"amount": 100.50, "currency": "USD"}
    },
    msg_type="pacs.008"
)
# result.verified = True ✅
```

### Why JSON vs. XML?

While `MessageGuard` handles traditional XML SWIFT messages, `ISOGuard` enables **Modern Banking Agents** to speak the same standard using lightweight JSON.

***

## 6. Query guard (SQLGlot)

**Purpose:** Prevent SQL injection and unauthorized data access.

```python theme={null}
from qwed_finance import QueryGuard

guard = QueryGuard(allowed_tables={"transactions", "accounts"})

# Check read-only safety
result = guard.verify_readonly_safety(
    "DELETE FROM users WHERE id=1"
)
# result.safe = False ❌
# result.violations = ["Mutation detected: DELETE statement"]
```

### Methods

| Method | Description |
| - | - |
| `verify_readonly_safety()` | Block INSERT/UPDATE/DELETE/DROP |
| `verify_table_access()` | Allow-list tables |
| `verify_column_access()` | Block restricted (PII) columns |
| `verify_no_injection()` | Detect injection patterns |
| `sanitize_query()` | Return a sanitized query, or reject it |

### Why AST, not regex?

```sql theme={null}
-- A regex looking for a leading DELETE misses this:
WITH d AS (DELETE FROM users RETURNING *) SELECT * FROM d

-- The SQLGlot AST finds the mutation inside the CTE:
-- "Mutation detected in subquery: delete statement"
```

Queries that SQLGlot cannot parse fail closed with an `SQL parse error` violation.

<Warning>
  **Since v3.0.1** ([GHSA-q8r4-6gpp-5fx2](https://github.com/QWED-AI/qwed-finance/security/advisories/GHSA-q8r4-6gpp-5fx2)): queries that contain MySQL `/*! ... */` or MariaDB `/*M! ... */` executable comments are rejected. MySQL runs the content of these comments, but the parser drops it, so tables and columns inside them could bypass the allow-list and PII checks. This applies to `verify_readonly_safety`, `verify_table_access`, `verify_column_access`, `sanitize_query`, and `CrossGuard.verify_query_with_pii_protection`. Ordinary comments and optimizer hints (`/*+ ... */`) are unaffected.
</Warning>

***

## 7. Cross-guard (multi-layer)

**Purpose:** Combine multiple guards to verify every component.

```python theme={null}
from qwed_finance import CrossGuard

guard = CrossGuard()

# SWIFT + Sanctions in one call
result = guard.verify_swift_with_sanctions(
    mt_string=swift_message,
    sanctions_list=["ACME Corp", "Bad Bank LLC"]
)
# Validates SWIFT format AND screens every party field
# result.passed, result.violations, result.receipts
```

### Methods

| Method | Description |
| - | - |
| `verify_swift_with_sanctions(mt_string, sanctions_list)` | SWIFT MT validation plus sanctions screening of party fields |
| `verify_iso20022_with_rules(xml_string, business_rules)` | ISO 20022 validation plus business rules |
| `check_business_rules(xml_string, business_rules)` | Business rules only (`max_amount`, `min_amount`, `positive_amount`, `allowed_currencies`) |
| `verify_query_with_pii_protection(sql_query, allowed_tables, pii_columns)` | Query safety, table allow-list, and PII column checks |

```python theme={null}
result = guard.check_business_rules(
    pacs008_xml,
    {"max_amount": 1000000, "positive_amount": True, "allowed_currencies": ["USD", "EUR"]}
)
# result.passed = False ❌
# result.violations = ["Amount 2500000 exceeds max 1000000"]
```

`CrossGuard` is the only guard that issues `VerificationReceipt` objects directly (in `result.receipts`). See [Compliance and auditing](/finance/compliance).

<Warning>
  **Since v3.0.1** ([GHSA-mrrj-6m2q-jch9](https://github.com/QWED-AI/qwed-finance/security/advisories/GHSA-mrrj-6m2q-jch9)): business rules read `IntrBkSttlmAmt` values and `Ccy` attributes from the parsed XML tree (local names, any namespace), not from regex over raw text. Amounts with child nodes, exponents, or grouping separators fail closed as unparseable.

  **Since v3.0.1** ([GHSA-mv2c-jwm9-pfrq](https://github.com/QWED-AI/qwed-finance/security/advisories/GHSA-mv2c-jwm9-pfrq)): sanctions screening folds diacritics and look-alike letters and compares separator-free names, so `EV-IL CORP`, `E.V.I.L. CORP`, or `ÉVIL CORP` no longer clear an `EVIL CORP` entry. An empty or missing sanctions list fails closed as unscreened.
</Warning>

***

## 8. Bond guard (yield analytics)

**Purpose:** Verify fixed income calculations like Yield to Maturity (YTM) and duration using Newton-Raphson.

```python theme={null}
from qwed_finance import BondGuard

guard = BondGuard()

# Verify YTM calculation
result = guard.verify_ytm(
    face_value=1000,
    coupon_rate=0.05,
    price=950,
    years_to_maturity=10,
    llm_ytm="5.66%"
)
# result.verified = True ✅ (within the default 0.5% tolerance)
# result.computed_value = "5.6617%"
```

### Rate format rules

<Warning>
  **Since v2.1.0:** `_parse_rate()` no longer silently guesses whether an input is a percentage or decimal. The old heuristic (`val < 1 → decimal, else percentage`) has been removed.
</Warning>

| Input | Parsed Value | Rule |
| - | - | - |
| `"5.25%"` | `0.0525` | Explicit `%` → divide by 100 |
| `"0.0525"` | `0.0525` | No `%` → use as-is (decimal fraction) |
| `"1.5"` | `1.5` | No `%` → use as-is (150% for distressed debt) |

```python theme={null}
# ✅ Correct: use explicit % for percentage values
guard.verify_ytm(..., llm_ytm="5.25%")

# ✅ Correct: use decimal fraction directly
guard.verify_ytm(..., llm_ytm="0.0525")

# ⚠️ Breaking: "5.25" now means 525%, NOT 5.25%
# Use "5.25%" instead
```

<Note>
  The same explicit-`%` rule applies to `BondGuard._parse_rate()` and `FinanceVerifier.verify_irr()`, so a rate string means the same thing in both.
</Note>

### Exact arithmetic with Decimal

<Info>
  **Since the Decimal/mpmath migration:** `BondGuard` runs Newton-Raphson YTM solving and all duration, convexity, and accrued-interest math in `Decimal` with 50-digit precision (`getcontext().prec = 50`). Inputs are converted to `Decimal` at the boundary, eliminating IEEE-754 cancellation in long-dated bond cashflow sums.

  `tolerance_pct` is now stored as `Decimal`. Pass a `float` or `int` — the guard converts it via `Decimal(str(value))` to avoid float contamination:

  ```python theme={null}
  # Both are safe
  guard = BondGuard(tolerance_pct=0.5)
  guard = BondGuard(tolerance_pct="0.5")
  ```

  `verify_ytm`, `verify_duration`, and `verify_convexity` return `computed_value` and their `details` fields as quantized strings (e.g. `"5.6617%"`) instead of raw floats.

  Other methods: `verify_accrued_interest()` and `verify_dirty_price()`.
</Info>

## 9. FX guard (currency arbitration)

**Purpose:** Validate cross-currency conversions and detect arbitrage opportunities.

```python theme={null}
from qwed_finance import FXGuard

guard = FXGuard()

# Verify Spot Conversion
result = guard.verify_currency_conversion(
    amount=1000,
    rate=0.92,
    llm_converted="920.00",
    from_currency="USD",
    to_currency="EUR",
)
# result.verified = True ✅
# result.computed_value = "EUR 920.00"
```

Pass `llm_converted` as a plain number (`"920.00"`). A leading currency symbol (`$`, `€`, `£`, `¥`) is stripped, but currency codes such as `"EUR 920.00"` raise `decimal.InvalidOperation`.

### Methods

| Method | Description |
| - | - |
| `verify_currency_conversion()` | Spot conversion |
| `verify_cross_rate()` | Cross rate from two pairs |
| `verify_forward_rate()` | Forward rate from interest-rate parity |
| `verify_swap_points()` | Forward minus spot, in points |
| `verify_ndf_settlement()` | Non-deliverable forward settlement amount |
| `verify_triangular_arbitrage()` | Arbitrage existence decision |

***

## 10. Risk guard (portfolio metrics)

**Purpose:** Ensure risk metrics like Sharpe Ratio and VaR (Value at Risk) are mathematically consistent.

```python theme={null}
from qwed_finance import RiskGuard

guard = RiskGuard()

# Verify Sharpe Ratio
# (Return - RiskFree) / Volatility
result = guard.verify_sharpe_ratio(
    portfolio_return=0.12,
    risk_free_rate=0.03,
    portfolio_volatility=0.15,
    llm_sharpe="0.60"
)
# result.verified = True ✅
# result.computed_value = "0.6000"
```

### Exact arithmetic with Decimal

<Info>
  **Since the Decimal/mpmath migration:** every `RiskGuard` method computes in `Decimal`. `verify_var` and `verify_sortino_ratio` use `Decimal.sqrt()` instead of `math.sqrt()`, and `verify_beta` accumulates covariance and variance in `Decimal` to prevent catastrophic cancellation on large return histories.

  The `Z_SCORES` lookup table is `Decimal`-typed:

  | Method | Backing math |
  | - | - |
  | `verify_var()` | `Decimal.sqrt()` for time scaling |
  | `verify_beta()` | `Decimal` covariance accumulation |
  | `verify_sharpe_ratio()` | `Decimal` division |
  | `verify_sortino_ratio()` | `Decimal.sqrt()` on downside deviation |
  | `verify_max_drawdown()` | `Decimal` running peak/trough |
  | `verify_information_ratio()` | `Decimal` division |
  | `verify_expected_shortfall()` | `Decimal` tail-loss average |
</Info>

***

## 11. Trading guard (order parameters)

**Purpose:** Verify order payloads for prediction markets (tick size, price bounds, volume, contract type, side) before they reach an execution API.

```python theme={null}
from decimal import Decimal
from qwed_finance import TradingGuard, MarketRules

guard = TradingGuard()
guard.register_market("fed-cut-dec", MarketRules(tick_size=Decimal("0.01"), max_contracts=500))

result = guard.verify_order(
    market_id="fed-cut-dec",
    contract_type="binary",
    price="0.425",
    volume=100,
    side="buy",
)
# result.verified = False ❌
# result.risk = "TICK_SIZE_MISMATCH"
# result.message = "Price 0.425 is not a multiple of tick size 0.01"
```

Unknown markets fail closed (`risk = "UNKNOWN_MARKET"`). Prices are exact `Decimal` values; float prices, non-finite values, and boolean volumes are rejected. Use `verify_order_batch()` to check a list of orders.

***

<Tip>
  All 11 guards are available via `pip install qwed-finance`.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.