bvb

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 12 Imported by: 0

README

bvb-go

A small, read-only Go client for the Bucharest Stock Exchange (Bursa de Valori București), plus a gobacktest source.Source adapter that serves native BVB OHLCV candles.

BVB publishes no official developer API, but its own website is backed by a TradingView-compatible datafeed at https://wapi.bvb.ro/api that answers plain HTTP requests. bvb-go talks to that datafeed for candles, config, metadata and search, and scrapes the server-rendered market-list pages at https://www.bvb.ro to enumerate the instrument universe.

No authentication is required — the client sends a browser-like User-Agent and a bvb.ro Referer, nothing more. No browser or headless runtime is needed.

Install

go get github.com/florinel-chis/bvb-go

Usage

c := bvb.New()

// Symbol metadata
si, _ := c.SymbolInfo(ctx, "TLV")            // "BANCA TRANSILVANIA S.A.", BVB, has_intraday

// Daily OHLCV, split-adjusted, priced in RON
bars, _ := c.History(ctx, "TLV",
    time.Now().AddDate(-2, 0, 0), time.Now(),
    bvb.D1, true, "RON")

// The full share universe (ticker + ISIN + issuer)
shares, _ := c.Instruments(ctx, bvb.Shares)  // also Bonds, FundUnits, Warrants, Certificates

// Company details + valuation snapshot (P/E, P/BV, EPS, div yield, ownership, …)
f, _ := c.Fundamentals(ctx, "TLV")
As a gobacktest source
import bvbbs "github.com/florinel-chis/bvb-go/backtestsource"

src := bvbbs.New(c)                          // WithCurrency, WithAdjusted
data, _ := src.Fetch(ctx, "TLV", start, end, source.D1)

Data surface

What How
OHLCV candles History/api/history (daily back to ~1997, weekly, monthly, and 1/5/15/30/60-minute intraday)
Symbol metadata SymbolInfo/api/symbols
Symbol search Search/api/search (server-capped ~30; a lookup, not an enumeration)
Datafeed config Config/api/config
Instrument universe Instruments → market-list HTML (Shares/Bonds/FundUnits/Warrants/Certificates)
Company fundamentals + details Fundamentals → detail page (identity, Indicatori bursieri valuation ratios, issue info, ownership); current snapshot only — no multi-year statements

Resolutions map to the constants M1 M5 M15 M30 H1 D1 W1 Mo1.

Quirks handled for you

  • Daily is 1D, not D. The datafeed advertises TradingView-style "D" in symbol metadata, but /history rejects it with HTTP 500. Use bvb.D1.
  • /history requires from, to, ajust, countback and currencyCode on every call; History always supplies them and sizes countback to cover the requested span.
  • Indices are not part of Instruments (different page shape), but index candles are reachable through History/SymbolInfo by their datafeed symbol (e.g. BET).
  • Transient gating. Under bursty access the datafeed can briefly answer HTTP 401 "Authorization has been denied for this request."; it clears on its own. History/etc. surface this as an *APIError (whose message includes the response body) — space out large multi-symbol scans.
  • Unknown tickers come back as HTTP 200 with an empty body; SymbolInfo reports them as ErrUnknownSymbol rather than a blank struct.
  • Deep intraday. A single request carries a bounded number of bars; if a long intraday span can't reach start, History returns ErrHistoryTruncated instead of a silently shortened series — retry with a coarser resolution.

Terms

This client accesses BVB's own public backend and ships code, not data. Redistributing BVB price data is your responsibility under BVB's terms of use.

License

MIT — see LICENSE.

Documentation

Overview

Package bvb is a read-only client for the Bucharest Stock Exchange (Bursa de Valori București). It talks to two of the exchange's own public backends:

  • The TradingView UDF datafeed at https://wapi.bvb.ro/api, which serves the symbol config, per-symbol metadata, a (server-capped) symbol search, and historical OHLCV candles. These endpoints require no authentication token — a browser-like User-Agent and a bvb.ro Referer are sent defensively, nothing more.

  • The server-rendered market-list pages at https://www.bvb.ro, scraped to enumerate the full instrument universe (ticker + ISIN + name), because the datafeed search is capped and cannot list every symbol.

The client is read-only: BVB exposes no trading API. Redistributing BVB price data is the caller's responsibility under BVB's terms of use — this package ships code, not data.

