You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.8 KiB
Python

"""Issuer Sync: populate issuer_master / symbol_master from Oracle company info."""
from __future__ import annotations
import argparse
import asyncio
import datetime as dt
import uuid
from sqlalchemy.dialects.postgresql import insert
from libs.common.config import get_settings
from libs.common.ids import issuer_id_from_cik, new_job_run_id, symbol_id_from_ticker
from libs.common.logging import bind_job_run_id, configure_logging, get_logger
from libs.db.models import IssuerMaster, JobRun, SymbolMaster
from libs.db.session import get_session
from libs.oracle_client.client import make_oracle_client
from libs.oracle_client.financial import FinancialService
logger = get_logger(__name__)
async def run_issuer_sync(run_id: str) -> dict[str, int]:
settings = get_settings()
symbols = settings.get_symbols()
stats = {"seen": 0, "written": 0, "errors": 0}
async with make_oracle_client() as client:
svc = FinancialService(client)
async with get_session() as session:
job = JobRun(
job_run_id=uuid.UUID(run_id),
job_name="issuer_sync",
source_name="oracle",
run_date=dt.date.today(),
status="running",
)
session.add(job)
await session.flush()
for ticker in symbols:
stats["seen"] += 1
try:
info = await svc.get_company_info(ticker)
cik = info.cik
issuer_id_str = issuer_id_from_cik(cik) if cik else f"ISSUER::{ticker}"
symbol_id_str = symbol_id_from_ticker(ticker, info.exchange or "US")
# Upsert issuer
issuer_stmt = (
insert(IssuerMaster)
.values(
issuer_id=issuer_id_str,
cik=cik,
ticker=ticker,
issuer_name=info.name or ticker,
exchange=info.exchange,
country_code=info.country,
is_active=True,
created_at_utc=dt.datetime.now(tz=dt.UTC),
updated_at_utc=dt.datetime.now(tz=dt.UTC),
)
.on_conflict_do_update(
index_elements=["issuer_id"],
set_={
"ticker": ticker,
"issuer_name": info.name or ticker,
"exchange": info.exchange,
"updated_at_utc": dt.datetime.now(tz=dt.UTC),
},
)
)
await session.execute(issuer_stmt)
# Upsert symbol
symbol_stmt = (
insert(SymbolMaster)
.values(
symbol_id=symbol_id_str,
issuer_id=issuer_id_str,
ticker=ticker,
venue=info.exchange or "US",
asset_type="common_stock",
currency="USD",
is_primary=True,
created_at_utc=dt.datetime.now(tz=dt.UTC),
updated_at_utc=dt.datetime.now(tz=dt.UTC),
)
.on_conflict_do_update(
index_elements=["symbol_id"],
set_={
"ticker": ticker,
"updated_at_utc": dt.datetime.now(tz=dt.UTC),
},
)
)
await session.execute(symbol_stmt)
stats["written"] += 1
logger.info("issuer_synced", ticker=ticker, issuer_id=issuer_id_str)
except Exception as exc:
logger.error("issuer_sync_error", ticker=ticker, error=str(exc))
stats["errors"] += 1
job.status = "succeeded" if stats["errors"] == 0 else "partial"
job.finished_at_utc = dt.datetime.now(tz=dt.UTC)
job.records_seen = stats["seen"]
job.records_written = stats["written"]
job.error_count = stats["errors"]
logger.info("issuer_sync_done", **stats)
return stats
def main() -> None:
parser = argparse.ArgumentParser(description="Issuer Sync")
parser.add_argument("--run-id", default=new_job_run_id())
args = parser.parse_args()
settings = get_settings()
configure_logging(settings.log_level)
bind_job_run_id(args.run_id)
asyncio.run(run_issuer_sync(args.run_id))
if __name__ == "__main__":
main()