Metadata-Version: 2.5
Name: mantis-imf
Version: 0.2.0
Summary: Instrument Management Facility for MANTIS
Author-email: Louis Zhuang <yzhuang@outlook.com>
Requires-Python: >=3.14
Requires-Dist: asof-store[sql-postgres,sql-sqlite]
Requires-Dist: nautilus-trader>=2.0.0rc8
Requires-Dist: pydantic>=2.0
Requires-Dist: service-registrar>=0.2.1
Description-Content-Type: text/markdown

# MANTIS Instrument Management Facility

`imf` resolves instrument queries from configured source providers. Install
`mantis-ib` alongside `mantis-imf` to enable the default Interactive Brokers
source.
Configure the SQL backend once in the repository's `service-registrar.toml`:

```toml
["asof-store.sql"]
sql_uri = "sqlite:///ib-instruments.db"
```

IMF supplies each resolver store's table and value types; `sql_uri` comes from
this section by default. Pass `--sql-uri URI` to `imf resolve` to override it
for that invocation. For PostgreSQL, set `sql_uri` to the database connection
URI.

## Resolving instruments

```powershell
imf resolve AAPL USD STK
imf resolve --symbol AAPL --field currency=USD
imf resolve --country US --exchange NASDAQ --product-type STK
imf resolve --local-symbol "ESZ6"
imf resolve --field currency=USD --field "contract.exchange=SMART"
imf resolve --as-of 2026-10-29T12:00:00+00:00 --symbol AAPL
```

Query values match exactly, ignoring case. Use `%` as a wildcard for any
sequence of characters, for example `--symbol 'AAP%'` or `imf resolve '%apple%'`.
Repeat `--field NAME=VALUE` to require additional source fields. Dotted names
address nested fields. Positional values search across source-declared fields
not already named by keyed conditions. Every query term must match. Results
are pretty-printed JSON with source payloads, match evidence, resolved facts,
provenance, conflicts, and one projection result per configured target.

The Nautilus target currently supports `STK`, `ETF`, `BOND`, and `WAR` records
as `Equity`; `CASH` as `CurrencyPair`; `IND` as `IndexInstrument`; and `CMDTY`
as `Commodity`.
`CFD` records are exported as `Cfd` with asset class `EQUITY` by default.
`FUT` records are exported as `FuturesContract`; when missing, activation and
expiration timestamps default to `0`, asset class to `COMMODITY`, and
multiplier and lot size to `1`.
`OPT` records are exported as `OptionContract`; missing option kind, strike,
activation and expiration timestamps default to call, `1`, `0`, and `0`, while
asset class defaults to `EQUITY` and multiplier and lot size to `100`.
Equity defaults are `USD`, `IB`, price precision `2`, price increment `0.01`,
and lot size `100`. CASH defaults are price precision `5`, price increment
`0.00001`, size precision `0`, and size increment `1`; base and quote currencies
come from IB's `symbol` and `currency` fields. IND and CMDTY defaults are `USD`,
`IB`, price precision `2`, price increment `0.01`, size precision `0`, and size
increment `1`. All native outputs use event and initialization timestamps of
`0`.
All missing source fields are listed in `info.mantis.missing_fields` in the
instrument output; IMF does not emit warnings for those fields.

## Resolver API

The resolver combines source-owned observations and projects each instrument to
each configured target. Each source scraper writes directly to its own
`asof-store`; the resolver stores reviewed source links separately.

```python
from mantis_imf import (
    IBContractTarget,
    InstrumentQuery,
    KeyedTerm,
    NautilusInstrumentTarget,
    PositionalTerm,
    open_identity_link_store,
    resolve_query,
)
from mantis_ib.scraper import open_ib_instrument_source

source = open_ib_instrument_source(table_name="ib_instruments")
links = open_identity_link_store(table_name="instrument_links")
try:
    query = InstrumentQuery(
        terms=(
            KeyedTerm(field="symbol", value="AAPL"),
            KeyedTerm(field="currency", value="USD"),
            PositionalTerm(value="NASDAQ"),
        )
    )
    results = resolve_query(
        query,
        (source,),
        (NautilusInstrumentTarget(), IBContractTarget()),
        links=links,
    )
finally:
    source.close()
    links.close()
```

Every term must match. Positional terms search the source adapter's declared
fields, excluding fields already named by keyed terms. The adapter maps keyed
field aliases (for IB, `exchange` maps to `exchangeId`) and raises an explicit
error for unsupported fields. Results include each candidate's original source
payload, evidence showing the query term, source field, and matched value, and
the aggregate facts, provenance, and conflicts used for projection.

Cross-source records are joined only through explicit links written with
`put_instrument_link`. Unlinked keys stay source-scoped. Conflicting facts return
a conflict result without projecting that instrument. Nautilus results contain
native instrument objects; the IB target currently reports unsupported.

`imf resolve` uses `[instrument-resolver]` from `service-registrar.toml`. With
no resolver section, it defaults to source `ib`, target `nautilus`, and separate
`ib_instruments` and `imf_instrument_links` tables. Configure a larger matrix
and any non-default source stores explicitly:

```toml
[instrument-resolver]
sources = ["ib"]
targets = ["nautilus", "ib"]

[instrument-resolver.source_stores.ib]
table_name = "ib_instruments"

[instrument-resolver.registry_store]
table_name = "imf_instrument_links"
```

Run `imf resolve AAPL --as-of 2026-10-30T12:00:00+00:00` to emit pretty-printed
JSON with one result per matched instrument and target. At least one positional
`VALUE` or keyed field condition is required. Each source store and the registry must use a unique
backend/table binding. `--source` and `--target` repeat to override the configured
lists for one run.

Use positional terms and repeatable field conditions to search each configured
source before resolution:

```console
imf resolve AAPL NASDAQ --field currency=USD
```
