feat: done vibing
This commit is contained in:
27 files changed
+1922
-13
No files matched your search
@@ -0,0 +1,75 @@
|
||||
// Package fetch defines the pluggable data-source interface and its
|
||||
// implementations (yfinance-equivalent prices, Finnhub, RSS news).
|
||||
package fetch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ytshih/stonks/internal/config"
|
||||
)
|
||||
|
||||
// Quote is a single point-in-time price observation for one symbol.
|
||||
type Quote struct {
|
||||
Symbol string // ticker as passed to the fetcher, e.g. "AAPL", "BTC-USD", "GC=F"
|
||||
Label string // human-friendly name, e.g. "Apple Inc."
|
||||
Price float64
|
||||
ChangePercent float64 // percent change over the source's default window (usually 1 day)
|
||||
Currency string
|
||||
Source string // which fetcher produced this, e.g. "yfinance", "finnhub"
|
||||
}
|
||||
|
||||
// NewsItem is a single headline/article.
|
||||
type NewsItem struct {
|
||||
Title string
|
||||
Source string // publication or fetcher name
|
||||
URL string
|
||||
Published time.Time
|
||||
Summary string
|
||||
}
|
||||
|
||||
// Result is what one fetcher call returns. The orchestrator merges Results
|
||||
// from every configured fetcher before handing them to the analyzer.
|
||||
type Result struct {
|
||||
Quotes []Quote
|
||||
News []NewsItem
|
||||
}
|
||||
|
||||
// Merge appends other's quotes and news onto r.
|
||||
func (r *Result) Merge(other Result) {
|
||||
r.Quotes = append(r.Quotes, other.Quotes...)
|
||||
r.News = append(r.News, other.News...)
|
||||
}
|
||||
|
||||
// Fetcher fetches market data and/or news for the given targets.
|
||||
// Implementations should return a partial Result plus an error when only
|
||||
// some data could be retrieved, rather than nothing at all — the caller
|
||||
// decides whether a partial result is still worth reporting.
|
||||
type Fetcher interface {
|
||||
Name() string
|
||||
Fetch(ctx context.Context, targets config.Targets) (Result, error)
|
||||
}
|
||||
|
||||
// Constructor builds a Fetcher from its PluginConfig (name + params).
|
||||
type Constructor func(cfg config.PluginConfig) (Fetcher, error)
|
||||
|
||||
var registry = map[string]Constructor{}
|
||||
|
||||
// Register makes a fetcher constructor available under name. Called from
|
||||
// each fetcher implementation's init().
|
||||
func Register(name string, ctor Constructor) {
|
||||
if _, exists := registry[name]; exists {
|
||||
panic(fmt.Sprintf("fetch: constructor already registered for %q", name))
|
||||
}
|
||||
registry[name] = ctor
|
||||
}
|
||||
|
||||
// Build looks up the constructor for cfg.Name and invokes it.
|
||||
func Build(cfg config.PluginConfig) (Fetcher, error) {
|
||||
ctor, ok := registry[cfg.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("fetch: no fetcher registered for %q", cfg.Name)
|
||||
}
|
||||
return ctor(cfg)
|
||||
}
|
||||
Reference in new issue
Block a user