Two quirks of the datafeed are handled for you: the daily candle resolution code is "1D" (bare "D" is rejected with HTTP 500), exposed here as the D1 constant; and the /history endpoint requires from, to, ajust, countback and currencyCode on every call, all supplied by History.

Index

Constants

This section is empty.

Variables

View Source
var ErrHistoryTruncated = errors.New("bvb: history window truncated")

ErrHistoryTruncated is wrapped when the requested span needs more bars than a single request can carry (maxCountback) and the delivered window does not reach back to from — so the series would be silently truncated. Match with errors.Is and retry with a coarser resolution or a later start. This only arises for long intraday spans; daily and coarser never hit the cap.

View Source
var ErrUnknownMarket = errors.New("bvb: unknown market")

ErrUnknownMarket is wrapped when Instruments is asked for a market that has no known list page. Match with errors.Is.

View Source
var ErrUnknownSymbol = errors.New("bvb: unknown symbol")

ErrUnknownSymbol is wrapped by SymbolInfo when the datafeed does not know a ticker. Match with errors.Is.

View Source
var ErrUnsupportedResolution = errors.New("bvb: unsupported resolution")

ErrUnsupportedResolution is wrapped when History is asked for a resolution outside the supported set. Match with errors.Is.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Status int    // HTTP status code
	URL    string // requested URL (no secrets are ever sent, so it is safe to surface)
	Body   string // truncated response body
}

APIError is a non-2xx HTTP response from a BVB backend.

func (*APIError) Error

func (e *APIError) Error() string

type Bar

type Bar struct {
	Time   time.Time
	Open   float64
	High   float64
	Low    float64
	Close  float64
	Volume int64
}

Bar is one OHLCV candle. Time is the bar's open time in UTC.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a read-only Bucharest Stock Exchange client. Create it with New; the zero value is not usable.

func New

func New(opts ...Option) *Client

New returns a Client pointed at BVB's public backends.

func (*Client) Config

func (c *Client) Config(ctx context.Context) (Config, error)

Config returns the datafeed configuration.

func (*Client) Fundamentals

func (c *Client) Fundamentals(ctx context.Context, ticker string) (Fundamentals, error)

Fundamentals fetches and parses the detail page for ticker. An unknown ticker (the page renders without an identity block) yields ErrUnknownSymbol.

func (*Client) History

func (c *Client) History(ctx context.Context, ticker string, from, to time.Time, res Resolution, adjusted bool, currency string) ([]Bar, error)

History fetches OHLCV candles for ticker between from and to at resolution res. When adjusted is true, prices are corrected for splits (ajust=1). currency selects the pricing currency (e.g. "RON"); empty means "RON".

The datafeed bounds the window by to and by a countback derived from the requested span, so the returned bars end at or before to and reach back to at least from; trim to an exact range if needed. A "no_data" response yields an empty slice and no error.

func (*Client) Instruments

func (c *Client) Instruments(ctx context.Context, market Market) ([]Instrument, error)

Instruments enumerates the instruments listed on a market's page. It is the reliable way to list the universe, since the datafeed search is capped.

func (*Client) Search

func (c *Client) Search(ctx context.Context, query string) ([]SearchResult, error)

Search queries the datafeed symbol search. The endpoint caps results (about 30) for broad queries, so it is a lookup helper, not a way to enumerate the universe — use Instruments for that.

func (*Client) ServerTime

func (c *Client) ServerTime(ctx context.Context) (time.Time, error)

ServerTime returns the datafeed's current time (GET /api/time).

func (*Client) SymbolInfo

func (c *Client) SymbolInfo(ctx context.Context, ticker string) (SymbolInfo, error)

SymbolInfo resolves one ticker to its datafeed metadata. An unknown ticker is reported as ErrUnknownSymbol: the datafeed answers HTTP 200 with an all-empty body rather than an error status, so SymbolInfo treats an empty ticker in the response as "not found".

type Config

type Config struct {
	SupportsSearch       bool         `json:"supports_search"`
	SupportsTime         bool         `json:"supports_time"`
	SymbolTypes          []SymbolType `json:"symbols_types"`
	SupportedResolutions []string     `json:"supported_resolutions"`
}

Config is the datafeed configuration (GET /api/config).

type Fundamentals

