All notable changes to okama are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
A feature release built around one idea: a financial plan is a sequence of
portfolios, not a single one. FinPlan chains stages so that the terminal
balance of each stage becomes the starting balance of the next — per Monte
Carlo scenario, not per percentile — which is what lets a retirement stage be
funded by whatever the accumulation stage actually produced. Cash-flow
strategies keep their time base across stage boundaries: indexation, extra
cash-flow compounding, Vanguard dynamic spending and the drawdown-triggered
withdrawal cut all count months from the start of the plan rather than
restarting at every stage. See
examples/04 investment portfolios with DCF.ipynb
for the single-portfolio cash-flow workflow this release generalizes.
FinPlanandFinPlanStage: a financial plan modelled as a sequence of portfolio stages. The terminal balance of each stage becomes the starting balance of the next, per Monte Carlo scenario, so a retirement is funded by whatever the accumulation stage produced. Plan-level methods:FinPlan.monte_carlo_wealth,FinPlan.monte_carlo_cash_flow,FinPlan.monte_carlo_survival_period,FinPlan.monte_carlo_irr,FinPlan.probability_of_success,FinPlan.balance_percentiles,FinPlan.plot_forecast_monte_carlo, and the calendar-history backtestFinPlan.wealth_index/FinPlan.cash_flow_ts.okama.portfolios.mc.generate_returns_tsandokama.portfolios.mc.resolve_distribution_parameters: pure functions for drawing Monte Carlo returns, extracted fromMonteCarlo.
dcf_calculations._simulate_paths_mcacceptsinitial_balance,month_offsetandtask. Defaults reproduce the previous behaviour, soPortfolio.dcfis unaffected.
A breaking release with a single theme: tracking_error() now computes the tracking
error as the CFA curriculum defines it. The previous default mixed two measures the
curriculum deliberately keeps apart — how far a fund lags its benchmark (tracking
difference) and how unstable that lag is (tracking error) — so the same symbols over
the same period return a different number after this upgrade. See
examples/02 index funds perfomance.ipynb
for the index-fund comparison workflow these methods serve. The release also fixes a
stale Portfolio.assets_weights mapping.
- Breaking.
tracking_error()now returns the sample standard deviation of the return differences around their mean (Bessel's correction), annualized bysqrt(12)— the tracking error as defined by the CFA curriculum (CFA Level II, 2019, V6, eq. 8; CFA Level I, 2025, V9 Portfolio Management, footnote 3). The previous default was the uncentered root-mean-square of the differences, which folded the systematic lag behind the benchmark (the tracking difference) into the result:TE_rms² = (N-1)/N · TE_std² + mean(d)². The same symbols over the same period therefore return a different number than in 2.3.1 and earlier — usually lower, since the lag term drops out, though marginally higher for a fund whose lag is smaller thanTE_std / sqrt(N), where the uncentered formula's division byNinstead ofN-1dominates. Affectshelpers.Index.tracking_error,AssetList.tracking_errorandPortfolio.tracking_error(#97).
- Breaking. The
methodparameter ofhelpers.Index.tracking_error,AssetList.tracking_errorandPortfolio.tracking_error. Tracking error now has a single definition, somethod="rms"andmethod="std"(both added in 2.2.2) are gone — passingmethod=raisesTypeError. Code that asked formethod="std"keeps its values by simply dropping the argument; code that relied on the"rms"values has to compute them itself, as the mixture of tracking difference and tracking error that it is.
Portfolio.assets_weights(the symbol → weight mapping) was built once in the constructor and never refreshed by theweightssetter, so afterpf.weights = [...]the public attribute kept reporting the weights the portfolio was created with. It is now rebuilt wheneverweightsis assigned.requirements.txtcappedarch < 8.0.0andstatsmodels < 0.15.0whilepyproject.tomlhad dropped both upper bounds, so the file forbade the very versions the release is tested against (arch8.0.0). It mirrors thepyproject.tomlconstraints again.
A hotfix release. import okama raised TypeError on Python 3.11, 3.12 and 3.13 —
every supported version except 3.14 — which made releases v2.2.3, v2.2.4 and v2.3.0
unusable for most users. The release also adds a continuous integration test matrix
so this class of breakage cannot reach PyPI again.
Frame.get_portfolio_mean_return()andFrame.get_portfolio_risk()annotated theirweightsparameter aslist | np.array.np.arrayis a factory function rather than a type, so the PEP 604|operator raisedTypeError: unsupported operand type(s) for |: 'type' and 'builtin_function_or_method'. Python evaluates annotations eagerly before 3.14, so the error was raised while importingokama.common.helpers.helpersand brokeimport okamaoutright on Python 3.11–3.13. Python 3.14 defers annotation evaluation (PEP 649), which is why the bug stayed invisible during development. Both annotations now usenp.ndarray. Reported and fixed by @nervgh in #95.
- The unit test suite now runs in CI on every supported Python version (3.11, 3.12, 3.13, 3.14) on each push and pull request. Previously only ruff and CodeQL ran automatically, and no test was ever executed on the minimum supported Python — which is how the import failure above shipped through three releases.
- The release workflow now requires a full unit-test run on the minimum supported Python before any version bump.
Adds native-language ("local") asset names as a first-class labelling option
across assets, lists, plots, tables and namespace search, so charts and reports
can show, for example, Сбербанк or 贵州茅台 instead of only the Latin ticker
or English name. The release also hardens the EfficientFrontier parallel
optimizer against a nested process-count explosion and fixes the
Portfolio.okamaio_link URL format. Dependencies were refreshed in the lock file.
Assetnow exposes alocal_nameattribute (native-language name,Nonewhen the data provider has none) and stores the raw provider payload onAsset.info.AssetListgained alocal_namesmapping (symbol → native name, falling back to the English name when a native one is missing) and an internal_asset_labelsresolver shared by the labelling call sites.- Native names can be selected as a label mode wherever ticker/name labels were
already available:
AssetList.plot_assetsandAssetList.describeaccepttickers="local_names"(in addition to"tickers"/"names"), keeping the legacyticker_namesboolean working.EfficientFrontierandEfficientFrontierSinglegained alabelsmode selector (labels/labels_are_tickersproperties) while still accepting the legacyticker_namesconstructor argument.- The
Portfolio.tableassets table adds a "local name" column when at least one member has a native name.
okama.search(namespace search) now also matches against the nativelocal_namecolumn, so a query likeСбербанкfinds the asset.
EfficientFrontierparallel optimization no longer multiplies worker processes in a nested parallel context (issue #94). The newokama.settings.resolve_n_jobshelper clamps joblibn_jobsto a single worker when already inside a parallel context — apytest-xdistworker (PYTEST_XDIST_WORKERset) or an active joblib pool (backend nesting level above zero) — otherwise honouring theOKAMA_N_JOBSenvironment variable (default-1, all cores). This prevents the multiplicativeN x Nprocess explosion (observed as ~480lokyworkers saturating RAM/swap) whenpytest-xdisttest workers each opened a fulllokypool.Portfolio.okamaio_linknow generates a URL matching the okama.io/portfolio query-string format. Previously the link carried parameters the page never read (a duplicaterebalancing_periodandrebalancing_abs_deviation/rebalancing_rel_deviation), so the rebalancing deviations were silently dropped on open. Dates now use the site-wideYYYY-MMformat instead ofYYYY-MM-DD, weights lose the trailing.0, and the rebalancing deviations are emitted asabs_dev/rel_devpercentages (0-100) instead of fractions.
- Fixed reStructuredText "Unexpected indentation" errors in the
describedocstrings ofMacroABC/Inflation(okama/macro.py) and thecvar_t/cvar_lognormdocstrings inokama/helpers/tails.py, so the Sphinx build no longer emits those errors. - Documented that rendering native (
local_names) labels in CJK scripts needs a CJK-capable Matplotlib font, and that namespace search also matches native names.
Fixes the Most Diversified Portfolios line (EfficientFrontier.mdp_points)
failing at its leftmost point — the same single-asset frontier-corner class of
failure fixed for the efficient frontier itself in 2.2.3.
EfficientFrontier.get_most_diversified_portfolio(rebalanced/multi-period) raisedRuntimeErrorat the leftmost point of the Most Diversified Portfolios line (mdp_points), where the target CAGR equals the minimum-CAGR asset's own CAGR: there the 100% single-asset portfolio is the only feasible point (diversification ratio 1) and SLSQP from the equal-weights start could not reach it, aborting the whole line. The method now falls back to the deterministic single-asset corner portfolio (the error-message typo "where" → "were" was also fixed).
Fixes two efficient-frontier failures where the risk optimizer could not reach a
single-asset corner point — the leftmost (minimum-CAGR) point of the rebalanced
EfficientFrontier and the maximum-return point of EfficientFrontierSingle — so
both frontiers are drawn for the affected asset sets instead of raising a
RuntimeError. The second failure surfaced with the stricter SLSQP solver in
scipy 1.18.
EfficientFrontier.minimize_risk(rebalanced/multi-period frontier) raisedRuntimeError: No solution found for target CAGR value: ...at the leftmost frontier point when the target CAGR equalled the minimum-CAGR asset's own CAGR and SLSQP failed to converge to that single-asset vertex from the multi-start initial guesses. The method now falls back to the deterministic single-asset corner portfolio (mirroring the existing guard in_maximize_risk), so the efficient frontier is drawn for such asset sets instead of failing.EfficientFrontierSingle.minimize_risk(single-period frontier) raisedRuntimeError: No solutions were foundat the maximum-return frontier point when the target return equalled a single asset's own mean return and SLSQP failed to converge to that single-asset vertex from the equal-weights start (surfaced by the stricter SLSQP in scipy 1.18). The method now falls back to the deterministic single-asset corner portfolio, soEfficientFrontierSingle.ef_pointsis drawn for such asset sets instead of failing.
Adds new analytics — ex-post tracking error for Portfolio and an RMS/std
method switch for tracking error across Index, AssetList, and Portfolio,
plus inflation-adjusted and price-only drawdown views — and fixes the
maintain_balance goals of find_the_largest_withdrawals_size() together with
duplicated efficient-frontier points under a thread backend.
Portfolio.tracking_error(benchmark, rolling_window, method)— ex-post tracking error of a portfolio against a benchmark (#61). The benchmark may be a string ticker or an asset-like object (Asset,Portfolio); the method delegates toAssetList.methodparameter forAssetList.tracking_errorand the underlyinghelpers.Index.tracking_error:"rms"(default, legacy uncentered root-mean-square) or"std"(centered sample standard deviation with Bessel's correction).AssetList.real_drawdownsandPortfolio.real_drawdowns— drawdowns of the inflation-adjusted wealth index, exposing purchasing-power losses hidden by nominal growth (requiresinflation=True) (#51).AssetList.price_drawdownsandPortfolio.price_drawdowns— drawdowns based on close prices not adjusted for dividends, which can differ markedly from the total-returndrawdownsfor high-dividend assets (#44).
PortfolioDCF.find_the_largest_withdrawals_size()raisedValueError: target_survival_period must be less than Monte Carlo simulation periodfor themaintain_balance_pvandmaintain_balance_fvgoals on any Monte Carlo period ≤ 27, even though those goals never usetarget_survival_periodand the caller never passed it (#90). The parameter is now validated only for thesurvival_periodgoal.EfficientFrontier.ef_pointsproduced duplicated right-part points under a thread-based joblib backend, because the right-part worker both appended its row to the shared list and returned it (#86). The worker now only returns the row, matching the left-part worker.Frame.kstest_series(used bykstest_for_all_distributionsand the distribution-fit properties ofAssetList/Portfolio) raisedTypeError: ndtr() takes from 1 to 2 positional arguments but 3 were givenwith scipy 1.18.0 on Python ≥ 3.12, which routes the named-distributionkstest(..., "norm", args=...)call through thendtrufunc. The Kolmogorov–Smirnov test now passes a frozen-distribution CDF, which is numerically equivalent and compatible with scipy 1.17 and 1.18.
- Clarified that
tracking_errorvalues are decimal fractions, not percentages. PortfolioDCF.find_the_largest_withdrawals_size()docstring now notes thatIndexationStrategy/PercentageStrategysubclasses (e.g.CutWithdrawalsIfDrawdown,VanguardDynamicSpending) are supported.
Fixes the multi-period Efficient Frontier around single-asset corner points — the right part of the frontier now always terminates at the corner asset (no dominated "hook", no silently missing right part, pairwise frontiers reach the asset dots) — and removes pandas 3 deprecation warnings.
EfficientFrontier.ef_pointsdrew a dominated hook near the max-CAGR corner (#84): SLSQP started exactly at the optimal vertex of the bounds fails spuriously, and the fallback start converged to an interior local maximum.EfficientFrontier._maximize_risk()now keeps the better of the optimizer result and the 100% single-asset portfolio whenever the target CAGR equals an asset's own CAGR, so the right part of the frontier ends exactly at the corner asset with monotonically increasing risk.- The right part of
EfficientFrontier.ef_pointscould silently disappear together with its corner point when the right CAGR span was much narrower than the left one (the point-count formula produced an empty target range). - Pairwise efficient frontiers (
EfficientFrontier.plot_pair_ef()) stopped short of the asset point when the best rebalanced mix beat the asset by less than 1% of CAGR (#87). An asset is now considered to be the global max-CAGR point only when both its CAGR and its risk match it, and a narrow-but-real right CAGR span is drawn instead of being treated as degenerate. EfficientFrontier.plot_pair_ef()ignored the parent'srebalancing_strategy, always computing pair frontiers with the default yearly rebalancing.Pandas4Warningon pandas 3 (#85): dropped the deprecatedcopykeyword insymbols_in_namespace()andIndex.rolling_fn()(slated for removal in pandas 4.0).
- The
EfficientFrontier.ef_pointstarget grid now samples every asset's CAGR lying inside the range (previously only the minimum-variance asset's), so the frontier polyline passes exactly through single-asset points on the boundary. The number of rows inef_points/mdp_pointscan therefore slightly exceedn_points.
- README refreshed: fixed broken images on PyPI, added a hero image, an MCP server section and a uv install option.
Makes Monte Carlo cash-flow simulations dramatically faster (vectorized wealth and cash-flow engines, a Brent-based withdrawal solver — three to four orders of magnitude per simulation), adds money-weighted IRR (MWRR) for portfolio cash flows — both on historical data and across Monte Carlo forecast paths — makes the Monte Carlo return draw cached and reproducible, and fixes three cash-flow calculation bugs.
Portfolio.dcf.irr()(PortfolioDCF.irr) — nominal annualized money-weighted internal rate of return (IRR/MWRR) of the portfolio cash flow over the full historical period, honoring the configuredCashFlowstrategy (IndexationStrategy,PercentageStrategy,VanguardDynamicSpending,CutWithdrawalsIfDrawdown,TimeSeriesStrategy). With no intermediate cash flows it equalsPortfolio.get_cagr()for the period.Portfolio.dcf.monte_carlo_irr()(PortfolioDCF.monte_carlo_irr) — the distribution (pandas.Series) of per-path money-weighted IRRs across Monte Carlo forecast paths, the forward-looking counterpart ofPortfolioDCF.irr.irr_of_cashflow_matrix()inokama.portfolios.dcf_calculations— a vectorized Newton solver (analytic derivative,scipy.optimize.brentqfallback) computing IRR for an(n_periods, n_series)cash-flow matrix in one pass; shared by bothPortfolioDCF.irrandPortfolioDCF.monte_carlo_irr.seedparameter for reproducible Monte Carlo draws:MonteCarlo.seedand the newseedargument ofPortfolioDCF.set_mc_parameters().
- The Monte Carlo return draw (
MonteCarlo.monte_carlo_returns_ts) is now generated once and cached — shared byPortfolioDCF.monte_carlo_wealth,monte_carlo_cash_flow,monte_carlo_survival_period,monte_carlo_irrand the CAGR-distribution methods — so all of them see one consistent scenario set (previously each access regenerated fresh randomness). The cache is invalidated when any Monte Carlo parameter changes (distribution,distribution_parameters,period,mc_number,seed). As a consequence, unseeded results ofPortfolioDCF.find_the_largest_withdrawals_size()and of the CAGR-distribution methods shift versus 2.1.1: the bisection infind_the_largest_withdrawals_sizenow evaluates every candidate against the same scenario set (removing the sampling noise that previously broke its monotonicity). Useset_mc_parameters(..., seed=...)for reproducible runs. Portfolio.dcf.find_the_largest_withdrawals_size()is faster: the bisection search is replaced with Brent's method (scipy.optimize.brentq) on a signed goal residual, and both ends ofwithdrawals_rangeare checked first so the solver exits after 1–2 Monte Carlo simulations when the solution lies outside the range. The public signature, theResultshape and the stopping rule (error_rel < tolerance_rel) are unchanged;iter_maxnow caps objective evaluations (Monte Carlo simulations), including the two range-end checks, so the history of intermediate attempts inResult.solutionsdiffers from the former bisection midpoints.iter_maxvalues below 1 are now rejected with aValueError.- Monte Carlo wealth simulation is vectorized:
Portfolio.dcf.monte_carlo_wealth()and everything built on it (monte_carlo_survival_period(),plot_forecast_monte_carlo(),find_the_largest_withdrawals_size()) now computes all random paths in one pass (get_wealth_indexes_fv_with_cashflow_mcinokama.portfolios.dcf_calculations) instead of a per-path pandasapply. Results are unchanged (pinned by an equivalence-test grid across strategies, frequencies and extra cash flows); measured speedup of one full simulation is three to four orders of magnitude (×1400 for yearly and ×6800 for monthly withdrawal frequencies on 1,000 paths × 30 years). The negative-balance masking and the survival-date scan are vectorized as well. - Monte Carlo cash-flow simulation is vectorized as well:
Portfolio.dcf.monte_carlo_cash_flow()builds its cache with the newget_cash_flow_fv_mc(one pass for all paths, sharing a core with the wealth engine), andPortfolio.dcf.monte_carlo_irr()now consumes the sharedmonte_carlo_wealth/monte_carlo_cash_flowcaches instead of two per-path computations (measured end-to-end speedup ofmonte_carlo_irr(): ×488 on 1,000 paths × 30 years).
PortfolioDCF.monte_carlo_cash_flow()withremove_if_wealth_index_negative=Truepreviously masked a cash-flow draw against a wealth-index draw generated from different randomness; with the shared cached draw the depletion mask is now consistent per path.- In periodic-frequency simulations, the first month of a period containing
extra cash flows (
time_series) skipped its return inget_wealth_indexes_fv_with_cashflow(and additionally skipped the cash flow inget_cash_flow_fvbalance tracking). The recursion is now uniform for every month; wealth indexes and cash flow series with extra cash flows atyear/half-year/quarterfrequencies change accordingly (#81). VanguardDynamicSpendingfloor_ceilinglimits never bound in wealth-index calculations (wealth_index,monte_carlo_wealthand everything built on them): the previous withdrawal was always reported as 0. Wealth indexes for VDS strategies withfloor_ceilingchange accordingly and are now consistent withcash_flow_ts(#82).VanguardDynamicSpending.__init__bypassed the validating setters forfloor_ceiling,min_max_annual_withdrawalsandadjust_min_max, so out-of-contract limits (e.g. a non-negative floor ormin > max) were silently accepted at construction. The constructor now routes through the public setters, and both limit setters acceptNone(meaning "limit disabled") so the documented defaults remain valid (#83).
- Dependency floor
idna >= 3.15to close CVE-2026-45409.
- New "IRR — money-weighted return" section in the
04 investment portfolios with DCF
notebook demonstrating
Portfolio.dcf.irr()andPortfolio.dcf.monte_carlo_irr(). - The
find_the_largest_withdrawals_size()docstring example now shows a real, seeded solver run (range-end checks followed by Brent steps).
Adds systematic (grid-based) enumeration of portfolio weights on the efficient frontier as a deterministic alternative to Monte-Carlo sampling, and enriches the frontier sampling outputs with per-asset weight columns.
EfficientFrontier.get_grid_portfolios()(multi-period, rebalanced) andEfficientFrontierSingle.get_grid_portfolios()(single-period) enumerate all portfolios whose weights lie on a fixed percentage grid (step, default0.10), respecting per-assetbounds. This complements the randomget_monte_carlo()with a reproducible, exhaustive sampling of the feasible region.Float.get_grid_weights()helper inokama.common.helpers— a reusable generator of all weight vectors summing to 1.0 on a given grid step, honoring per-asset bounds. Thestepis validated to lie in[0.01, 1.0]and to divide 1.0 evenly.- Per-asset weight columns in the outputs of
EfficientFrontier.get_monte_carlo()andEfficientFrontier.get_grid_portfolios()(multi-period), matching the column layout already produced byEfficientFrontierSingle.get_monte_carlo().
- Pinned
sphinx < 9for the docs build: the Sphinx 9.x autodoc rewrite raisesValueError: The truth value of a DataFrame is ambiguouson pandas DataFrame class attributes (e.g. inokama.common.make_asset_list). - Bumped the pre-commit
ruffhook tov0.15.14to match the poetry/CI ruff version, so it stops re-applying fixes the project ruff already suppresses via# noqa: UP0xxcomments.
Feature release that switches get_cagr and get_cumulative_return to an
expanding-window definition, makes the okama API endpoint configurable via
environment variables, and ships a batch of correctness fixes across the
helpers, frontier, DCF, macro, and plotting layers.
AssetList.get_cumulative_return()andPortfolio.get_cumulative_return()now return an expanding cumulative return series instead of a single end-of-period scalar. Notebook 03 investment portfolios.ipynb updated accordingly.AssetList.get_cagr()andPortfolio.get_cagr()now compute CAGR on an expanding window, consistent withget_cumulative_return().
- Configurable API base URL and request timeout via environment variables
(
OKAMA_API_URL,OKAMA_API_TIMEOUT) inokama.settingsandokama.api.api_methods.
Frame.get_semideviation()(inokama.common.helpers) now uses the sample mean of returns instead of the population mean, restoring the standard semideviation definition; propagated throughAssetListandPortfolioconsumers.AssetList.recovery_periodsis robust to alast_datethat is not on a month start.EfficientFrontier/EfficientFrontierReb(single- and multi-period variants) now raiseRuntimeErroron failed SLSQP optimisation instead of silently returning invalid weights.AssetList.plot_assets()/Portfolio.plot_assets()autoscale no longer passes the invalidaxis="year"argument.Inflation.cumulative_inflation(inokama.macro) uses.iloc[-1]instead of positional[-1], fixing a pandas FutureWarning / lookup bug.PortfolioDCFdiscount-rate attribute renamed from the misspelledmonlthly_discount_ratetomonthly_discount_rate(okama.portfolios.dcf,okama.portfolios.dcf_calculations).- Helpers producing NaN rows now use
np.nanindict.fromkeys(...)so resulting DataFrames keep float dtype (okama.common.helpers, consumed byAssetListandPortfolio).
- Dead
Portfolio._clear_cf_cachemethod.
- Enabled ruff
UP(pyupgrade) rules and applied auto-fixes acrossokama.common.helpers,okama.common.helpers.rebalancing,okama.portfolios.dcf, and notebook 11 rebalancing portfolio.ipynb. - Aligned declared supported Python versions with the
pyproject.tomlminimum;AGENTS.mdnow mandates a TDD workflow for production code changes. .gitignoreexcludes.env.
Maintenance-focused release that improves compatibility with pandas 3.x and Python 3.14,
refines PortfolioDCF cash flow calculations, and makes several plotting and sharing APIs
easier to integrate.
pandas3.x and Python 3.14 support acrossAsset,AssetList,Portfolio,PortfolioDCF,MacroABC,Rebalance, and related helpers.PortfolioDCF.plot_forecast_monte_carlo(),MonteCarlo.plot_qq(), andMonteCarlo.plot_hist_fit()now return matplotlibAxesobjects.Portfolio.plot_assets()andEfficientFrontier.plot_assets()accept extramatplotlib.pyplot.scatter()keyword arguments.Portfolio.okamaio_linkserializes the fullRebalanceconfiguration (period,abs_deviation,rel_deviation).AssetList.get_monthly_geometric_mean_return()andPortfolio.get_monthly_geometric_mean_return()— direct monthly geometric mean return calculations.EfficientFrontiercaches intermediate calculations to speed up repeated optimization runs.
PortfolioDCF.wealth_index()andPortfolioDCF.cash_flow_ts()no longer apply reverse discounting in present-value mode.PortfolioDCF.wealth_index(discounting="pv")now discounts inflation correctly.PortfolioDCF.find_the_largest_withdrawals_size()fixed for Monte Carlo period validation; works correctly withVanguardDynamicSpendingandCutWithdrawalsIfDrawdown.PortfolioDCF.plot_forecast_monte_carlo()no longer mutatesPortfolioDCF.cashflow_parameters.MonteCarlodistribution fitting and plotting: float dtype handling and QQ/histogram compatibility.EF._get_gmv_monthlyreturns geometric mean (not arithmetic).- First
Portfolio.rorvalue was lost when rebalancing. pandascompatibility forDataFrame.applymap(),pd.concat(..., copy=...), and updatedpd.Grouperfrequency aliases.
- Updated notebooks: 04 investment portfolios with DCF.ipynb, 10 forecasting.ipynb, 11 rebalancing portfolio.ipynb.
- Read the Docs navigation and API pages refreshed for
okama.search()andokama.symbols_in_namespace(). - README: added GitHub and pepy.tech download badges and refreshed the project roadmap.
- Migrated linting and formatting from
flake8+blacktoruff check, including GitHub workflows and contributor instructions inAGENTS.md. - Added
.pre-commit-config.yamlwith ruff hooks. - Added top-level
requirements.txtmirroring runtime dependencies frompyproject.toml. - Added this
CHANGELOG.mdfollowing Keep a Changelog and Semantic Versioning.
Major release focused on a rewritten efficient frontier, extended Monte Carlo engine, and richer DCF cash flow strategies.
- New
EfficientFrontierbased onEfficientFrontierReb, using fast vectorizedRebalance.wealth_ts_ef/return_ror_ts_efmethods. EfficientFrontierReb.get_tangency_portfolio()andplot_cml().- Most Diversified Portfolio (MDP) in
EfficientFrontierReb. MonteCarloclass: Monte Carlo methods moved fromPortfoliointo a dedicated class; parameters fornorm,lognorm, and Student's t distributions; all distribution calculations consolidated intails.py.- New cash flow strategy
CutWithdrawalsIfDrawdown(CWD, formerly CWID) andVanguardDynamicSpending(VDS) extensions (adjust_min_max,adjust_floor_ceiling, negative-percentage guard). ListMaker.period_lengthprecision improvements; consistent use ofAsset.first_date/Asset.last_date.- HTTPS for the okama API with SSL verification.
- New
Portfolio.okamaio_linkURL format with full rebalancing-strategy parameters. - Public
okama.search(),okama.symbols_in_namespace(), andokama.namespaceswith docstrings and Read the Docs pages.
PortfolioDCF.find_the_largest_withdrawals_size()refactored; no longer mutatesCashFlowparameters.- Rebalancing sped up by moving
pd.concatout of the inner loop. - Mean return for
Portfoliousesror.mean(). - CWID renamed to CWD across cash flow strategies.
- DCF discount rate for short histories (< 12 months).
Portfolio.dcf.wealth_index(discounting="pv")correctness.mc._get_params_for_lognormal,_target_cagr_range_left, and several VDS floor/ceiling and min/max edge cases.- Pandas
FutureWarningonconcatwith empty DataFrame during rebalancing. - Annual arithmetic mean return compounding.
- Feature and maintenance release preceding the 2.0 rewrite of the efficient frontier and Monte Carlo subsystems.
First stable release.