Nubra-native analytics, plots, and reports for trading workflows.
Project description
nubrastats
nubrastats is a Nubra-native helper library for:
- stock performance analytics
- portfolio performance analytics using symbol + quantity inputs
- performance plots
- HTML tearsheet reports
- Nubra API historical data parsing
- Nubra order/trade payload normalization
It is designed so users can fetch market data from Nubra and generate metrics/reports with minimal code.
What This Library Does
Standard workflows:
Single-stock workflow:
- Fetch close prices from Nubra Historical API (
nubrastats.nubra) - Convert prices to returns (
nubrastats.utils.to_returns) - Compute performance metrics (
nubrastats.stats) - Generate charts (
nubrastats.plots) - Export terminal/HTML reports (
nubrastats.reports)
Portfolio workflow:
- Fetch close prices for multiple Nubra symbols
- Use symbol quantities plus first-date prices to derive start weights
- Build portfolio value, returns, and equity curve
- Compare portfolio against optional benchmark
- Export terminal/HTML reports and plots
Alternative workflow:
- Convert filled order payloads to trade rows (
nubrastats.adapters.orders_to_trades) - Compute realized FIFO PnL (
nubrastats.adapters.realized_pnl_fifo) - Build equity and returns from trades
- Run the same stats/plots/reports pipeline
Installation
Install from PyPI:
pip install nubrastats
If you use Nubra API fetch helpers, install Nubra SDK too:
pip install nubra-sdk
Local editable install:
cd nubra-stats
pip install -e .[dev]
Packaging Notes
- The published package is Nubra-only (
nubrastats). - Example scripts in
examples/are for local development and are excluded from release artifacts. - Release artifacts include:
- library source under
src/nubrastats - HTML templates under
src/nubrastats/templates - project metadata (
pyproject.toml,README.md,LICENSE)
- library source under
Quick Start (Popup UI)
If you want users to only call one function and fill details in a popup:
import nubrastats as ns
ns.ui.launch_analyzer_ui()
This opens a popup UI for:
- primary stock analysis inputs
- optional portfolio mode with multiple symbols + quantity
- date range (calendar picker) and interval
- benchmark options
- plot popup options
- optional PNG/HTML output options
- configurable Risk-Free Rate (%) field
After clicking Generate Report, the library handles authentication, analysis, and report generation internally.
UI behavior:
- when only primary symbol mode is used, the generated HTML title defaults to
Nubra Stock Analysis - when portfolio mode is enabled, the generated HTML title defaults to
Nubra Portfolio Report - stock/instrument inputs are forced to uppercase in the UI
- UI instrument dropdowns are limited to
STOCKandINDEX
.env credential note for popup/auth flows:
- keep a
.envfile in project root orexamples/.env - use keys exactly:
PHONE_NOandMPIN - recommended format:
PHONE_NO="9999999999"MPIN="1234"
If credentials are missing, the popup UI now asks for phone / OTP / TOTP / MPIN in modal dialogs instead of waiting silently in the terminal.
When popup plot display is enabled, charts open in one viewer with Previous/Next navigation and keyboard arrow support.
Quick Start (Minimal)
import pandas as pd
import nubrastats as ns
returns = pd.Series([0.01, -0.005, 0.007, 0.002])
ns.reports.metrics(returns=returns, display=True)
ns.reports.html(returns=returns, title="Demo", output="demo-report.html")
# detailed tearsheet mode (extended metrics + multi-section charts)
ns.reports.html(
returns=returns,
title="Demo Detailed",
output="demo-detailed-report.html",
mode="detailed",
)
Quick Start (Nubra API -> Report)
from nubra_python_sdk.marketdata.market_data import MarketData
from nubra_python_sdk.start_sdk import InitNubraSdk, NubraEnv
import pandas as pd
import nubrastats as ns
nubra = InitNubraSdk(env=NubraEnv.UAT, env_creds=True, totp_login=False)
md = MarketData(nubra)
prices = ns.nubra.fetch_close_series(
md_client=md,
symbol="RELIANCE",
exchange="NSE",
instrument_type="STOCK",
start="2025-01-01",
end="2025-12-31",
interval="1d",
)
returns = ns.utils.to_series(ns.utils.to_returns(prices)).dropna()
ns.reports.metrics(returns=returns, display=True)
ns.reports.html(returns=returns, title="RELIANCE Report", output="reliance_report.html")
ns.reports.html(
returns=returns,
benchmark=returns * 0.8, # replace with actual benchmark returns
title="RELIANCE Detailed Report",
output="reliance_detailed_report.html",
mode="detailed",
)
Quick Start (One Call, Minimal User Code)
from nubra_python_sdk.marketdata.market_data import MarketData
from nubra_python_sdk.start_sdk import InitNubraSdk, NubraEnv
import nubrastats as ns
nubra = InitNubraSdk(env=NubraEnv.UAT, env_creds=True)
md = MarketData(nubra)
result = ns.nubra.analyze_symbol(
md_client=md,
symbol="RELIANCE",
exchange="NSE",
instrument_type="STOCK",
start="2025-01-01",
end="2025-12-31",
interval="1d",
benchmark_symbol="NIFTY", # optional
show_plots=True, # pop up plots
save_plots=False, # optional PNG save
generate_html=True, # optional HTML report
open_html=True, # optional browser auto-open
html_output="reliance_report.html",
html_mode="detailed", # "basic" | "detailed"
)
In generated metrics/HTML, the strategy and benchmark are labeled using the
actual symbols (for example RELIANCE and NIFTY) instead of generic names.
The equity curve represents compounded notional capital (base 100000) from
periodic returns, not raw stock price.
Quick Start (Portfolio)
from nubra_python_sdk.marketdata.market_data import MarketData
from nubra_python_sdk.start_sdk import InitNubraSdk, NubraEnv
import nubrastats as ns
nubra = InitNubraSdk(env=NubraEnv.PROD, env_creds=True)
md = MarketData(nubra)
result = ns.nubra.analyze_portfolio(
md_client=md,
positions=[
{"symbol": "RELIANCE", "exchange": "NSE", "instrument_type": "STOCK", "quantity": 2},
{"symbol": "IDEA", "exchange": "NSE", "instrument_type": "STOCK", "quantity": 5},
],
portfolio_name="My Portfolio",
start="2025-01-01",
end="2025-12-31",
interval="1d",
benchmark_symbol="NIFTY",
show_plots=True,
generate_html=True,
html_mode="detailed",
)
Portfolio weights are derived from:
quantity * first available pricefor each symbol- then normalized into start-date portfolio weights
This means the portfolio report reflects the quantity mix entered by the user, not equal weights unless the quantities and prices imply that.
Input Conventions
returnsare decimal returns per period, not percentage.- Example:
0.01means+1%.
- Example:
pricesandequityshould bepandas.Serieswith datetime index.- Nubra historical close values are treated as paise and converted to rupees
(
price_scale="paise"default innubrastats.nubra.fetch_close_series). - Trade prices/fees from broker payloads may be paise-based.
adapters.orders_to_trades(..., price_scale="paise")converts to rupees.
Public API Reference
Top-Level Package (nubrastats)
nubrastats.__version__nubrastats.extend_pandas()- modules:
nubrastats.nubranubrastats.adaptersnubrastats.utilsnubrastats.statsnubrastats.plotsnubrastats.reportsnubrastats.ui
extend_pandas() adds methods to pandas objects:
ns_to_returnsns_to_equityns_sharpens_sortinons_cagrns_max_drawdownns_drawdown_series
Module: nubrastats.nubra
Purpose: build Nubra historical request payloads and extract close price series.
Constants
NUBRA_INTERVALS = {"1s","1m","2m","3m","5m","15m","30m","1h","1d","1w","1mt"}NUBRA_TYPES = {"STOCK","INDEX","OPT","FUT"}
Dataclass: Instrument
| Field | Type | Default |
|---|---|---|
symbol |
str |
required |
exchange |
str |
"NSE" |
instrument_type |
str |
"STOCK" |
Functions
to_utc_iso(value: str | pd.Timestamp) -> str
Converts input datetime to UTC ISO string ending with Z.
Parameters:
value: date/time string orpd.Timestamp
build_historical_payload(...) -> dict[str, Any]
Signature:
build_historical_payload(
*,
symbol: str,
exchange: str,
instrument_type: str,
start: str | pd.Timestamp,
end: str | pd.Timestamp,
interval: str = "1d",
fields: list[str] | None = None,
) -> dict[str, Any]
Parameters:
symbol: instrument symbolexchange: exchange name, e.g."NSE"instrument_type: one ofSTOCK/INDEX/OPT/FUTstart: start date/timeend: end date/timeinterval: one of Nubra supported intervals abovefields: API fields list; defaults to["close"]
Returns:
- request payload dict for
MarketData.historical_data
close_series_from_historical_response(response, symbol) -> pd.Series
Extracts close prices from Nubra historical response object for one symbol.
Parameters:
response: object returned bymd_client.historical_data(...)symbol: symbol to extract
Returns:
- close-price
pd.Seriesindexed by timestamp
fetch_close_series(...) -> pd.Series
Signature:
fetch_close_series(
md_client: Any,
*,
symbol: str,
exchange: str = "NSE",
instrument_type: str = "STOCK",
start: str | pd.Timestamp,
end: str | pd.Timestamp,
interval: str = "1d",
) -> pd.Series
Parameters:
md_client: NubraMarketDatainstancesymbol,exchange,instrument_type: instrument identitystart,end: date boundsinterval: candle interval
Returns:
- filtered close-price
pd.Seriesfor the selected range
Raises:
ValueErrorwhen no data is available or invalid date interval
analyze_symbol(...) -> dict[str, Any]
One-call convenience pipeline for minimal user code:
- fetch primary symbol historical close
- optional benchmark fetch and alignment
- compute returns/equity
- compute metrics
- show plots immediately (
show_plots=True) - optionally save PNGs (
save_plots=True) - optionally generate HTML report (
generate_html=True) - optionally auto-open HTML (
open_html=True)
Signature:
analyze_symbol(
md_client: Any,
*,
symbol: str,
exchange: str = "NSE",
instrument_type: str = "STOCK",
start: str | pd.Timestamp,
end: str | pd.Timestamp,
interval: str = "1d",
benchmark_symbol: str | None = None,
benchmark_exchange: str = "NSE",
benchmark_instrument_type: str = "INDEX",
rf: float = 0.06,
periods_per_year: int = 252,
show_plots: bool = True,
save_plots: bool = False,
plots_dir: str | None = None,
generate_html: bool = False,
open_html: bool = False,
html_output: str | None = None,
html_mode: str = "basic",
title: str | None = None,
display_metrics: bool = True,
) -> dict[str, Any]
Returns dictionary keys:
prices,returns,equitybenchmark_prices,benchmark_returns,benchmark_equitymetrics(DataFrame)plot_paths(empty unlesssave_plots=True)html_path(Noneunlessgenerate_html=True)html_opened(Trueif browser auto-open succeeded)html_mode("basic"or"detailed")
analyze_portfolio(...) -> dict[str, Any]
One-call portfolio pipeline for multiple symbols and quantities.
Behavior:
- normalize portfolio items
- fetch close prices for each symbol
- align dates across symbols
- compute start-date value from
quantity * first price - derive portfolio weights
- build portfolio value, returns, equity, and optional benchmark comparison
- generate plots and HTML report just like single-symbol flow
Important input rule:
- quantity must be greater than
0for every portfolio item - duplicate symbol/exchange/type rows are rejected
Returns dictionary keys include:
prices/component_pricesreturns,equityportfolio_itemsportfolio_weightsmetricsbenchmark_prices,benchmark_returns,benchmark_equityhtml_path,html_opened,plot_paths
Module: nubrastats.ui
Purpose: popup-based minimal UX for end users (no manual plumbing code).
Dataclass: AnalyzerUIConfig
Holds all UI/run configuration fields such as:
- environment + login flags
- primary symbol settings
- portfolio settings (
portfolio_enabled,portfolio_name,portfolio_items) - benchmark settings
- plot/report options
risk_free_rate(UI default6.0, interpreted as percent)
run_from_config(config, *, md_client=None) -> dict[str, Any]
Runs analysis directly from a config object.
Parameters:
config:AnalyzerUIConfigmd_client: optional pre-created NubraMarketDataclient
Returns:
- same output dictionary as
nubrastats.nubra.analyze_symbol
launch_analyzer_ui(initial=None) -> None
Opens the popup UI and executes analysis when user clicks Generate Report.
Parameters:
initial: optionalAnalyzerUIConfigto prefill defaults
Module: nubrastats.adapters
Purpose: convert broker/order payloads into consistent trade tables and PnL.
orders_to_trades(orders, *, price_scale="paise") -> pd.DataFrame
Parameters:
orders: iterable of order dictsprice_scale:"paise"or raw-unscaled mode
Reads these possible keys from each order:
- quantity keys:
filled_qty,order_qty,trade_qty,quantity - price keys:
avg_filled_price,trade_price,order_price,price,last_traded_price - side keys:
order_side,side - timestamp keys:
filled_time,order_time,last_modified,timestamp - fee keys:
brokerage,fee,charges
Output columns:
timestamp,symbol,side,quantity,price,fee,order_id,tag,strategy_id,status
realized_pnl_fifo(trades: pd.DataFrame) -> pd.DataFrame
Computes realized PnL using per-symbol FIFO lot matching. Supports long and short inventory transitions.
Required columns:
timestamp,symbol,side,quantity,price
Optional:
fee(defaults to0.0)
Adds columns:
realized_pnlcum_realized_pnl
equity_curve_from_trades(trades, *, starting_capital=100000.0) -> pd.Series
If realized_pnl is missing, it computes it first via FIFO.
Then cumulative sum over realized PnL on top of starting capital.
returns_from_trades(trades, *, starting_capital=100000.0) -> pd.Series
Builds equity curve from trades and converts it to period returns.
Module: nubrastats.utils
Purpose: reusable conversion/index/time helpers.
to_series(data) -> pd.Series
Parameters:
data:pd.Series,pd.DataFrame, or iterable
Returns:
- one
pd.Series(for DataFrame takes first column)
ensure_datetime_index(data, *, fallback_start=None, freq="D")
Ensures datetime index. If conversion fails, generates date range index.
to_returns(equity) -> pd.Series | pd.DataFrame
Converts equity/price series to returns via pct_change.
to_equity(returns, *, start_balance=100000.0)
Compounds returns into equity series.
annualization_factor(periods_per_year=252) -> int
Returns a safe annualization factor (min 1).
safe_div(numerator, denominator) -> float
Returns NaN if denominator is zero or NaN.
monthly_returns_matrix(returns: pd.Series) -> pd.DataFrame
Pivoted matrix: index year, columns Jan..Dec, values monthly compounded returns.
normalize_side(value: str) -> str
Maps side aliases to standard:
- BUY aliases:
BUY,ORDER_SIDE_BUY,B - SELL aliases:
SELL,ORDER_SIDE_SELL,S
to_timestamp(value) -> pd.Timestamp
Converts int/float/string timestamps. For numeric values, tries:
- nanoseconds if very large
- milliseconds
- seconds
Module: nubrastats.stats
Purpose: performance metrics from returns/equity/trades.
comp(returns) -> float | pd.Series
Compounded return: prod(1 + r) - 1.
cagr(returns, periods_per_year=252) -> float
Annualized compounded growth rate.
volatility(returns, periods_per_year=252, annualize=True) -> float
Sample standard deviation; annualized if requested.
sharpe(returns, rf=0.06, periods_per_year=252) -> float
Sharpe ratio using per-period risk-free conversion.
sortino(returns, rf=0.06, periods_per_year=252) -> float
Sortino ratio using downside deviation only.
drawdown_series(returns=None, equity=None) -> pd.Series
Drawdown over time: equity / cummax(equity) - 1.
Either returns or equity must be provided.
max_drawdown(returns=None, equity=None) -> float
Minimum value of drawdown series.
win_rate_returns(returns) -> float
Fraction of positive non-zero return periods.
win_rate_trades(trades) -> float
Fraction of positive non-zero trade PnL rows.
Uses realized_pnl column when available, otherwise pnl.
profit_factor(trades) -> float
sum(winning pnl) / abs(sum(losing pnl)).
expectancy(trades) -> float
Mean trade PnL.
summary(...) -> pd.Series
Signature:
summary(
*,
returns: pd.Series | None = None,
equity: pd.Series | None = None,
trades: pd.DataFrame | None = None,
rf: float = 0.06,
periods_per_year: int = 252,
) -> pd.Series
Returns these keys:
Total ReturnCAGRVolatilitySharpeSortinoMax DrawdownWin Rate (Periods)Avg ReturnBest PeriodWorst Period
If trades provided, also:
Win Rate (Trades)Profit FactorExpectancyTrade Count
Module: nubrastats.plots
Purpose: plotting helpers using matplotlib/seaborn.
All plotting functions return matplotlib.figure.Figure.
Most functions support:
show=True/Falsesavefig={...}passed tofig.savefig
By default, plots are displayed when show=True.
Saving PNG files is optional via savefig or higher-level helpers.
equity_curve(equity, benchmark=None, *, title="Equity Curve", show=True, savefig=None)
Plots strategy equity and optional benchmark equity.
drawdown(returns=None, equity=None, *, title="Drawdown", show=True, savefig=None)
Filled drawdown chart from returns or equity.
monthly_heatmap(returns, *, title="Monthly Returns Heatmap", show=True, savefig=None)
Year-month heatmap in percentage terms.
pnl_distribution(trades, *, title="Trade PnL Distribution", show=True, savefig=None)
Histogram/KDE of trade PnL (realized_pnl or pnl).
rolling_sharpe(returns, *, window=63, rf=0.0, periods_per_year=252, title="Rolling Sharpe", show=True, savefig=None)
Rolling Sharpe time series plot.
figure_to_png_bytes(fig) -> bytes
Encodes a figure as PNG bytes (used by HTML report embedding).
Module: nubrastats.reports
Purpose: ready-to-use metrics tables and report generation.
metrics(...) -> pd.DataFrame
Signature:
metrics(
*,
returns: pd.Series | None = None,
equity: pd.Series | None = None,
trades: pd.DataFrame | None = None,
benchmark: pd.Series | None = None,
rf: float = 0.06,
periods_per_year: int = 252,
display: bool = True,
precision: int = 4,
) -> pd.DataFrame
Behavior:
- computes summary metrics for strategy
- optional benchmark summary in second column
- prints table if
display=True
basic(...) -> pd.DataFrame
Same metric inputs as metrics plus:
show_plots: bool = True
Behavior:
- prints metrics
- optional standard plot set (equity/drawdown/monthly heatmap)
full(...) -> pd.DataFrame
Same parameters as basic.
Behavior:
- includes
basicmetrics - adds rolling Sharpe and PnL distribution (if trades provided)
html(...) -> str
Signature:
html(
*,
returns: pd.Series | None = None,
equity: pd.Series | None = None,
trades: pd.DataFrame | None = None,
benchmark: pd.Series | None = None,
rf: float = 0.06,
periods_per_year: int = 252,
title: str = "Nubra Strategy Tearsheet",
output: str | None = None,
template_path: str | None = None,
mode: str = "basic",
) -> str
Behavior:
mode="basic": compact report (metrics + core charts)mode="detailed": extended tearsheet (multi-section charts + advanced tables)- writes file to
output:- basic default:
nubrastats-report.html - detailed default:
nubrastats-detailed-report.html
- basic default:
Returns:
- output file path string
Module: nubrastats.models
Simple typed dataclasses:
Trade
| Field | Type | Required | Default |
|---|---|---|---|
timestamp |
datetime |
yes | - |
symbol |
str |
yes | - |
side |
str |
yes | - |
quantity |
float |
yes | - |
price |
float |
yes | - |
fee |
float |
no | 0.0 |
strategy_id |
`str | None` | no |
tag |
`str | None` | no |
OrderEvent
| Field | Type | Required | Default |
|---|---|---|---|
timestamp |
datetime |
yes | - |
order_id |
`str | int` | yes |
symbol |
str |
yes | - |
side |
str |
yes | - |
quantity |
float |
yes | - |
price |
float |
yes | - |
status |
str |
yes | - |
tag |
`str | None` | no |
Example Scripts Included
| Script | Purpose |
|---|---|
examples/import_smoke.py |
import/version smoke test |
examples/generate_report.py |
sample report generation from sample trades |
examples/terminal_menu_tester.py |
interactive Nubra terminal flow |
examples/nubra_api_static_test.py |
non-interactive QA/checklist runner |
examples/nubra_api_quick_test.py |
short placeholder-based run script |
examples/detailed_report_mode_demo.py |
generate extended HTML tearsheet (mode="detailed") |
examples/nubra_ui_popup_demo.py |
one-function popup UI launcher |
Testing and Quality
Run checks:
python -m ruff check src tests examples
python -m pytest
Build package:
python -m build
python -m twine check dist/*
Notes
- The quick script is for minimal user lines.
- The static script is intentionally larger for diagnostics and validation checks.
- This project is Nubra-native and does not depend on external market-data providers.
- Popup UI currently defaults Risk-Free Rate to
6%, but users can override it. - Detailed HTML report is the default mode in the popup UI.
- Portfolio mode in the popup UI generates
Nubra Portfolio Report; single-symbol mode generatesNubra Stock Analysisby default.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nubrastats-0.1.4.tar.gz.
File metadata
- Download URL: nubrastats-0.1.4.tar.gz
- Upload date:
- Size: 46.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cf60c2986f3a084f6f90577e4534556e1fa97e1f2633181b568a5045226e5a6
|
|
| MD5 |
7f879112412fcc7e8fea34d9909dcf86
|
|
| BLAKE2b-256 |
8fefbe128704dba5d5d46a481186ad0132e47e54edc4e2a98cc4ebdfa60ae1d3
|
File details
Details for the file nubrastats-0.1.4-py3-none-any.whl.
File metadata
- Download URL: nubrastats-0.1.4-py3-none-any.whl
- Upload date:
- Size: 42.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d69e7320e0934371f7739c1712b2c7b1994de3a93adebf3418a5c9dd9d38cc3e
|
|
| MD5 |
fdbaf97471929ec3f0913f715abc52cc
|
|
| BLAKE2b-256 |
260b4c610641a4759bcfc0e9bec1c5579f5931c7ad508836b678f7cd82886dd2
|