type Fundamentals struct {
	Ticker   string
	Name     string // issuer name
	ISIN     string
	Type     string // e.g. "Actiuni"
	Segment  string // e.g. "Principal"
	Category string // e.g. "Premium"
	Status   string // e.g. "Tranzactionabila"

	// Indicatori bursieri (current valuation snapshot).
	MarketCap    float64 // Capitalizare
	PER          float64 // price/earnings
	PBV          float64 // price/book
	EPS          float64
	DivYield     float64 // DIVY, in percent
	Dividend     float64 // last dividend per share
	DividendYear int     // the year that dividend belongs to

	// Issue info.
	SharesOutstanding int64     // Numar total actiuni
	NominalValue      float64   // Valoare Nominala
	ShareCapital      float64   // Capital social
	FirstTradeDate    time.Time // Data start tranzactionare

	// Ownership structure (excludes the TOTAL row).
	Shareholders []Shareholder
}

Fundamentals is the company snapshot scraped from an instrument's detail page: identity, the "Indicatori bursieri" valuation ratios, issue info, and the ownership structure. BVB does not publish multi-year financial statements as structured data, so this is a current snapshot, not a historical track record.

Numeric indicators that the page omits (e.g. PER for a loss-maker, or a missing dividend) are left zero.

type Instrument

type Instrument struct {
	Ticker string
	ISIN   string
	Name   string // issuer / instrument name
	Market Market
}

Instrument is one listed instrument from a market-list page. ISIN may be empty for the rare listing whose grid row omits it.

type Market

type Market string

Market names a BVB market-list page. These five share the same server-rendered grid (symbol + ISIN + issuer), so Instruments can scrape them uniformly. Indices are not here: they are a different page shape and a different notion of "instrument"; index candles are still reachable through History/SymbolInfo by their datafeed symbol (e.g. "BET").

const (
	Shares       Market = "Shares"
	Bonds        Market = "Bonds"
	FundUnits    Market = "FundUnits"
	Warrants     Market = "Warrants"
	Certificates Market = "Certificates"
)

type Option

type Option func(*Client)

Option configures a Client.

func WithDatafeedURL

func WithDatafeedURL(u string) Option

WithDatafeedURL overrides the wapi.bvb.ro datafeed origin (scheme+host, no trailing slash). Intended for tests.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient replaces the default HTTP client (30s timeout).

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent overrides the User-Agent header.

func WithWebURL

func WithWebURL(u string) Option

WithWebURL overrides the www.bvb.ro origin used for market-list scraping (scheme+host, no trailing slash). Intended for tests.

type Resolution

type Resolution string

Resolution is a datafeed candle resolution. The constants carry the exact codes the /history endpoint accepts — note that daily is "1D", not the TradingView-style "D" advertised by SymbolInfo (bare "D" is rejected with HTTP 500).

const (
	M1  Resolution = "1"
	M5  Resolution = "5"
	M15 Resolution = "15"
	M30 Resolution = "30"
	H1  Resolution = "60"
	D1  Resolution = "1D"
	W1  Resolution = "1W"
	Mo1 Resolution = "1M"
)

func (Resolution) Valid

func (r Resolution) Valid() bool

Valid reports whether r is a supported resolution.

type SearchResult

type SearchResult struct {
	Symbol      string `json:"symbol"`
	FullName    string `json:"full_name"`
	Description string `json:"description"`
	Exchange    string `json:"exchange"`
	Ticker      string `json:"ticker"`
	Type        string `json:"type"`
}

SearchResult is one hit from the datafeed symbol search. Description typically embeds the ISIN. Type is lowercase (share/bond/structured/...).

type Shareholder

type Shareholder struct {
	Name    string
	Shares  int64
	Percent float64
}

Shareholder is one row of the ownership table.

type SymbolInfo

type SymbolInfo struct {
	Name                 string   `json:"name"`
	Description          string   `json:"description"` // issuer/company name
	Exchange             string   `json:"exchange"`
	Type                 string   `json:"type"`
	Ticker               string   `json:"ticker"`
	Session              string   `json:"session"`
	Timezone             string   `json:"timezone"`
	HasIntraday          bool     `json:"has_intraday"`
	SupportedResolutions []string `json:"supported_resolutions"`
}

SymbolInfo is per-symbol metadata (GET /api/symbols?symbol=...).

type SymbolType

type SymbolType struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

SymbolType is one instrument class advertised by the datafeed config. Value is the single-letter code (S=shares, B=bonds, R=rights, U=fund units, T=structured, F=futures, I=indices); it is empty for the "all" pseudo-type.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL