package instruments import ( "context" "fmt" "overnight-trading-bot/internal/domain" "overnight-trading-bot/internal/repository" "overnight-trading-bot/internal/tinvest" ) type Registry struct { repo repository.Repository gateway tinvest.Gateway } func NewRegistry(repo repository.Repository, gateway tinvest.Gateway) Registry { return Registry{repo: repo, gateway: gateway} } func (r Registry) SyncMetadata(ctx context.Context) error { instruments, err := r.repo.ListInstruments(ctx, true) if err != nil { return err } for _, instrument := range instruments { if !instrument.Enabled || instrument.Quarantine { continue } remote, err := r.gateway.GetInstrument(ctx, instrument.Ticker, instrument.ClassCode) if err != nil { if insertErr := r.repo.InsertRiskEvent(ctx, domain.RiskEvent{ Severity: domain.SeverityWarn, EventType: "instrument_metadata_sync_failed", InstrumentUID: instrument.InstrumentUID, Message: fmt.Sprintf("sync instrument metadata %s: %s", instrument.Ticker, err), ContextJSON: fmt.Sprintf(`{"ticker":%q,"class_code":%q}`, instrument.Ticker, instrument.ClassCode), }); insertErr != nil { return fmt.Errorf("record metadata sync failure %s: %w", instrument.Ticker, insertErr) } continue } remote.Enabled = instrument.Enabled && remote.Enabled remote.FundType = instrument.FundType remote.ExpectedCommissionBpsPerSide = instrument.ExpectedCommissionBpsPerSide remote.FreeOrderLimitPerDay = instrument.FreeOrderLimitPerDay remote.Quarantine = instrument.Quarantine remote.QuarantineReason = instrument.QuarantineReason remote.ExcludeReason = instrument.ExcludeReason if err := r.repo.ReplaceInstrument(ctx, instrument.InstrumentUID, remote); err != nil { return fmt.Errorf("replace synced instrument %s: %w", instrument.Ticker, err) } } return nil } func CheckInstrument(instrument domain.Instrument, status domain.TradingStatus) error { switch { case !instrument.Enabled: return fmt.Errorf("%s disabled", instrument.Ticker) case instrument.Quarantine: return fmt.Errorf("%s quarantined: %s", instrument.Ticker, instrument.QuarantineReason) case !instrument.MetadataValid(): return fmt.Errorf("%s invalid metadata", instrument.Ticker) case status != domain.TradingStatusNormal: return fmt.Errorf("%s trading status %s", instrument.Ticker, status) default: return nil } }