Skip to content

Repository files navigation

OpenContractID (OCID)

OpenContractID is an open specification and Python reference implementation for deterministic, reversible, UUIDv8-based identifiers for financial instruments and contracts.

OCID treats the identifier itself as the stable identity. A broker ID, database sequence, FIGI, ISIN, Yahoo symbol, IBKR conId, or other provider identifier is an alias or metadata rather than the source of truth.

Current status

This repository is the unreleased initial implementation. The package version remains 0.1.0 until the first publication. The protocol is explicitly versioned by an OCID schema nibble so future incompatible layouts can coexist.

Design

Two public value types define the Python API:

  • Contract: immutable domain value object and human-readable representation.
  • ContractUUID: a subclass of Python's uuid.UUID, carrying the deterministic OCID identity.

Contract is not a persistence entity. No contracts table is required to recover the core identity fields.

Contract
   │ encode
   ▼
ContractUUID (uuid.UUID subclass)
   │ decode
   ▼
Contract

For options, print(), str() and repr() use OCC/OSI 21-character option symbology. ContractUUID deliberately retains normal UUID string behavior.

Schema 1 payload

OCID uses UUIDv8 as a 128-bit container. UUID version and variant consume 6 fixed bits. The remaining 122 payload bits are:

Field Bits Schema 1 meaning
schema 4 OCID schema version (1)
market 8 canonical market namespace
asset_type 4 equity / ETF / option / ...
symbol 54 reversible symbol code, max 10 canonical chars
expiry 16 days since 2000-01-01; zero means none
right 2 none / call / put
strike 30 fixed-point strike * 1000
reserved 4 zero in Schema 1

Canonical symbol alphabet:

ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-

Install

Development checkout:

python -m pip install -e '.[dev]'

After the package is published:

pip install opencontractid

Python API

Create an option

strike accepts either Decimal or a numeric string. Floating-point strikes are intentionally rejected.

from decimal import Decimal

from ocid import Contract

contract = Contract.option(
    "INTC",
    market="US",
    expiry="2026-08-21",
    right="C",
    strike="150",
)

print(contract)
# INTC  260821C00150000

assert contract.strike == Decimal("150")

Equivalent construction with Decimal:

contract = Contract.option(
    "INTC",
    expiry="2026-08-21",
    right="CALL",
    strike=Decimal("150.125"),
)

Convert to UUID

from uuid import UUID

cid = contract.to_uuid()

assert isinstance(cid, UUID)
print(cid)
# standard UUID text, UUID version 8

ContractUUID extends Python's standard uuid.UUID, so it can generally be passed directly to PostgreSQL drivers, SQLAlchemy UUID columns, Pydantic UUID fields and APIs expecting a UUID.

Aliases are provided where external SDK conventions make them convenient:

contract.to_uuid()
contract.toUUID()
contract.to_id()
contract.toID()
contract.id
contract.uuid

Decode without a registry or database

from ocid import Contract

restored = Contract.from_uuid(cid)
assert restored == contract

Accepted OCID inputs include ContractUUID, uuid.UUID, UUID string, 16-byte UUID bytes and integer UUID values through ContractUUID.parse().

Contract.from_uuid(cid)
Contract.fromUUID(cid)
Contract.from_id(str(cid))
Contract.fromID(str(cid))

cid.to_contract()
cid.toContract()

OCC / OSI

Options render as the OCC/OSI 21-character format:

contract = Contract.from_osi("INTC  260821C00150000")

str(contract)
# 'INTC  260821C00150000'

repr(contract)
# 'INTC  260821C00150000'

contract.to_osi()
# 'INTC  260821C00150000'

OSI conversion is a human/exchange representation. OCID remains the identity. str(contract.id) always remains standard UUID text.

Equities

from ocid import Contract

intel = Contract.equity("intc", market="US")

str(intel)
# 'INTC'

intel.id
# ContractUUID(...)

US share/class separators are normalized into the OCID canonical form:

Contract.equity("BRK-B").symbol
# 'BRK.B'

Numeric markets

Market decorators normalize exchange conventions before encoding:

Contract.equity("700", market="HK").symbol
# '00700'

Contract.equity("1", market="CN").symbol
# '000001'

Market decorators

The UUID layout is global. Markets normally customize only canonicalization and validation.

from ocid import Market, contract_market

@contract_market(Market.US)
class USMarketRules:
    @staticmethod
    def normalize_symbol(symbol: str) -> str:
        return symbol.strip().upper().replace("-", ".")

Do not create a separate UUID codec for every exchange. A market decorator should adapt its symbology into the global canonical contract model.

Built-in namespaces currently include US, HK, CN, JP, GB, DE, FR, NL, CH, CA, AU, SG, GLOBAL, FX and CRYPTO.

CLI

Encode:

ocid encode \
  --market US \
  --asset OPTION \
  --symbol INTC \
  --expiry 2026-08-21 \
  --right CALL \
  --strike 150

Decode:

ocid decode <uuid>

Parse OSI and emit OCID:

ocid osi 'INTC  260821C00150000'

Provider identity

Keep provider identifiers outside the core identity:

OCID / ContractUUID   deterministic internal identity
FIGI / ISIN           external industry identity
IBKR conId            broker identity
Yahoo/Futu symbol     provider alias
exchange              metadata / venue

A persistence system may store queryable metadata keyed by OCID, but the metadata record does not own the identity.

Agent skills

The repository includes skills under skills/:

  • opencontractid-python-api/SKILL.md: how an agent should consume OCID from Python applications.
  • opencontractid-development/SKILL.md: how an agent should safely modify the implementation and protocol.

The Python API skill is deliberately usage-oriented: construction, conversion, parsing, persistence boundaries, safe strike handling and provider integration.

Repository layout

src/ocid/
  model.py          Contract + enums
  uuid8.py          ContractUUID + UUIDv8 free-bit mapping
  codec.py          Schema 1 packing and validation
  symbol_codec.py   reversible symbol encoding
  registry.py       decorator-based market registration
  markets/          market canonicalization rules
  cli.py            command-line interface

docs/SPEC.md        protocol specification
docs/ARCHITECTURE.md domain and integration architecture
skills/              Python API and development agent skills
tests/               behavior and protocol tests
AGENTS.md            coding-agent repository rules

Development and release

python -m pip install -e '.[dev]'
pytest
ruff check .
mypy src/ocid
python -m build
python -m twine check dist/*

The package is configured for PyPI as opencontractid and imports as ocid. The first production publication should occur only after the Schema 1 golden vectors are intentionally frozen.

Intentional Schema 1 constraints

  • Canonical symbol: at most 10 characters from A-Z0-9.-.
  • Option strike input: Decimal or numeric str; no float.
  • Strike precision: at most 0.001.
  • OCID encoded strike maximum: (2^30 - 1) / 1000.
  • OCC/OSI rendering additionally requires a root that fits 6 characters and an 8-digit scaled strike.
  • Expiry: uint16 day offset from 2000-01-01 with zero reserved for no expiry.
  • Corporate-action renames produce a new symbol-derived identity in Schema 1; metadata can link identities.
  • Complex adjusted options, exotic derivatives and exceptional long symbols are deferred.

License

Apache-2.0. See LICENSE.

About

Deterministic and reversible UUIDv8 contract identifiers for equities, options and other financial instruments, with OCC/OSI support.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages