fincanva
Build, backtest and analyse Fincanva strategies from Python, with your own Fincanva account.
pip install "fincanva[all]" # with pandas and matplotlib; plain `fincanva` works without them
import matplotlib.pyplot as plt
import fincanva as fc
fc.connect() # reads FINCANVA_API_KEY; never writes the key anywhere
fc.connect("fk_...") # or the key itself
fc.connect(key_file="~/fincanva-api-key.txt") # or a text file that holds the key alone
sim = fc.simulate("66f1c0a2b3d4e5f60718293b", start=2008) # a saved strategy of yours, or a public one
print(sim.headline.strategy.cagr, sim.headline.strategy.sharpe)
ax = sim.equity().plot(title=sim.strategy_name) # growth since the start: strategy beside benchmark
sim.regimes().shade(ax) # the risk-off periods in grey
plt.show()
sim.monthly_returns().loc["2020"] # one row per month
sim.metrics().to_frame() # every metric, strategy beside benchmark
sim.stress(volatility_multiplier=2.0).to_frame() # the risk under a harsher view
sim.cone(horizon_days=1260) # a Monte Carlo cone, five years ahead
sim.trust().intervals() # bootstrap intervals of return, Sharpe, drawdown
sim.positions() # what it holds today
assets = ["SPY", "TLT", "GLD"]
w = fc.allocation.weights(fc.allocation.RiskParity(), assets) # today's weights by a method of the catalogue
w.weights, w.as_of # by permaTicker, and the date they are for
fc.allocation.efficient_frontier(assets, allow_short=False).plot()
fc.allocation.walk_forward(fc.allocation.InverseVolatility(), assets, start=2015, rebalance_months=3).metrics.sharpe
fc.estimators.estimate(assets, fc.estimators.correlation.SpearmanOnReturns).matrix
r = fc.risk.regime(fc.risk.presets.Trend200) # risk-on or risk-off now, and since when
r.state, r.since, r.history
fc.raw # every API operation as a typed function, one to one
fc.allocation # the allocation methods, one class each
fc.risk.presets # the risk condition presets
fc.filters # the screening filters, grouped as in the app
fc.estimators # the covariance and correlation estimators
try:
fc.allocation.walk_forward("risk_parity", assets, start=1990)
except fc.LicenceRefused as refused: # raised only when a request reaches the protected long-run history
refused.first_allowed_dates # by permaTicker: the first date to ask again from (None: leave it out)
fc.connect() calls Fincanva at https://app.fincanva.com. To call another address, pass it as
fc.connect(base_url="https://...") or set FINCANVA_BASE_URL: the origin alone, or the origin followed by
/api/v1/research. The address must use https (plain http is accepted for localhost only), so the key never
travels unencrypted.
Python 3.10 or later. Without pandas, the typed results (sim.headline, sim.metrics(), sim.trust(),
sim.stress(), sim.regimes()) work as they are, and a method that returns a DataFrame raises
fc.MissingDependency, naming the extra to install, before it spends anything. The research results
(w.weights, r.history, ...) answer a dict or a list instead of a pandas object.
The designed surface
Built on fc.raw alone: every method calls one API operation through it, never a transport of its own. Every number
is the API's own; a DataFrame reshapes an answer and never computes a figure.
| Call | Returns | API operation |
|---|---|---|
fc.simulate(strategy_id, *, start, benchmark, base_currency, starting_capital, variant) |
Simulation |
simulate_strategy |
fc.Simulation(simulation_id, *, variant), fc.Simulation.from_answer(answer) |
Simulation |
none |
fc.Variant(costs=False, reinvest=True, taxes=False); sim.with_variant(costs=True) |
the money-management variant | none |
sim.headline, sim.strategy_name, sim.start_year, sim.benchmark_name, ... |
typed, from the run | none |
sim.metrics() |
results.Metrics: typed; .to_frame(), .by_year() |
get_simulation_metrics |
sim.equity(), sim.drawdown(), sim.equity_series(part, *, view, inflation_adjusted) |
DataFrame / Series by date | get_simulation_equity |
sim.monthly_returns(), sim.yearly_returns() |
DataFrame by month / year | get_simulation_monthly_returns |
sim.allocations(measure, *, sub_strategies) |
DataFrame by date, one column per sub-strategy | get_simulation_allocations |
sim.positions(on, *, capital), sim.target_positions(...), sim.orders(...) |
DataFrame | get_simulation_positions |
sim.regimes() |
results.Regimes: typed; .periods(), .to_frame(), .shade(ax) |
get_regime_timeline |
sim.stress(*, annual_return_view, volatility_multiplier) |
results.StressTest: typed; .to_frame() |
run_stress_test |
sim.cone(*, horizon_days, method, scenarios) |
DataFrame by day: p5 to p95 |
get_projection_cone |
sim.trust(*, method, resamples) |
results.Trust: typed; .intervals(), .walk_forward() |
get_backtest_trust |
sim.correlations(*, year), sim.rolling_correlation(a, b) |
DataFrame of pairs / Series by date | get_simulation_correlations, get_rolling_correlation |
Every read is made on the simulation's own variant, so one object's results agree, and is kept on the object: asking again spends nothing. Positions are always the base variant's, as the API reports them.
The allocation methods and the risk-on/off rules, called from the catalogue each choice is made from:
| Call | Returns | API operation |
|---|---|---|
fc.allocation.weights(method, assets, *, as_of, lookback_months, covariance_estimator, correlation_estimator) |
results.Weights: .weights by permaTicker, .as_of, .instruments, .to_frame() |
compute_weights |
fc.allocation.combined_weights(method, strategy_id, *, as_of, lookback_months, covariance_estimator, timeout) |
results.CombinedWeights: .weights by sub-strategy id, .rotation_date, .profile |
compute_combined_weights |
fc.allocation.efficient_frontier(assets, *, as_of, lookback_months, covariance_estimator, allow_short, min_weight, max_weight, limit_weights, points, place) |
results.Frontier: .points, .minimum_volatility, .maximum_sharpe, .maximum_return, .placed, .plot(ax) |
efficient_frontier |
fc.allocation.walk_forward(method, assets, *, start, as_of, rebalance_months, lookback_months, covariance_estimator, correlation_estimator, variant, timeout) |
results.WalkForward: .metrics, .years, .walk_forward_years, .rotations |
walk_forward |
fc.estimators.estimate(assets, estimator, *, matrix, as_of, lookback_months) |
results.Estimate: .matrix by permaTicker, .kind, .estimator |
estimate_covariance |
fc.risk.regime(rule, *, start, as_of, strategy_id, timeout) |
results.RuleRegime: .state, .since, .history, .periods(), .shade(ax) |
compute_regime |
method is an entry of fc.allocation with its settings (fc.allocation.MeanVariance(objective="minimum_volatility")),
the entry alone, or its id; its settings are checked against the level the call uses (inside a strategy, or a
Combined's) before anything is sent. An estimator of fc.estimators.covariance or fc.estimators.correlation carries
its own settings (ExponentiallyWeighted(decay_factor=0.97)) and chooses the matrix. rule is a preset of
fc.risk.presets, a preset's id, or a rule of a type as create_strategy takes a risk condition. Each result keeps the
operation's typed result as .result, beside .summary and .assumed.
Your library and your real holdings (K), batches, pipelines and long runs (P), strategy discovery (S), explanations (X), your own series (U) and Fin Research (L), each its own module:
| Call | Returns | API operation |
|---|---|---|
fc.library.strategies(*, limit, offset), .live_strategies(...), .screeners(...) |
library.Listing: the typed entries; .total, .next_offset, .allowance, .to_frame() |
list_my_strategies, list_my_live_strategies, list_my_screeners |
fc.library.strategy(strategy_id), fc.library.screener(screener_id) |
the saved item in full, .definition as create_strategy / create_screener take it |
get_my_strategy, get_my_screener |
fc.review.positions_to_weights(positions, *, cash, as_of, base_currency) |
review.PositionWeights: .weights by permaTicker, .total_value, .stale, .to_frame() |
review_strategy |
fc.review.rebalance_plan(current, target, *, current_cash, target_cash, capital, base_currency, minimum_trade_weight, slippage, transaction_cost) |
review.RebalancePlan: .trades, .dropped_trades, .turnover, .estimated_costs, .post_trade_weights |
review_strategy |
fc.review.live_trades(*, on, since, strategy_ids) |
review.LiveTrades: .strategies, .refused, .to_frame() |
review_strategy |
fc.review.health_check(simulation, *, as_of, window_months, factors, regime_series, episodes, wait, timeout) |
review.HealthCheck: .priorities, .not_computed, .episodes(), every section typed |
review_strategy |
fc.Job(job_id): .status(), .wait(timeout), .cancel(), .answer(); fc.jobs.cancel(job_id) |
jobs.JobStatus; the run's designed result; jobs.Cancellation |
get_result, cancel_job |
fc.batch(calls, *, wait, timeout) with fc.Call(operation, **arguments) |
batches.Batch: one BatchItem per call (.result, .answer(), .error, .job, .get()), .errors, .counts |
run_batch |
fc.pipeline({step_id: fc.Call(...)}, *, wait, timeout) with fc.from_step(step_id, pointer) |
batches.Pipeline: each step by id, as BatchItem |
run_pipeline |
fc.research_guide(*, query, family, tool, method) |
batches.Guide: .scenarios, .tool |
get_research_guide |
fc.discovery.discover_strategy(strategy_id, space, *, validation, folds, fold_years, objective, constraints, max_trials, ...) with Choices, Range, Alternatives |
discovery.Discovery: .shortlist, .best, .pbo, .multiple_testing, .to_frame(), .trials() |
discover |
fc.discovery.discover_risk_rule(...), .robust_parameter_region(...), .improve_strategy(...) |
discovery.Discovery: also .baseline, .region, .robust_centre, .proposals |
discover |
fc.discovery.trials(discovery_id, *, limit, offset) |
discovery.DiscoveryTrials: .trials, .to_frame(), .next_page() |
discover |
fc.explain.performance(simulation, *, year, by_sub_strategy, by_asset, by_regime, by_factor, factors, drag, variant) |
explain.PerformanceExplanation: .sub_strategies(), .assets(), .regimes(), .factors(), .drag |
explain |
fc.explain.trade(simulation, asset, on, *, variant) |
explain.TradeExplanation: .position, .entry, .regime, .selection |
explain |
fc.explain.what_if(strategy_id, changed_strategy_id, *, start, variant, wait, timeout) |
explain.WhatIf: .to_frame(), .years(), .relative_growth |
explain |
fc.my_series.upload(name, points, *, kind), .list(), .delete(series) |
my_series.UserSeries: .reference (user:u_..., where a method names a series); SeriesList; the id deleted |
my_series |
fc.fin_research.start(question, *, strategy_ids, screener_ids, language); ResearchSession(session_id): .get(), .wait(), .answer(), .approve(), .edit_plan(), .redirect(), .follow_up(), .cancel(), .report(), .refresh(), .schedule(), .delete(); fc.fin_research.sessions(), .schedules(), .delete_schedule() |
fin_research.ResearchSession, Report (.text() shows each cited figure as its display), Schedule |
research |
Market research, one module per family: valuation, fundamentals, the universe of instruments, screener runs and options.
Each function calls one API operation, a method or a mode of it, and each result keeps the operation's typed result as
.result (every field the API answered) with named views, as the research results above do; a family's result classes
are named on its module (fc.valuation.ReverseDcf). An argument left out is not sent: the API applies its default.
| Call | Returns | API operation |
|---|---|---|
fc.valuation.reverse_dcf(asset, *, as_of, valuation_method, horizon_years, record_years, history_window_years, change_months, peer_group, discount_basis, discount_rate, terminal_growth) |
ReverseDcf: .implied_growth, .implied_return, .excess_return_over_capm, .peer_percentile, .peer_gap (each a Figure(value, reason)), .figures, .to_frame() |
value_company, reverse_dcf |
fc.valuation.dcf_valuation(asset, *, stage1_growth, ...) |
DcfValuation: .scenarios (bear, base, bull, yours), .scenario(name), .to_frame() |
value_company, dcf_valuation |
fc.valuation.dcf_sensitivity(asset, *, discount_rates, terminal_growths, stage1_growth, ...) |
DcfSensitivity: .grid (rates by growths), .value_at(rate, growth) |
value_company, dcf_sensitivity |
fc.valuation.implied_growth_history(asset, *, history_years, ...) |
ImpliedGrowthHistory: .implied, .realized by month-end, .to_frame() |
value_company, implied_growth_history |
fc.fundamentals.report(asset, *, as_of) |
Fundamentals: .filed, .income, .balance_sheet, .cash_flow, .ratios, ..., .to_frame() |
analyze_fundamentals, get_fundamentals |
fc.fundamentals.trends(asset, *, start, as_of) |
FinancialTrends: .years, .growth, .streaks |
analyze_fundamentals, financial_trends |
fc.fundamentals.metric_history(asset, field, *, filter, start, as_of) |
MetricHistory: .series, .latest (percentile, z-score) |
analyze_fundamentals, get_metric_history |
fc.fundamentals.valuation_vs_peers(asset, filter, *, start, as_of) |
PeerValuation: .to_frame() (the stock, its sector, its market, its index), .latest |
analyze_fundamentals, valuation_vs_peers |
fc.fundamentals.market_valuation(*, start, as_of) |
MarketValuation: .to_frame() (Shiller P/E, Buffett indicator, its deviation), .latest |
analyze_fundamentals, market_valuation |
fc.universe.find(queries, *, asset_class, region, currency, ucits, accumulating, candidates) |
Found: .resolved, .unresolved (never guessed), .handle, .to_frame() |
find_assets |
fc.universe.coverage(assets, *, datasets, fields) |
Coverage: .span(asset, dataset), .unknown, .to_frame() |
get_data_coverage |
fc.universe.describe(asset) |
AssetDescription: .name, .type, .sector, .size_tier, .statistics, .availability |
explore_universe, describe_asset |
fc.universe.universes(*, as_of) |
Universes: .universes, .indices, .sectors |
explore_universe, list_universes |
fc.universe.index_members(index) |
IndexMembers: .date, .perma_tickers, .to_frame() (today's membership only) |
explore_universe, get_index_members |
fc.universe.search_by_exposure(exposure, *, region, accumulating, as_of) |
Exposures: .default_funds, .to_frame() |
explore_universe, search_by_exposure |
fc.universe.datasets() |
Datasets: .version, .fields(dataset), .to_frame() |
explore_universe, list_datasets |
fc.screens.screen(screener, *, limit, timeout) |
Screen: .picks, .rows, .emptied_by, .handle, .to_frame() |
run_screen, screen_assets |
fc.screens.backtest(screener, *, timeout, wait) |
ScreenBacktest: .horizons, .path, .row_impacts() |
run_screen, backtest_screen |
fc.screens.improve(screener, *, timeout, wait), fc.screens.suggest(universe, *, name, timeout, wait) |
ScreenSuggestion: .robust, .outperformance_score, .trials_evaluated, .screener_id, .problems |
run_screen, improve_screen / suggest_screen |
fc.screens.membership_changes(screener, since, *, as_of, timeout) |
MembershipChanges: .entered, .left, exact counts |
run_screen, screen_membership_changes |
fc.screens.collect(run_id, *, timeout); wait=False answers a fc.Job |
the run's result | get_result |
fc.options.price(right, *, underlying_price, strike, days_to_expiry, volatility, model, rate, dividend_yield, dividends) (model and rate required by every calculator) |
OptionPrice: .price, .greeks, .units |
price_options, price_option |
fc.options.implied_volatility(right, *, ..., option_price, model, rate) |
ImpliedVolatility: .implied_volatility, .reason, .greeks |
price_options, implied_volatility |
fc.options.payoff(legs, *, underlying_price, model, rate, ...) with fc.options.Leg(...) |
StrategyPayoff: .profit, .breakevens, .max_profit, .max_loss, .probability_of_profit, .plot(ax) |
price_options, strategy_payoff |
fc.options.expiries(underlying, *, as_of), fc.options.chain(underlying, expiry, *, greeks, min_abs_delta, root, as_of) |
Expiries; OptionChain: .to_frame(), .calls(), .puts() |
analyze_options, get_option_expiries / get_option_chain |
fc.options.term_structure(underlying, ...), fc.options.skew(underlying, ...) |
TermStructure, Skew: .horizons, .expiries, .history |
analyze_options, volatility_term_structure / volatility_skew |
fc.options.implied_vs_realized(underlying, ...) |
ImpliedVsRealized: .iv30, .iv_rank30, .iv_rv_spread, .realised, .vrp, .history |
analyze_options, implied_vs_realized |
fc.options.expected_move(underlying, to, *, measure, as_of) |
ExpectedMove: .expected_move_pct, .target_date, .reason |
analyze_options, expected_move |
fc.options.positioning(underlying, *, profile, expiry, band_width, history_from) |
Positioning: .pc_volume, .max_pain_strike, .gex_total, .walls, .bands, .history |
analyze_options, options_positioning |
fc.options.earnings_history(underlying, *, last_events, pair_type) |
EarningsHistory: .events, .aggregates |
analyze_options, earnings_options_history |
backtest, improve and suggest are long runs, waited for like a simulation (a screener run is never cancelled); improve and suggest save what they
find as a new screener and are refused to an app set to read-only (ToolsetDisabled). A figure that does not apply is
None with its reason, never 0: a valuation that does not apply (.reason), reverse_dcf's peer figures while the peers
are not read (peer_group_too_small), an implied volatility no price gives (no_volatility_reprices_it).
The analysis families, one module each over one API operation that takes the method as method (each function sends
one method; the API operation and method are named in its documentation):
| Call | Returns | API operation, method |
|---|---|---|
fc.instruments.analyze(assets, *, start, benchmark, variant, as_of, years, sortino, factor_betas) |
Backtests: .metrics, .years(), .factor_betas() |
analyze_instruments, analyze_assets |
fc.instruments.compare(assets, rank_on, *, start, order, ...) |
Ranking: .rows, .best |
compare_assets |
fc.instruments.performance_history(assets, *, start, growth, real, drawdown, ...) |
PerformanceHistory: .growth(), .drawdown() by date |
get_performance_history |
fc.instruments.rolling(assets, statistic, *, start, factor, rolling_months, window_years, ...) |
RollingStatistics: .lines(), .window_outcomes(), .aggregates() |
rolling_statistics |
fc.instruments.calendar_returns(assets, *, start, ...) |
CalendarReturns: .monthly() by month, .years() |
calendar_returns |
fc.instruments.drawdowns(assets, *, start, min_depth, top, ...) |
Drawdowns: .episodes(), .lines(), .max_drawdown |
drawdown_analysis |
fc.instruments.return_distribution(assets, *, start, monthly, bins, ...) |
ReturnDistribution: .summary(), .histogram() |
return_distribution |
fc.instruments.seasonality(assets, *, start, day_of_week, turn_of_month, ...) |
Seasonality: .month_of_year(), .day_of_week(), .turn_of_month() |
seasonality |
fc.instruments.event_study(assets, *, benchmark, start, events, events_series, earnings, ...) |
EventStudy: .car(), .path(), .events(), .skipped() |
event_study |
fc.relationships.betas(assets, against, *, lookback_months, as_of) |
Betas: .betas, .to_frame() |
analyze_relationships, beta_analysis |
fc.relationships.expected_returns(assets, *, lookback_months, black_litterman, momentum_months, view_confidence, covariance_estimator) |
ExpectedReturns: .annualized |
estimate_expected_returns |
fc.relationships.factor_exposure(assets, *, start, year, benchmark, variant) |
FactorExposure: .factors() |
factor_exposure |
fc.relationships.factor_regression(assets, *, simulation, start, factors, monthly, rolling_months, ...) |
FactorRegression: .betas(), .fit(), .rolling() |
factor_regression |
fc.relationships.pairs(first, second, *, lookback_months, johansen, z_score_window_days) |
Pair: .cointegrated, .hedge_ratio, .half_life_days, .z_score |
pairs_analysis |
fc.relationships.risk_contributions(weights, *, lookback_months, covariance_estimator) |
RiskContributions: .volatility, .risk_shares |
risk_contributions |
fc.relationships.correlation_matrix(assets, *, lookback_months, estimator, clustered) |
CorrelationMatrix: .matrix in its clustered order |
correlation_matrix |
fc.relationships.regime_behaviour(rule, assets, *, simulation, start, ...) |
RegimeBehaviour: .states(), .state, .time_in_regime |
asset_regime_behaviour |
fc.macro.series(kind, *, asset, start, as_of) |
MacroSeries: .points by date, .latest |
analyze_macro, get_macro_series |
fc.macro.snapshot(*, as_of, start) |
MacroSnapshot: .latest() by kind |
macro_snapshot |
fc.macro.sensitivity(assets, *, start, series, columns, monthly, benchmark, variant) |
MacroSensitivity: .betas(), .fit() or .columns() |
macro_sensitivity |
fc.scenarios.crisis_replay(assets, *, start, episodes, ...) |
CrisisReplay: .replay(), .episodes |
run_scenario, crisis_replay |
fc.scenarios.regime_projection(assets, *, start, horizon_days, scenarios, method, state_count, regime_series, ...) |
RegimeProjection: .cone(), .regime |
regime_conditional_projection |
fc.scenarios.macro_scenario(assets, shocks, *, start, horizon_months, monthly, ...) |
MacroScenario: .impacts(), .betas() |
macro_scenario |
fc.scenarios.historical_analogues(assets, *, start, features, horizon_months, count, minimum_gap_months, ...) |
HistoricalAnalogues: .analogues(), .following_returns(), .today |
historical_analogues |
fc.robustness.cone(returns, *, horizon_days, scenarios, method) |
SeriesCone: .bands by day |
analyse_return_series, projection_cone_on_series |
fc.robustness.trust(returns, *, risk_free_rate, resamples, method) |
SeriesTrust: .intervals() |
backtest_trust_on_series |
fc.robustness.deflated_sharpe(sharpe, trial_sharpes, *, observations, skewness, kurtosis) |
DeflatedSharpe: .probability |
deflated_sharpe |
fc.robustness.sweep(strategy_id, axis, *, rank_by, start, variant) |
Sweep: .cells(), .control, .best |
stress_strategy, sweep |
fc.robustness.check_overfitting(strategy_id, axis, *, start, variant) |
OverfittingCheck: .trials(), .best |
check_overfitting |
fc.robustness.stability_report(strategy_id, axis, *, rank_by, slicing, slice_years, step_years, minimum_era_days, ...) |
StabilityReport: .stability(), .ranks() |
get_stability_report |
Every figure is an instrument's one-instrument backtest, a window of prices, a macro series or the numbers you give,
as the engine answers it. A method that computes backtests (analyze, drawdowns, return_distribution,
seasonality, event_study, factor_exposure, factor_regression, regime_behaviour, the regression form of
macro.sensitivity, every scenario, every sweep) is a long run: it waits up to timeout seconds, then raises
RunStillRunning, whose run_id the family's collect(run_id) or fc.Job(run_id) turns into the same typed result
later. compare, performance_history, rolling and calendar_returns read backtests
analyze computed, with the same instruments, start, benchmark and variant, and are refused NotFoundError before.
variant is a fc.Variant. Each result keeps the method's typed result as .result; its views answer pandas objects
when pandas is installed and plain Python otherwise, and to_frame() always needs pandas.
Every long run is one fc.Job. A call that may be a long run (a simulation, health_check, a search, a what-if, a
batch or a pipeline the request cannot finish, a screener's backtest or search, a method computed from backtests)
waits for it by default, up to timeout seconds; where it takes wait=False, it returns a fc.Job at once instead
(job.wait(), job.cancel()), already done when the API answered at once. A wait that runs out raises
RunStillRunning: the run goes on, and fc.Job(error.run_id).wait() picks it up and finishes as the same designed
result, whichever family started it. Following and cancelling a run are free; job.cancel() stops a batch, a
discovery, a walk-forward, a sweep or a method computed from backtests, and refuses the rest. A batch item that is
itself a long run holds its own Job. A Fin Research session is not a run: when session.wait() runs out, the
RunStillRunning it raises has no run_id, and fc.fin_research.ResearchSession(session_id) reads the session again.
The errors the API answers each have a class under fc.FincanvaError: AuthenticationError, ValidationError,
PlanLimitError (the plan, an allowance or the per-minute cap), ToolsetDisabled (the user's settings for a connected
app: .family, .read_only), LicenceRefused (data that may not leave Fincanva: for the protected long-run history,
.first_allowed_dates by permaTicker; otherwise .dataset, .licence_class, .surface), NotFoundError,
SavedStrategyChanged, RunFailed, RunStillRunning, ServiceUnavailable.
Design notes
- Install extras.
fincanva[pandas](the DataFrames),fincanva[plot](matplotlib, forRegimes.shade()without an axes),fincanva[all]. The core needs httpx and pydantic only.import fincanvanever imports pandas, matplotlib or the generated models, and neither does importing a catalogue. - Typed results are the generated models.
fincanva.resultssubclasses the generated result model of each simulation answer and adds views, so the fields, names and documentation arefc.raw's, and a field the API adds appears in both at once. A research result keeps that model as.resultinstead, because its named views (.weights,.matrix,.history) answer a pandas object or plain Python where the model has a list. - No instrument as a benchmark yet.
benchmark=takes"strategy","none"or a strategy id, as the API does;fc.simulate(..., benchmark="ACWI")is refused before anything is sent. - No "excluded" list.
compute_weightsanswers no entry for an instrument with no price on the date and states no list of what it left out;Weightsshows what the API answered and never infers the rest. - Every other operation: through
fc.raw. The designed surface above covers every family of the API but three groups, which are infc.rawonly, one typed function each, with the operation's own arguments and typed result: building and saving strategies (create_strategy,edit_strategy,replace_strategy,validate_strategy,explain_strategy,describe_strategy_model; a designedfc.Strategyis not in this version), building and saving screeners (create_screener,edit_screener,replace_screener,validate_screener,explain_screener,describe_screen_filter), and more reads on simulations (compare_simulations,get_contribution_analysis,get_simulation_trade_history,get_start_date_sensitivity,get_strategies_analytics,get_strategies_returns,get_rotation_dates,get_todays_rotations). The four listings (list_allocation_methods,list_covariance_estimators,list_risk_rule_presets,list_screen_filters) are what the cataloguesfc.allocation,fc.estimators,fc.risk.presetsandfc.filterswere recorded from;fc.rawasks them live.
Metadata
Release files for fincanva 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| fincanva-1.0.0.tar.gz | 437.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fincanva-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / fincanva-1.0.0.tar.gz
| Download URL | fincanva-1.0.0.tar.gz |
|---|---|
| Size | 437.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5240e4482884e2eaacf62595c2e648707174f93fac5f1a799df281dd010db479
|
|
BLAKE2b-256 checksum How to use checksums |
0a406fd8d4c04a8069f12b3728fec0d10ca1316d8512059e2cf7174296bf79d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / fincanva-1.0.0-py3-none-any.whl
| Download URL | fincanva-1.0.0-py3-none-any.whl |
|---|---|
| Size | 572.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a38006b8b6b21a7038fdafd866f0cde51573cc2edf6bf9e427342bbfe4595f56
|
|
BLAKE2b-256 checksum How to use checksums |
3218c99f094950ee3d65ba54337f3c9ba19691f24d77972463e603a094399727
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for fincanva 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fincanva-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.