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.
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.
Two public value types define the Python API:
Contract: immutable domain value object and human-readable representation.ContractUUID: a subclass of Python'suuid.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.
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.-
Development checkout:
python -m pip install -e '.[dev]'After the package is published:
pip install opencontractidstrike 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"),
)from uuid import UUID
cid = contract.to_uuid()
assert isinstance(cid, UUID)
print(cid)
# standard UUID text, UUID version 8ContractUUID 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.uuidfrom ocid import Contract
restored = Contract.from_uuid(cid)
assert restored == contractAccepted 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()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.
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'Market decorators normalize exchange conventions before encoding:
Contract.equity("700", market="HK").symbol
# '00700'
Contract.equity("1", market="CN").symbol
# '000001'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.
Encode:
ocid encode \
--market US \
--asset OPTION \
--symbol INTC \
--expiry 2026-08-21 \
--right CALL \
--strike 150Decode:
ocid decode <uuid>Parse OSI and emit OCID:
ocid osi 'INTC 260821C00150000'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.
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.
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
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.
- Canonical symbol: at most 10 characters from
A-Z0-9.-. - Option strike input:
Decimalor numericstr; 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-01with 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.
Apache-2.0. See LICENSE.