ledgr 0.2.2
Upgrading from 0.2.1.0
ledgr 0.2.2 makes long-window, point-in-time portfolio research a complete public workflow. Availability-aware runs now share one causal history substrate across runs, sweeps and walk-forward evaluation; strategies can read identified history matrices or frames, declare readiness, use point-in-time facts and corporate actions, and return targets through the existing risk, execution and ledger pipeline. Declarative schedules move recurring decisions outside callbacks, and the new minimum-variance article demonstrates the intended long-only research path end to end.
This is a current-only, pre-CRAN release. Rebuild snapshots, experiment stores, saved sweeps and walk-forward artifacts created by older ledgr versions; no migration or compatibility reader is provided. Current persistent identities include experiment-store schema 124, snapshot hash rule 4, run identity rule 4, valuation and corporate-action policy schema 2, saved-sweep format 6, retention format 2, provenance and reproduction-key format 2, walk-forward candidate and inspection format 2, and promotion-context format 3.
Install DuckDB 2.0.0 or later. The independently normalized schema-124 catalogue is unchanged from DuckDB 1.5.6, but v0.2.2 uses 2.0.0 as its tested and declared dependency floor.
Public changes that require source edits include:
- replace
ledgr_param_grid()with an ordinary data frame,ledgr_strategy_grid(), or the feature/strategy grid helpers; - remove
persist_featuresand calls to the removedledgr_feature_cache_clear(); features are computed on the uncached path and are not persisted; - replace the removed
fill_modelargument withtiming_modeland a classedcost_model, and replace thetypealias ofas_tibble.ledgr_backtest()withwhat; - use
ledgr_experiment()andledgr_run()for snapshots with point-in-time availability facts;ledgr_backtest()remains dense-only.
Important boundaries remain explicit. Strategy schedules currently require one pulse per declared open session. The estimator example is long-only; this release does not claim shorting, leverage, market-neutral construction, model lifecycle, intraday schedules, or paper/live execution. Terminal holdings without complete settlement evidence follow the declared corporate-action and valuation policies: ledgr either refuses or records the chosen write-off, haircut, qualifying-mark, or frozen-value assumption.
Halts and untraded sessions that a data source encodes as zero-volume bars carrying the previous close, as the reference Sharadar data does, are still read as observed prices: they can fill and they bypass valuation ageing and freezing.
Parallel sweeps trade time for memory because each local worker receives its own prepared history input. The retained long-window workload peaked at 1.89 GiB for one run and 4.46 GiB for the two-worker tree. These are workload- specific observations, not a general memory bound; use workers = 1L when memory is the tighter constraint.
Declarative strategy schedules
Added
ledgr_strategy_schedule()with declarativeledgr_schedule_month_start(),ledgr_schedule_month_end(),ledgr_schedule_week_start(),ledgr_schedule_quarter_start(),ledgr_schedule_year_start(),ledgr_schedule_weekday(),ledgr_schedule_month_day(),ledgr_schedule_dates(), andledgr_schedule_from_param()rules. Weekday and month-day anchors move only to the next open session inside the same civil week or month. Rules compile once against the declared session calendar before execution.Scheduled strategies now skip only decision-context construction, callback invocation, and execution planning on non-decision pulses. Observation, corporate-action settlement and boundary binding, reconciliation, strategy state persistence, interruption, checkpointing, and commit processing still run on every pulse. Skips are recorded as
scheduled_skip, distinct from an explicit strategy hold. A flat run whose resolved schedule has no decision pulse refuses before fold entry; an opening or carried portfolio may still run and hold through the range.Experiment-store schema 124 persists the canonical schedule declaration and hash together with decision-pulse and scheduled-skip counts. Experiment and run output disclose the schedule, while run output and summaries disclose the counts.
ledgr_run_list()exposes the physicalschedule_json,schedule_hash,decision_pulse_count, andscheduled_skip_countcolumns; run information verifies them against the canonical configuration. Schema-123 stores are older and refuse; rebuild them with the current package. Current scheduler-aware research artifacts use saved-sweep format 6, provenance format 2, reproduction-key format 2, walk-forward candidate and inspection formats 2, and promotion-context format 3; older formats refuse.
Cross-asset correctness and honest defaults
Fractional fills that close FIFO lots within the existing machine-epsilon dust tolerance now normalize the matching canonical position to exact zero at the accounting transition. Literal fill and ledger quantities, cash, fees, realized P&L, event order, hashes, and identities are unchanged.
An omitted metric calendar still uses the 252-session US-equity daily convention, but experiment plans and experiment, run, and summary output now disclose its display label as
(default). Explicit US-equity and crypto calendars remain unmarked; the display label stays outside identity.Public result output now uses the asset-neutral
Economic eventsheadline for the existing four corporate-action fidelity states. The API and event support are unchanged; this does not add token, derivative, funding, tax, or other event families.
Rectangular sweep inputs
-
ledgr_sweep()now accepts an ordinary rectangular data frame or tibble as a strategy grid, with one candidate per row. Strategy and feature parameter namespaces remain separate, and sweep output prints their readable summaries before the candidate hash. The pre-releaseledgr_param_grid()compatibility constructor has been removed; use a data frame,ledgr_strategy_grid(), or the feature/strategy grid composition helpers.
Optional strategy intent evidence
- A structured strategy result may include
intent_reason, one non-empty character scalar. ledgr records it once for that invoked pulse inledgr_results(run, "diagnostics"); availability-awareledgr_run_explain()joins the note to its instrument rows. Omitting it preserves the existing result and writes no extra diagnostic. This is a narrow portfolio-intent note, not a general factor or prediction store.
A long-only minimum-variance strategy
- New article
vignette("minimum-variance", package = "ledgr"): a long-only minimum-variance strategy that decides on the first session of each month, reads one identified return matrix with recorded panel readiness, solves the constrained problem withquadprog::solve.QP(), aligns the unnamed solution without reconstructing IDs, turns it into full share targets, and records only the two decision notesinsufficient_sampleandrebalanced.quadprogis a new suggested package. The causal-history article’s stand-in solver is no longer described as long-only minimum variance.
Corporate-action disclosure
Refused corporate-action facts (
complete = FALSE) are now visible in results.ledgr_corporate_action_summary()gainssealed_refusal_counts(every sealed refused fact by reason) andrefusal_entitled_quantity, and itsrefusal_countsnow counts the refused facts whose parent the run held at entitlement;print()andsummary()show both counts. A held refused fact takes the unsupported-quantity route: under the research preset it counts inunsupported_factsand fidelity readsUNSUPPORTED; under the strict preset an availability-aware run stopsINCOMPLETEwithcorporate_action_quantity_refusedand a dense run raisesledgr_corporate_action_quantity_unsupported. Previously such a fact was silent. Theunsupported_quantitypolicy identities move tov002.A corporate action whose knowledge time ledgr fills known-when-effective now records its knowledge as
assumed(reasonknowledge_assume_effective) instead ofevidenced, and results of an availability-aware run listequity_corporate_actionsamong their assumption-backed facts. A supplied knowledge time staysevidenced. The fact IDs and family hash of such rows change. Older experiment stores refuse withledgr_store_rebuild_required; rebuild them.A
carry_test_statewalk-forward no longer stops withledgr_invalid_pulse_contextwhen two or more corporate-action cash postings are pending at a fold boundary; the carried opening checks every posting instant at once. Inledgr_corporate_action_summary(), theunsupported_quantity.report_onlyexercised-choice count now counts each held fact on the unsupported-quantity route, refused facts included, instead of reading 0.
Fact pipeline follow-ups
Fact construction is faster at scale with unchanged rows, fact IDs and hashes: correction lineage, trading-status validation and Date resolution now grow linearly with fact rows (for example 0.15 s instead of 83 s to validate 120,000 corrected status rows).
Date and POSIXct clocks on one row are compared on the session instants the Dates resolve to, not on UTC midnight. A Date entitlement whose session close falls after a same-day POSIXct effective time now refuses (
ledgr_fact_invalid_clock_order); a POSIXct start before a Date interval end’s session open is now accepted. Such Date-dependent refusals are raised byledgr_facts()rather than by the family constructor.A corporate action without a knowledge time is known when its event becomes effective: a Date event at its session’s open, a POSIXct event at its instant. A supplied knowledge time is always kept, for corporate actions and, under
knowledge = "assume_effective", for membership, trading-status and lifetime facts; it was previously replaced by a session open when the event or effective time was a Date.A complete ordinary cash dividend, cash, stock or mixed acquisition or spin-off with an effective time but no entitlement time now refuses at the pulse it would act on with
ledgr_corporate_action_entitlement_missing, in single runs, sweeps, the carried walk-forward chain and dense runs. Supply the entitlement time.A fact family edited after construction is refused whatever else its bundle holds, including an edited sessions family that a Date family resolves against. Membership-snapshot headers are ordered by resolved times.
Causal history foundation
Ordinary causal-history matrices and wide frames are now direct sibling manifestations of the verified run substrate. They retain the ordered axis, selection and policy provenance, but no longer build cell-evidence planes or hash transient values; request
layout = "long"for cell diagnostics. Metadata is provenance for the originating read, not authentication of values after an attribute-preserving R transformation.Availability-aware runs, sweeps and walk-forward sessions now prepare one immutable causal-history substrate from the snapshot’s first session. It owns the common venue axis, one bar representation, separate observation and knowledge clocks, bounded fact segments and consumer-local cursors. Dense execution is unchanged.
History treatment keeps raw missingness, expectedness, bounded price-input carry, calculation validity, optional output carry, readiness and estimator admission as separate evidence. Price carry is causal, field-local, never applies to volume and cannot cross a lifetime or terminal barrier.
Availability sweep workers stage the immutable history input once per local daemon. One- and two-worker execution is the measured support envelope; larger local counts still run and are labelled unmeasured. Worker count is not research identity.
ledgr_history_policy()now records the experiment-level causal-history policy. Its defaults are five expected sessions of price-input carry, no output carry and the default segment guard. The policy and its axis, cutoff and treatment versions are part of identity rule 4.Point-in-time facts preserve effective and knowledge clocks separately. Date-backed facts retain their civil date plus resolved instant, venue zone and calendar identity; availability-aware Date boundaries resolve to venue session opens or closes instead of UTC midnight.
A membership, trading-status or lifetime fact without a knowledge time is still accepted and sealed, but runs, sweeps, feature precompute, walk-forward sessions and availability-aware pulse inspection on that snapshot now refuse with
ledgr_fact_knowledge_missingbefore writing anything. Supply a knowledge time, or declareknowledge = "assume_effective"to use the effective time.Trading-status corrections now use one acyclic supersession policy before precedence. Corporate actions distinguish known-when-effective rows, clockless audit-only rows and knowable rows missing an event clock. A Date-valued entitlement, effective or payment event resolves to the venue session close, while a Date-valued knowledge time resolves to session open.
Indicators now declare a history class (
boundedorhistory_dependent), the bar fields they require and, for a custom boundedfn/series_fnpair, an optional comparison tolerance. Built-ins and registered TTR forms take exact declarations from a package catalogue keyed by function, output and algorithm-changing argument; an undeclared custom indicator or an unregistered TTR form is disclosed as history-dependent. TTRaroonnow declares its true dependency width ofn + 1rows in both modes, so its dense fingerprint changes and dense aroon features gain one more warm-upNArow. The declarations enter availability-aware feature identity; other dense fingerprints are unchanged.A custom indicator whose id matches a built-in pattern (
sma_<n>orreturn_1) now runs its own function in dense and availability-aware runs, in any session and call order. Runs, sweeps and walk-forward resolve each feature from the experiment’s own definitions and no longer write the session indicator registry, so an explicitledgr_indicator_register()entry is never replaced by a run. A run config whose stored fingerprint matches neither the built-in nor a resolvable indicator refuses withledgr_run_hash_mismatch, and the unusedsma_nconfig id is removed. It was previously replaced, silently, by the built-in of that id, so dense results for such definitions change.Availability-aware experiments now run every indicator. Bounded indicators use their last
requires_barsexpected sessions on the instrument’s lifetime mask; history-dependent indicators run once per contiguous segment of available input and restart after a gap. Default price carry now fills eligible feature inputs for up to five expected sessions, while return and volatility catalogue rows never use a carried price; a custom indicator can declareinput_carry = "excluded"to do the same.ledgr_history_policy(price_carry = NULL)keeps the previous strict values.ledgr_indicator_gap_unsupportedis retired. A custom indicator whosestable_afterexceedsrequires_barsnow receives exactlyrequires_barsrows, and prints inside a known-inactive lifetime interval no longer enter feature calculation.Availability-aware feature preparation now checks every indicator for look-ahead before any run, sweep candidate or precompute payload uses it: at no more than four fixed sessions on one instrument, a value recomputed from the history ending there must equal the prepared value, or preparation refuses with
ledgr_feature_lookahead_detected. A custom boundedfn/series_fnpair is compared at the same sessions instead of at every window. Both checks are samples with a fixed cost, not proofs of causality, and dense execution runs neither.A delisting ends availability-aware bounded indicator windows as well as price carry: after every relisting a bounded indicator restarts and needs
requires_barsrows from the relisting before it is ready, so no window mixes pre-delisting and post-relisting prices.History-frame feature evidence reports the feature preparation’s planes:
calculatedis admission,readyis first-valid readiness,calculation_valida ready finite value, input and output carry are disclosed,used_before_windowalso reports carried values and bounded windows that reach before the returned window, andmissing_reason(includingcalculation_non_finitefor a ready cell whose calculation is not finite) distinguishes warm-up, missing and excluded inputs, expired output carry and sessions outside the calculation.Snapshot hash rule 4 and the current experiment-store schema add the causal civil-time representation. Older snapshots, stores and identity artifacts refuse; no migration or compatibility reader is provided before CRAN.
Public causal history
ledgr_history_frame()reads one bar field or one feature as causal history from an availability-aware strategy context with an explicit full or finite lookback, admissible, decision-axis or explicit instruments, a finite-observation requirement and wide or long layout. The result records its data start, window, cutoff, source and policy identity, the selection table, requirements and missingness and carry evidence.ledgr_history_matrix()now reads the same causal plan directly from a pulse context without first allocating a data frame. Its one-argument frame adapter remains available. Both forms return an identified ordinary double matrix; transformations deliberately remove ledgr identity.ledgr_history_info()returns what a history result recorded about itself: its cutoff, window and lookback, the selection table with every exclusion reason, the included and excluded instrument IDs and whether any value used history before the window.print()of a history result points to it.ledgr_align_weights(solution, history)turns an estimator’s or solver’s numeric output intoledgr_weightsnamed by the history result’s included instruments, without rebuilding column names, and refuses a length, names or shape that do not match (ledgr_weight_alignment_mismatch); it pipes assolution |> ledgr_align_weights(history) |> ledgr_target_rebalance(ctx).ledgr_passed_warmup()now also accepts an intact history frame or matrix and returns its recordedmin_observations/min_instrumentsreadiness result without rescanning cells.Feature history from
ledgr_history_frame()is a vintage view: after a lifetime fact becomes known late, earlier feature cells keep the value and the evidence they had at their own pulse, while bar history is restated with what is known at the cutoff. Before, those feature cells showed their earlier values with evidence resolved at the cutoff.Strategy preflight now classifies as Tier 3 a strategy that reaches private context internals: a dot-prefixed member such as
ctx$.history, a string naming a private context field, or closure and frame environments such asenvironment(ctx$feature). Those carriers hold prepared data beyond the pulse. Read history withledgr_history_frame().New article “Reading Causal History” (
vignette("causal-history", package = "ledgr")). It opens an existing snapshot and completes a run, then teaches history reads from the defaults to explicit instrument and missing-value choices, the matrix adapter,ledgr_history_info(), structural sufficiency, indicator history classes, carry evidence, return features instead of differenced carried closes, the vintage feature view, one history across walk-forward windows, finite lookbacks and what preflight does and does not guarantee. One executed portfolio estimate goes from a history read through a solver andledgr_align_weights()toledgr_target_rebalance(), keeping held instruments the estimate dropped.Results,
summary(),ledgr_experiment_plan()andprint()of an experiment now name the valuation horizon as the “stale-mark horizon” and show the experiment’s history policy (price carry, output carry and segment limit), each value marked default or declared. Previouslyprint()showed an unnamed “max sessions” value that could be read as the history policy’s price carry.Under
split_adjusted, corporate-action per-share amounts must be supplied per split-adjusted share. ledgr credits the amount times the held split-adjusted quantity and converts nothing at a split, so an amount declared per as-traded share before a later split is credited too large by the split factor unless the adapter converts it.ledgr_pulse_snapshot()accepts an availability-awareledgr_experimentwithts_utc,paramsandfeature_paramsand returns the context the experiment’s run starting at that cutoff passes to its strategy, without calling the strategy or writing anything. The snapshot-and-universe form is unchanged.ledgr_precompute_features()prepares availability-aware payloads through the causal substrate; a sweep reuses one only on its exact causal axis, cutoff, policy, snapshot and feature identity.Availability-aware history has one start, the data start: every run, sweep, window, promotion, walk-forward window, explicit precompute and pulse inspection reads feature, mark and entitlement history from the snapshot’s first session.
hydration_start_utc,ledgr_unsupported_hydration_start,ledgr_precomputed_hydration_mismatchand the payloadwarmup_rangeare removed, and the current experiment-store schema drops the stored fold column; older stores refuse.Under
carry_test_state, availability walk-forward test windows form one economic chain that equals one run with the same parameter switches: each window except the last executes its final decision at the next session open, and the next window opens from the resulting cash, positions, FIFO lots, corporate-action entitlements, dispositions and quantity effects bound but not yet settled, and frozen holdings. Monthly rolling and anchored folds derive every boundary from the anchor and clamp to month end.
Current-only experiment stores
Experiment-store schema 124 is opened through one marker-pair gate. Every package-owned persistent-store path refuses an older, newer, unmarked, malformed, or fingerprint-mismatched store with
ledgr_store_rebuild_required; pre-CRAN migrations and tolerant readers have been removed.Stored experiment records are current-only as well: run and walk-forward identity rule 4, policy schema 2, saved-sweep schema 6, retention schema 2, current provenance and execution timing version 2 are required. Older records are not inferred, translated or reopened; rebuild the research store from its source inputs with this ledgr version.
Canonical identity JSON now orders named keys with radix ordering, so config, strategy, feature, sweep and walk-forward hashes do not depend on process collation. The ordering rule is versioned in the current store fingerprint, and
identity_rule_versionis accepted only as the exact integer4L.
Feature retention and identity
Removed the process-local feature cache and its public clear function. Feature computation now follows the existing uncached production path, and cache telemetry is no longer recorded or printed.
Removed
persist_features, durable feature rows, and the unusedfeaturestable. Older stores and identity records refuse through the existing current-only gates; no migration, fallback, ignored argument, or compatibility alias is provided.Indicator and feature fingerprints now bind deterministic direct function captures. Unsupported captures refuse as non-deterministic, stale precomputed features refuse before execution, and the CSV adapter continues to represent captured row values through its existing
params$data_hash.Removed the pre-release public
fill_modelargument, thetypealias foras_tibble.ledgr_backtest(), and their dedicated compatibility conditions. Usetiming_model, a classedcost_model, andwhat, respectively.
Sweep persistence
-
ledgr_sweep_save()now saves sweeps whose candidates resolve to exactly one feature fingerprint. Parameterized feature maps, concrete one-feature maps, and plain one-feature lists reopen and promote with their existing identity and provenance.
Portable snapshot verification
New snapshots use snapshot hash rule 4, a portable R-owned binary identity with fixed 10,000-row blocks and an explicit byte-order check. Snapshot information reports the rule. Current stores require the rule-4 marker; missing or different markers are invalid stored evidence. ledgr does not migrate, reinterpret, or fall back to an older hash format.
Complete rule-4 verification is reused inside one process through a private, PID-bound set keyed by snapshot store, snapshot ID, stored root, and rule. Sealing or one successful complete recomputation establishes the proof; explicit validation and
ledgr_snapshot_open(verify = TRUE)always recompute. Snapshot information and printing report whether the exact tuple is currently verified. The proof is never persisted or serialized, and in-memory or unresolved stores never enter the set.
Consistent entry points and disclosed assumptions
ledgr_select_top_n()breaks ties by instrument ID in C-locale (byte) order, the order the availability provider uses, whatever the session’s collation. Previously ties followedLC_COLLATE, so the same strategy could select different instruments on machines with different locales.ledgr_compute_metrics()now withholds annualized return, volatility and Sharpe ratio for a run whose achieved window is shorter than requested, assummary()already did: the fields areNA, named in awithheldattribute, and print as withheld. Walk-forward test-window metrics follow the same rule.ledgr_backtest()now refuses a snapshot that declares point-in-time facts (sessions, membership, trading status, or lifetime) with classledgr_backtest_availability_unsupported. It runs dense execution only and previously ignored those facts silently; useledgr_experiment()andledgr_run(). Snapshots without facts, or with corporate actions only, still run.ledgr_valuation_stale()now defaults tomax_sessions = 5, and an availability-aware experiment without avaluation_policyruns underledgr_valuation_stale()instead of refusing. The default was chosen from US equity vendor data for S&P 500 members, 1998 to 2026: a few held missing-bar runs exceed 5 sessions and, under the"freeze"default, none stops a run. The defaulted policy has the same identity as declaring it; plans and results mark a horizon of 5 as(default). Snapshots without availability facts stay dense.Run identity now binds snapshot content instead of the store-local snapshot ID. Runs record
identity_rule_version = 4and the snapshot hash, so the same bars sealed under two snapshot IDs give the sameconfig_hash, and walk-forwardexperiment_hashandsession_idfollow. Rule 4 is the only readable stored identity; older research stores must be rebuilt. A run whose opening is dated before the snapshot now records the snapshot start, where it actually begins, as its start.ledgr_promote()now records the candidate’s strategy, feature-set, universe, valuation-policy, corporate-action-policy and opening identities beside the promoting experiment’s, inledgr_promotion_context(bt)$identity_check, and warns withledgr_promote_identity_mismatchwhen they differ. It still promotes. Promotion context records are nowledgr_promotion_v3; other versions refuse rather than being inferred.Current run configurations must carry explicit risk and corporate-action policy identities. Saved sweeps require retention schema 2, and a current run whose required strategy-provenance row is missing refuses with
ledgr_strategy_provenance_missing.Results of an availability-aware run now say what their point-in-time view rested on.
print()shows aPoint in time:line naming the universe kind (membership rule or fixed basket, noting membership facts a fixed basket does not apply), the horizon, and any fact families whose knowledge clocks or schedules were assumed;summary()lists the same with the valuation evidence, andledgr_valuation_summary()gainsuniverse_kind,universe_id,membership_declared,assumption_backed_families,assumption_reasonsandmax_sessions_default.
Inputs ledgr refuses or reports instead of ignoring
A snapshot no longer seals a zero or negative open, high, low or close (including a price that rounds to zero at the stored 8 digits). A dense snapshot fails its seal check with
ledgr_ohlc_non_positive; a snapshot with point-in-time facts reports the row asohlc_non_positiveand refuses to seal, or quarantines it underinvalid_observations = "quarantine". Previously a zero close sealed and became a priced current mark, so equity dropped while sizing helpers treated the instrument as unpriced. Zero volume with positive prices stays valid.ledgr_facts_sessions()refuses a session whose POSIXct open or close falls on another date in the venue zone (ledgr_session_instant_off_date) and two open sessions that share an open or close instant (ledgr_session_instant_duplicate), for example a vendor row shifted by one day. Previously such a calendar sealed;ledgr_run()then failed with an unclassified database error whileledgr_sweep()reported success.An availability-aware run, sweep, run window or walk-forward fold now refuses with
ledgr_sessions_incompletewhen the declared session calendar does not cover the dates of its sealed bars that it was asked to run. Previously the run silently covered only the calendar’s days and still reported itself complete.ledgr accounts in split-adjusted share units (no split event acts on a position), so as-traded bars across a split would book a phantom loss without a signal. An availability-aware experiment on a snapshot sealed without a price basis now runs on that model and says so: the plan, the experiment,
ledgr_corporate_action_summary()andprint()of a result showsplit_adjusted (assumed default; the snapshot declares none), and a declared basis shows(declared).ledgr_experiment_plan()andledgr_corporate_action_summary()gainprice_basis_default. The run’s identity keeps the basis undeclared. Dense experiments are unchanged.A valid bar that falls on no declared session close (for example a vendor bar on a market holiday) still seals and is still never a pulse of an availability-aware run, but it is no longer invisible:
ledgr_snapshot_quarantine()lists it with reasonoff_session_barand gains adispositioncolumn (excludedorsealed_not_a_pulse),ledgr_snapshot_info()gainsoff_session_bar_count, andsummary()of an availability-aware result reports the count when it is not zero. The snapshot hash is unchanged.A held corporate action that ledgr does not model is no longer silently ignored. A complete fact with a subtype outside the supported set, or an acquisition in a dense run, now counts in
unsupported_factsand makescorporate_action_fidelityreadunsupportedunder the research preset; the strict preset refuses it as an unsupported quantity effect. Previously such a fact never acted and the summary reported fidelitynone.A corporate-action settlement refused by the policy (for example a held dividend under
ledgr_corporate_actions_strict()) now ends an availability-aware runINCOMPLETEwith its accepted prefix and stop reasoncorporate_action_cash_refusedorcorporate_action_quantity_refused, like a refused terminal; the run opens and reruns, and its corporate-action fidelity readsunsupported. Previously the run failed and rolled back all its evidence. A dense run has no incomplete state and still raisesledgr_corporate_action_unsupported.
Runs and evaluations that keep going on real data
An interrupted and resumed run now binds dividend entitlement exactly like an uninterrupted run and a sweep. Previously the resumed run counted a fill executed at the entitlement session’s open, so a buyer on that open received the dividend only after a resume and a seller lost it, changing cash and equity.
The acquisition and spin-off composition in
ledgr_corporate_action_summary()now counts each holder’s entitled quantity as the run binds it: a fill at the entitlement session’s open is not counted, and a fact entitled before the run’s first pulse or after the last pulse it processed is not reported.An availability-aware run that stops on its very first pulse, for example because an opening holding has no price there, now stores an empty result that opens, prints and reruns. Previously it stored a row of missing equity for every pulse and could be neither opened nor rerun, which also made a walk-forward session that hit it impossible to rerun. A resumed run whose first resumed pulse stops now keeps the equity and completion record its earlier attempt committed, identical to an uninterrupted run, instead of discarding them. A run interrupted after its last pulse now finishes on resume; previously it stored missing equity and no completion record.
ledgr_state_reconstruct()now rebuilds an incomplete availability-aware run whose last fill executed after its last valued pulse. It used to stop with a cash-identity error; the rebuild now covers the ledger up to the last valued pulse, matching the stored equity, while the later fill stays visible in the ledger and inledgr_results(bt, "fills").ledgr_walk_forward()gainsno_selection = c("previous", "fail"). A fold in which no training candidate has a finite selection metric no longer ends the whole session: by default it runs its test window with the parameters most recently selected from training, or, before any training selection, runs no test window. Each fold records itsselection_source(train,previousornone), the printed session counts them, andno_selection = "fail"keeps the old refusal. Default sessions get new session IDs; rebuild older experiment stores under the current-only format.In an availability-aware run,
ledgr_target_rebalance()no longer stops the run when an eligible member has no current close, which is common in vendor data. That member is skipped for the rebalance: it keeps its current quantity, its weight stays in cash, and asizing_unavailabledecision row inledgr_results(bt, "diagnostics")records the skip. Other members keep their usual size unless the skipped holdings would push the total above allocatable equity, in which case they are scaled down. Thewhere = is.finite(ctx$vec$close)workaround is no longer needed, and it sells and rebuys around every gap where the skip does not. Dense runs still warn and target zero.A run interrupted before or during execution, by an error, an R time limit or a user interrupt, is now recorded
FAILEDwith the reason inerror_msg, instead of stayingCREATEDforever. The condition still reaches the caller, and rerunning the same experiment and run ID resumes it. Previously a user interrupt during the pulse loop was absorbed by the fold’s database transaction: the run rolled back its pulses and then finishedDONEas if they had never happened, which could not be resumed.Rerunning an interrupted
ledgr_walk_forward()session with the same arguments now finishes it. A test run the interruption leftFAILEDresumes where it stopped instead of failing its fold; finished test runs are still reused as recorded. Anyledgr_run()resume of a run that stopped before posting its opening positions now posts them first; it used to resume without them.ledgr_walk_forward()with the defaultopening_state_policy = "carry_test_state"now requires test windows that follow each other in time and touch exactly, and refuses other fold lists before running anything. A gap (for examplesteplarger thantest_window) carried positions across days that were never valued, and an instrument that delisted in between stopped the next test run. An overlap (stepsmaller thantest_window) or a list out of time order opened a test run from state its predecessor reached later. The errors name the first offending pair; use contiguous folds oropening_state_policy = "flat_test_state", which still accepts any layout.Walk-forward folds and windows can start on the first date of the snapshot. Bars are stamped at the session close, so a fold starting at midnight of the first date used to be refused as outside the snapshot, as was a precompute range ending late on the last date. A date outside the snapshot still refuses, and the error now names the snapshot’s first and last dates.
ledgr_state_reconstruct()works on availability-aware runs whose members miss bars. It rebuilds equity on the run’s session calendar with the same current, stale or frozen marks the run used, up to the last fully valued pulse, and matches the stored equity exactly; it used to fail withledgr_missing_bars.Creating and opening stores costs less. A new, empty store gets the current schema directly instead of through the upgrade path for older stores, and in one measurement
ledgr_db_init()took about a quarter and a smallledgr_snapshot_from_df()a little over half of their previous time. Sealing finds the availability tables with one catalogue query, and a store whose schema matches one already validated in the session is not validated again; a store whose columns, constraints or schema marker differ is validated in full, and damage to the required schema still fails.
Scoring starts
These changes alter results for dated and windowed work; undated runs and sweeps are unchanged.
-
ledgr_sweep(),ledgr_promote()andledgr_precompute_features()now honorledgr_opening(date = )as the scoring start, asledgr_run()already did. Sweeps previously scored and traded from the snapshot start. Walk-forward training windows no longer execute or score pulses before their start, so their scores and selections can change. - An opening date before the snapshot starts at the first bar in sweeps and precompute, as in runs. A sweep whose scoring range has fewer than two pulses refuses with
ledgr_run_window_too_short, as a run does. - A sweep’s
scoring_rangeattribute names the first and last pulse it scored, so it matches the same sweep afterledgr_sweep_save()andledgr_sweep_open().
ledgr 0.2.1.0
ledgr 0.2.1.0 models equity corporate actions from sealed facts, gives strategies one helper pipeline that behaves the same in dense and availability-aware runs, prints results on one screen, and reads CSV files through one path. It requires R 4.6.0 and duckdb 1.5.6.
Upgrading from 0.2.0.1
These changes need edits to existing code:
- Install R 4.6.0 or later and duckdb 1.5.6 or later. ledgr uses base R’s
%||%, added in R 4.4, so it never ran on the R 4.2 it declared; R 4.6 is the oldest release with duckdb 1.5.6 binaries on Windows and macOS. - In strategies,
ctx$vec$positionsis nowctx$vec$position, andctx$positionsis removed: usectx$vec$position, aligned toctx$universe, orctx$position(id).ctx$safety_stateis removed, and so are thectx$targets()andctx$current_targets()stubs; usectx$flat()andctx$hold(). A strategy edited to the new names gets a new source hash. -
ledgr_select_argmax()andledgr_select_argmin()are nowledgr_rule_argmax()andledgr_rule_argmin(). Walk-forward selections are unchanged. -
ledgr_pulse_snapshot()’sinitial_cashis nowcash: the cash held besidepositions. The pulse’sctx$equitynow adds the marked positions, so a pulse built with holdings reports different, correct equity and sizing. -
ledgr_snapshot_import_bars_csv()andledgr_snapshot_import_instruments_csv()are removed; useledgr_snapshot_from_csv(csv_path, instruments_csv_path = ...). The caller-managed create, import and seal steps on an open connection,auto_generate_instruments = FALSE,validate = "none"and a non-UTF-8encodinghave no replacement. CSV errors now raiseledgr_invalid_args;LEDGR_CSV_FORMAT_ERRORis gone. -
ledgr_feature_map()refuses a named indicator bundle, as inledgr_feature_map(bands = bundle); name the outputs with the bundle’sprefixornamingargument. It also refuses an alias equal to another entry’s feature ID. -
ledgr_opening(cash = 0)now fails when the opening is built; runs already refused it. -
vignette("ttr-and-adapter-indicators")is nowvignette("ttr-indicators").
These change results or identity without a code change:
- In availability-aware runs, the context signals
ledgr_signal_return(),ledgr_signal_feature()andledgr_signal(ctx, values)now score every instrument inctx$universeand carry eligibility in aneligibleattribute, instead of returning current members with inadmissible scores set toNA. Code that relied on the member-only length or on theNAs should readattr(signal, "eligible")orctx$vec$admissible. - New experiments record a corporate-action policy, and Yahoo snapshots declare their price basis, so config hashes differ from 0.2.0.1 for otherwise identical experiments.
- CSV files are read by DuckDB. All-numeric text with leading zeros is kept, so an instrument
0001no longer seals as1, and the textNAin a text column is text rather than missing. Snapshots of affected files get corrected IDs and hashes. - Pulse feature values now match a real run for
series_fnindicators,fn(window, params)indicators and recursive TTR indicators such as RSI; they were wrong on a pulse before. -
print(bt)andsummary(bt)print differently; see below.
Equity corporate actions
-
ledgr_facts_equity_corporate_actions(), used insideledgr_facts(), seals vendor-neutral corporate-action facts such as dividends, acquisitions and spin-offs, each with its dates, completeness, provenance tier and refusal reason. The facts are part of the snapshot hash; a snapshot without them keeps its old hash. Supplying them does not switch on availability. -
ledgr_snapshot_from_df(),ledgr_snapshot_from_csv()andledgr_snapshot_from_yahoo()takeprice_basis,"split_adjusted"or"distribution_adjusted", which is part of experiment identity. Yahoo snapshots default to"split_adjusted". -
ledgr_experiment()andledgr_backtest()takecorporate_action_policy, built withledgr_corporate_actions()or its presetsledgr_corporate_actions_research(), the default, andledgr_corporate_actions_strict(), which refuses every modeled treatment. Runs stored before this release reopen with no policy recorded. - Gross cash distributions post as
CASHFLOWledger events. Entitlement is fixed before the ex-date, the policy’scash_postingcredits the cash at the effective close or the next open, a fact that becomes known late posts once, when it becomes known, and a resumed run posts each fact exactly once. - A held position ended by a corporate action is closed by a new
DISPOSITIONledger event that consumes its lots, moves the modeled proceeds into cash and reports realized model PnL. Without a usable mark it refuses before changing the account. -
print(bt)states corporate-action coverage and the price basis,summary(bt)reports the evidence, andledgr_corporate_action_summary(bt)returns the full record, includingcorporate_action_fidelity:not_supplied,none,modeledorunsupported. -
compiled_accounting_model = "spot_fifo"refuses a run with corporate-action events instead of dropping them; use the default accounting for such runs. - New articles: “Cash Distributions” and “Authoring A Corporate-Action Adapter”.
Corporate actions are not complete in this release. This release does not claim corporate-action completeness, broker-exact settlement, net cash, tax correctness, or exact recipient exposure. Exact quantity settlement, such as new shares from a split or a spin-off and the basis allocated to them, is not modeled, and bars declared distribution-adjusted are refused as execution bars.
Strategy authoring
- The helper pipeline, a signal (
ledgr_signal_return(),ledgr_signal_feature()) intoledgr_selection()intoledgr_target_quantity()orledgr_target_rebalance(), behaves the same in dense and availability-aware runs. Dense contexts now exposectx$members,ctx$vec$member,ctx$vec$target_restricted,ctx$vec$target_restriction_reasonandctx$vec$admissible, with every instrument eligible. -
ledgr_signal_feature(ctx, feature_id)reads any registered feature as a signal. -
ledgr_signal(ctx, values)andledgr_selection(ctx, ids = , where = )accept a strategy context.ledgr_selection(missing = "exclude")treats a missing value as not selected; the default still refuses it. A context selection never picks an ineligible instrument. -
ledgr_select_top_n(partial = "allow")accepts fewer thannusable scores without warning, and ranks eligible instruments only. -
ledgr_target_quantity(selection, ctx, qty)gives selected instrumentsqtyshares and the rest zero; ineligible holdings keep their quantity. -
ledgr_target_rebalance(keep = )keeps the named positions at their quantity and sizes the remaining capital. A zero weight no longer needs a price, and several unpriced instruments raise one warning naming them all. -
ledgr_signal_strategy()keeps ineligible instruments at their current quantity and returns an empty target on an empty universe. -
ledgr_passed_warmup(ctx, values)checks eligible instruments only. -
ctx$tradable()returns the instruments that are members, admissible and priced; it does not promise a fill. -
ledgr_signal()andledgr_target()accept empty vectors. - A loop of one scalar accessor, including
ctx$feature(id, feature_id), over 100 or more instruments warns once per run withledgr_scalar_accessor_loopand names the vector form. Results are unchanged. -
ledgr_run_strategy(trust = TRUE)returns a function that can call ledgr’s exported functions and those of base and recommended R, so a recovered helper-pipeline strategy reruns. Objects in the global environment and ledgr’s unexported functions stay out of reach, and Tier 1 preflight now accepts only exported functions of recommended packages.
Inspection and results
-
print(bt)fits on one screen: period, opening cash, final equity, total return, maximum drawdown, closed trades, the corporate-action and price-basis lines, and a pointer tosummary(). -
summary(bt)shows the metrics first. Without corporate-action facts, its corporate-action block shrinks to the two headline lines and a pointer toledgr_corporate_action_summary(bt). -
ledgr_compute_metrics(bt)prints as a short table; the object and its values are unchanged. -
ledgr_run_completion(bt)answers whether a run completed, and if not, its achieved window, stop reason and affected exposure. -
ledgr_run_explain(bt, instrument_id)withoutts_utcreturns the instrument’s whole per-pulse history. -
ledgr_snapshot_quarantine(snapshot)returns the observations excluded underinvalid_observations = "quarantine", with reasons. -
ledgr_pulse_snapshot(state_prev = )supplies the previous state for testing a stateful strategy on one pulse, in dense runs. The pulse print shows cash, equity, held positions and whether a previous state was supplied. - After
dplyr::select(), the prints ofledgr_run_list(),ledgr_run_compare(), sweep results and walk-forward degradation show the selected columns in the selected order.
Data input and point-in-time evidence
-
ledgr_snapshot_from_csv()is the file form ofledgr_snapshot_from_df(): it takesinstruments_csv_path,factsandinvalid_observations, accepts the same columns and timestamps, and seals to the same hash. -
ledgr_snapshot_from_df()and the fact constructors accept integer-backedPOSIXcttimestamps. -
ledgr_sim_pit_inputs()generates a deterministic point-in-time input bundle with switchable cases, and the datasetledgr_demo_pit_inputsis one such bundle, with a venue closure, a missing open-session bar, a delisting, a late-known halt and a cash dividend. The new article “Preparing Point-In-Time Inputs” runs it end to end; “Data Input And Snapshots” is now “Importing And Sealing Market Data”. -
ledgr_sweep()now accepts strict-window indicators such asledgr_ind_sma()on a declared session calendar, asledgr_run()already did.ledgr_ind_ttr("SMA", input = "close", n = ...)is supported in availability-aware runs; other TTR indicators and recursive built-ins are still refused there before the strategy runs. - Extra columns on a quarantined row no longer change the snapshot hash.
Speed and reliability
- ledgr validates a store’s schema once per session and opens the store once per public call; calls that commit runs,
ledgr_walk_forward(),ledgr_promote()andledgr_backtest(), open it once for their own work plus once per run. With duckdb 1.5.6, which opens a store far more cheaply, a smallledgr_run()takes about 140 ms against 300 ms on duckdb 1.5.5 with the same code, andledgr_experiment()about 30 ms against 110 ms. - No public call leaves a connection open after it returns;
ledgr_sweep(),ledgr_snapshot_info(),ledgr_promote(), pulse snapshots and indicator dev objects used to. - ledgr can use a store that you hold open with your own DuckDB connection, which duckdb 1.5.6 would otherwise refuse.
- Availability-aware runs are much faster to value: in one measurement of a large run, computing the availability marks went from about 12 minutes to 11 seconds.
-
ledgr_snapshot_from_csv()read a 630,000-row file about three times faster in one cold measurement. - Experiment stores upgrade their schema automatically. A store that holds historical
FEEledger rows is kept but can no longer be read until cleaned. Malformed ledger event metadata now stops with an error instead of a warning, and a store missing a table fails to open instead of being rebuilt. -
ledgr_strategy_preflight()no longer loads every recommended package to classify a strategy, which removes stray warnings such as Tk’s.
Documentation
- The README is rewritten for a first reader, and all 26 articles were corrected against the shipped behaviour and reordered into Start Here, Building Blocks, Research Workflow, Point-In-Time Evidence, Going Deeper and Design. Strategies are taught as named vectors first and helpers second.
-
?ledgr_strategy_contextlists every public context member, and?ledgr_indicatordocuments bothfn(window)andfn(window, params).
ledgr 0.2.0.1
- Development version opened for the accepted v0.2.0.1 packet. No user-facing changes have shipped yet. The packet productionizes the reviewed availability provider and diagnostic representations behind the shared fold, replaces the quadratic seal-time conflict validators, repairs resumed-run equity finalization, and closes with separated cold, warm, and peer benchmark records. It changes no public API, schema, hash, run identity, accounting policy, or availability policy. The packet is at
inst/design/ledgr_v0_2_0_1_spec_packet/; Batch 0 is complete after maintainer acceptance. Batches 1 through 7 are complete after independent review and maintainer acceptance. The accepted hot-path complexity amendment adds linear memory/durable event writes and prepared fold-time valuation; its ticket cut was independently accepted, and the Batch 8 event buffers and Batch 9 prepared valuation are complete after review. Batch 10 is retained as diagnostic history. Batches 11 through 14 are complete after review, including the corrected public peer boundary, dense timestamp validation, within-chunk hash formatting, and final accepted-source records. Batch 15 completed local suite, build, check, documentation, Linux portability, governance, and artifact gates and passed independent review. The maintainer accepted the local release gate; remote branch CI, merge, main CI, tag, tag CI, and release publication remain later evidence.
ledgr 0.2.0.0
- Made ledgr-owned DuckDB drivers use session-local extension and secret storage, removing repeated DuckDB home notices from tests and rendered articles without suppressing ledgr diagnostics. Release checks now verify GFM freshness and local image targets, and the non-CRAN quantstrat DSR oracle is a documented manual check while CRAN
pbois declared inSuggests. - Corrected reversal-fill projections so derived CLOSE and OPEN fees are allocated pro rata and sum to the source event fee without changing cash, lot state, realized PnL, or trade metrics.
- Simplified
ledgr_run_fills()to one eagerbtargument with a stable tibble result; the cursor, lazy-read, and threshold-switching modes were removed. - Restored source and risk lineage on ranked sweep-review results so explicit candidate extraction and promotion work after review and reopen. The compact
review$toptable remains presentation-only and carries no sweep lineage. - Added the committed
risk_chain_hashtoledgr_run_info(). Historical runs without recorded risk identity reportNArather than an inferred no-op plan; inspection remains read-only and does not expose the full risk plan. - Locked durable run handles as resource-owning locators:
close(bt)releases resources without deleting evidence, and completed runs remain readable from the closed handle or throughledgr_run_open()in a later session. Public examples now use ordinary target indexing and an explicit review-to-promotion workflow. - Split the backtest implementation into its accepted config, handle, fills, and result ownership files, and extracted all four run-coordinator stages as explicit preparation, snapshot, finalization, registration, and resume records. Runtime effect order, snapshot guards, cleanup ownership, public exports, and execution behavior remain unchanged.
- Corrected post-fold failure handling so committed ledger, strategy-state, and feature evidence survives a failed finalization and the same run can resume finalization without duplicate rows. Added detecting feature causality, caller-RNG preservation, and resource-cleanup regression gates.
- Protected the structural
ts_utccolumn in retained wide sweep projections with a reversible reserved-name encoding. Original candidate IDs remain unchanged in long, matrix, panel, and candidate-extraction surfaces; no persisted mapping or identity input was added. - Corrected the availability run window so declared session-calendar bounds keep their time of day. The bounds are ISO8601 strings, and parsing them without an explicit format truncated both to midnight, which silently dropped the final open session from the decision axis and left a target decided on the last remaining pulse unfilled even though its next open was present in the snapshot. Runs over declared session calendars now cover one more session, so equity curves, pulse counts, and run identity hashes change for affected configurations. The same truncation is corrected for the walk-forward
created_at_utcrecord. - Added classed point-in-time fact constructors and a read-only pre-seal validation report for membership, trading status, lifetime, and complete venue-session calendars. Daily vendor dates map explicitly to declared session closes; observed bars never define the expected-session clock.
- Added read-only fact history and cutoff resolution over in-memory facts or sealed snapshots. Resolution preserves explicit true, false, and unknown membership states without exposing future-known evidence or widening the strategy context.
- Added constituent-list membership input and an optional qlcal session preparation adapter. Equivalent row/list membership evidence hashes identically; generated schedules retain provider, calendar, hours, override, and assumption provenance without persisting a calendar object.
- Session construction now rejects ambiguous fall-back and nonexistent spring-forward local wall times consistently while preserving explicit POSIXct instants.
- Added experiment-store schema 113, saved-sweep schema 4, and snapshot hash rule 2 for normalized fact families and explicitly acknowledged observation quarantine. Fact-free snapshots retain hash rule 1 unchanged, legacy sealed snapshots are never rehashed, and quarantined rows remain hashed audit evidence rather than runtime bars.
- Added explicit availability activation with public membership-universe and stale-valuation policies plus effective-plan inspection. Availability-aware experiments require a complete declared session calendar and valuation policy; canonical omission keeps the dense path unchanged.
- Added the internal point-in-time availability provider, dynamic public pulse axes, decision-time context planes, and stable-ID asset state. Facts are resolved only after both effective and knowledge time, while provider code remains free of valuation, affordability, risk, and fill policy.
- Corrected availability-aware execution so a close-time decision resolves execution facts and records an accepted fill at the next declared session opening. Decision-frozen membership and the terminal no-opportunity rule are preserved, while dense execution remains unchanged.
- Added read-side fill alignment and explicit timing provenance. Public fill timestamps remain economic execution times, while
recording_pulse_ts_utcderives the associated execution-session close for grouped equity alignment without adding a persisted column. New active runs record timing version 2 in identity; retained version-1 runs reopen read-only, and mixed or unknown timing conventions are marked ineligible for fill-level equivalence without hiding their summary metrics. - Added strict expected-session feature semantics for the built-in SMA and returns indicators. Whole-feed outages remain visible, missing required observations invalidate affected windows, and unsupported gap behavior fails before strategy use. Availability-aware compiled spot-FIFO requests also fail before execution; dense compiled behavior is unchanged.
- Added availability-aware target restrictions and helper-safe sizing. Held nonmembers are preserved and reserved by default, unavailable sizing data fails closed, post-risk targets cannot enlarge strategy exposure, and new or enlarged shorts are rejected before any fill can finance another purchase. The pre-release restriction vocabulary is finalized by renaming
status_haltedtotrading_haltedandstatus_quotation_onlytoquotation_only. - Added separate fresh/stale valuation and risk marks plus bounded affordability. Accepted reductions fund purchases through a deterministic virtual cash ledger, rejected sales fund nothing, stale marks never price execution, and final cash is reconciled without imposing an intrapulse floor.
- Added experiment-store schema 114 with direct-run completion and diagnostic evidence. Expected valuation, risk-mark, settlement, and reconciliation stops preserve accepted prefixes as
INCOMPLETE; unexpected fold errors remainFAILEDand retain only post-rollback error diagnostics. - Added terminal recovery for availability-aware
DONEandINCOMPLETEruns. Achieved incomplete run IDs return idempotently, while failed finalization resumes from committed terminal evidence without rerunning strategy code or duplicating fold rows.ledgr_run_open()now opens either terminal status; malformed evidence fails withledgr_run_terminal_evidence_invalid. - Curated fact-history and resolution printing now keeps effective and knowledge times, completeness, and resolution reasons visible while naming omitted stored columns and preserving the full evidence objects unchanged.
-
ledgr_run_list()now appends recorded completion evidence and puts achieved horizons beside raw metrics in its default print. Incomplete metrics are explicitly labelled as prefix-only; dense and historical runs retain typed unknown completion fields. - Backtest summaries now follow recorded completion evidence: when performance is incomplete they retain prefix total return and maximum drawdown, name the achieved window, and withhold annualized return, annualized volatility, and Sharpe ratio with an explanation. Run-info printing presents completion before identity and telemetry fields.
- Backtest summaries now label
n_tradesasClosed Trades, matching its closed-trade-row computation without changing the value. - Bound the completed-run comparison rule: explicitly named non-
DONEruns fail withledgr_run_not_complete, while inventory-style comparison without IDs excludes them. The Survivorship Bias article now uses the public fact prints and completion-aware inventory rather than article-defined reporting helpers. - Added experiment-store schema 115 completion evidence for sweep candidates and walk-forward scores. Incomplete prefixes stay visibly labelled but are excluded from complete panels, selection, candidate extraction, and promotion; an incomplete test fold ends a carry-state walk-forward session as
PARTIALwithout inventing a later opening state. Extraction and promotion reject incomplete candidates withledgr_incomplete_sweep_candidateandledgr_promote_incomplete_candidate, respectively. - Added
diagnosticsandavailabilityresult tables plusledgr_run_explain(). These read durable traces and reconstruct availability from sealed facts without executing strategy code; missing traces fail withledgr_run_explanation_unavailablerather than producing an inferred decision. - Added the executed Survivorship Bias article. Its public workflow builds dated membership and session facts, seals and runs an ordinary strategy, explains a blocked exit and retained holding, reopens the same evidence, and contrasts strict invalid-observation rejection with explicit quarantine and an honestly incomplete valuation horizon.
- Rebuilt that workflow around public fact-history and cutoff-resolution queries, an explicit whole-feed outage, opening-time fill evidence, and a result-derived common comparison window.
ledgr_run_info()andsummary()now expose recorded completion bounds, stop evidence, and affected instruments without reconstructing historical evidence. - Development continues under the accepted v0.2.0.0 packet. The ordered scope next hardens representation, provenance, and run-coordinator boundaries, then implements the first point-in-time asset-availability path on the shared fold. The packet is at
inst/design/ledgr_v0_2_0_0_spec_packet/; Batches 0-12 are complete after review and Batch 13 is awaiting review.
ledgr 0.1.9.7
- Added
ledgr_return_panel()as the public return-panel entry point for selection-integrity diagnostics. The diagnostics now accept either a source-neutral return panel or a retained sweep, carry a uniform evidence-onlypanel_hash, and are renamed toledgr_pbo(),ledgr_min_track_record(),ledgr_dsr(), andledgr_effective_trials()with no deprecated aliases. - Extended opt-in sweep retention with closed-trade evidence and added
ledgr_sweep_trades(). The compact retained table records deterministic trade order, close time, realized P&L, and win/loss outcome; it is captured during the sweep and is never reconstructed from fills. - Added
ledgr_business_objective()and seven classed, serializable, hashable eligibility criteria covering trade distribution, profit distribution, strict-lattice stable regions, drawdown, run stability, minimum trade count, and positive equity trajectory. Named DSR and MinTRL diagnostic outputs can also be thresholded as evidence without being recomputed. Objective hashes are provenance only and do not enter run, sweep, candidate, promotion, session, or walk-forward identity. - Added
ledgr_sweep_filter()as an evidence-only, all-candidates objective evaluation. Its default table retains every completed candidate and every criterion outcome, including failures. It does not rank, choose, or promote a candidate, and candidate extraction, promotion, and walk-forward selection reject the result explicitly. - Added native
ledgr_k_ratio()using the pinned Kestner (2013) compounded- return definition. Like the other Selection Integrity diagnostics, it is evidence only and makes no future-profitability claim. - Closed the v0.1.9.6 intraday-audit M-1 honesty gap with a warning-only, classed metric-context cadence guardrail. It detects intraday observations paired with daily annualization assumptions without changing metric values, execution behavior, or identity. This is not first-class intraday runtime support.
- Rebuilt the Selection Integrity article around visible return-panel inputs, concept-first explanations, and executed cautionary contrasts for PBO/CSCV, MinTRL, K-Ratio, DSR, and effective trials.
- Deferred objective-filtered walk-forward identity, broader non-lattice robustness criteria, scored/weighted objective composition, and a public third-party criterion contract to a later business-objective RFC. Triple Penance remains paper-verification gated; cadence-aware walk-forward window warnings and the bounded intraday example remain with the later intraday cycle. Purging/embargo/CPCV, portfolio optimization, point-in-time data, benchmark-relative metrics, OMS, broker reconciliation, paper/live trading, and liquidity/capacity remain separate future work.
ledgr 0.1.9.6
- Added canonical single-run returns as
ledgr_results(bt, what = "returns")andas_tibble(bt, what = "returns"), using the same adjacent-equity return formula as ledgr-owned metrics and retained sweep returns. - Added retained-sweep return panels and adapter-shaped projections for validation consumers.
ledgr_sweep_returns_panel()fails closed on ragged completed-candidate grids, while matrix, data-frame, and optional xts projections keep candidate ordering deterministic. - Ran the PBO/CSCV spike and accepted a native implementation path. ledgr now ships native evidence-only selection-integrity diagnostics:
ledgr_sweep_pbo(),ledgr_sweep_min_track_record(),ledgr_sweep_dsr(), andledgr_sweep_cluster(). The CRANpbo, PerformanceAnalytics, and quantstrat packages remain optional reference checks, not runtime dependencies. - Added the Selection Integrity article. It teaches PBO/CSCV, minimum track record length, Deflated Sharpe Ratio, and deterministic effective-trial clustering as one method family: what evidence they consume, how to interpret them, what they cannot prove, and why they do not select or promote candidates.
- Completed the intraday-readiness audit and recorded the future guardrails it found. v0.1.9.6 makes no intraday runtime change.
- Redid the internal peer-benchmark preflight on the current cost/risk surface and recorded the current-surface row boundary. This is internal measurement evidence only; the release adds no public benchmark ranking claim and does not flip the compiled spot-FIFO default.
- Deferred out of v0.1.9.6: business-objective filtering, objective-filtered walk-forward identity, K-Ratio, Triple Penance, purging, embargo, CPCV, benchmark-relative diagnostics, portfolio optimization, intraday runtime support, and a compiled spot-FIFO default decision.
ledgr 0.1.9.5
- Consolidated the post-v0.1.9.x public surface after the cost, sweep persistence, target-risk, and walk-forward feature arc. This release is a naming, teaching, contract, and audit-hardening release before the validation-toolkit packet.
- Renamed and unexported pre-CRAN API names for naming consistency. No compatibility aliases are shipped.
- Fixed scheduled v0.1.9.4 deep-review findings in runner/results behavior, accounting hardening, compiled spot-FIFO validation, timestamp hashing, lot-close dust handling, and contracts.
- Refreshed the article set: quickstart, risk-and-cost, walk-forward, strategy-authoring, TTR/adapter indicators, metric-context conventions, experiment-store, and execution-semantics now reflect the renamed API and the v0.1.9.x feature arc.
- Added maintainer-manual articles for the cost resolver, target-risk layer, and walk-forward machinery, plus an internal performance-arc follow-up for cost/risk/walk-forward measurement questions.
- Made walk-forward inspection easier to read:
wf$degradationnow prints a curated train-versus-test view (the full table is oneas_tibble()away), and fold lists print their per-fold train and test windows. - Added
ledgr_sweep_review()for explicit sweep review tables andledgr_temp_store()for disposable.duckdbpaths used in examples and teaching workflows. - Deferred out of v0.1.9.5: validation-toolkit statistics, the standalone debugging article, the Split E vignette split, additional sweep/metric-context splits, lower-value vignette-audit helpers, paired entry/exit trade views, and the promotion-recovery summary helper.
| Old name | New name / disposition |
|---|---|
iso_utc() |
ledgr_iso_utc() |
passed_warmup() |
ledgr_passed_warmup() |
select_top_n() |
ledgr_select_top_n() |
signal_return() |
ledgr_signal_return() |
target_rebalance() |
ledgr_target_rebalance() |
weight_equal() |
ledgr_weight_equal() |
ledgr_clear_feature_cache() |
ledgr_feature_cache_clear() |
ledgr_compare_runs() |
ledgr_run_compare() |
ledgr_deregister_indicator() |
ledgr_indicator_remove() |
ledgr_extract_strategy() |
ledgr_run_strategy() |
ledgr_extract_fills() |
ledgr_run_fills() |
ledgr_get_indicator() |
ledgr_indicator_get() |
ledgr_list_indicators() |
ledgr_indicator_list() |
ledgr_register_indicator() |
ledgr_indicator_register() |
ledgr_snapshot_load() |
ledgr_snapshot_open() |
ledgr_ttr_warmup_rules() |
ledgr_ind_ttr_warmup_rules() |
ledgr_walk_forward_results() |
ledgr_walk_forward_open() |
ledgr_backtest_run() |
no longer exported |
ledgr_compute_equity_curve() |
no longer exported; use ledgr_results(bt, "equity")
|
ledgr_create_schema() |
no longer exported |
ledgr_metric_context_resolve() |
no longer exported |
ledgr 0.1.9.4
- Added the first walk-forward evaluation surface.
ledgr_walk_forward()runs fold-local train sweeps, applies scalar selection rules, executes the selected candidate on each test window throughledgr_run(), and persists compact session, fold, and score evidence. - Walk-forward results now lead with a train-vs-test degradation table. The table records selected train and test metric values, absolute and percentage differences, and warning flags such as
short_test_windowandcold_start_distorted. - Added read-only walk-forward inspection helpers and explicit candidate extraction.
ledgr_walk_forward_results(),ledgr_walk_forward_scores(), andledgr_walk_forward_folds()reopen stored evidence without recomputing or rerunning.ledgr_walk_forward_extract_candidate()returns a normalledgr_sweep_candidatefor deliberateledgr_promote()use. - This release does not add PBO, CSCV, CPCV, DSR, benchmark diagnostics, selection-integrity correction, cross-snapshot walk-forward, OMS behavior, paper/live behavior, gross-vs-net attribution, signal decay, or implementation/cost decay.
ledgr 0.1.9.3
- Added the first public target-risk API: classed
ledgr_risk_chain(),ledgr_risk_none(),ledgr_risk_long_only(), andledgr_risk_max_weight()objects. Risk chains transform full validated strategy target vectors before fill timing and cost resolution; they do not construct targets, rank candidates, estimate costs, model liquidity, or own order lifecycle behavior. - Added target-risk execution identity. Modern configs, runs, sweep candidates, promotion context, and saved sweeps carry
risk_chain_hashplusrisk_plan_jsonalongside existing strategy, feature, metric, and cost identity. Omitted risk,risk_chain = NULL, andledgr_risk_none()normalize to the same no-op risk plan. - Target-risk transforms apply uniformly across canonical R, parallel, and compiled spot-FIFO sweep paths with the same risk identity and parity requirements.
- Saved sweeps now use schema v2 for risk identity. The first
ledgr_sweep_save()against a v0.1.9.2 store performs an additive migration forrisk_chain_hashandrisk_plan_json; older saved sweeps with no risk fields reopen as the no-op risk plan when unambiguous. - Refactored the fold pulse into plan, risk, timing/cost, emit, and apply phases so risk transforms happen once per pulse before fills are proposed. This preserves the canonical fold path and changes internal telemetry attribution by moving proposal-build work into pulse planning /
t_fill; it is not a behavior change apart from the requested risk transforms. - This release does not add arbitrary risk callbacks, affordability enforcement, cash-floor checks, portfolio optimization, margin, shorting or borrow policy, liquidity/capacity modeling, OMS behavior, broker-grade risk controls,
failure_typeschema columns, walk-forward, or automatic candidate selection.
ledgr 0.1.9.2
-
ledgr_sweep()can now retain and persist compact sweep artifacts. The newretain = ledgr_sweep_retention()argument defaults to today’s scalar-only sweep behavior; opting intoreturns = "completed"keeps net portfolio equity and pulse-aligned returns for completed candidates. Saved sweeps can be eagerly reopened from the experiment store as sweep-like result objects and queried throughledgr_sweep_returns()orledgr_sweep_returns_wide(). Failed candidates keep scalar summary rows but no retained return rows. - Pre-CRAN sweep candidate rows now expose
candidate_idplus one-indexedcandidate_rowinstead of overloadingrun_id. Committed runs still userun_id; sweep promotion context records the sourcecandidate_id/candidate_rowseparately from the promoted run ID. - This release does not add ranking helpers, automatic selection, benchmark-relative alpha/beta diagnostics, signal-decay tooling, or gross-vs-net cost attribution; those remain routed to later benchmark-context, feature-analysis, and execution-attribution RFCs.
ledgr 0.1.9.1
- Added the public transaction-cost API: classed
ledgr_cost_*()model constructors, orderedledgr_cost_chain()composition,ledgr_cost_steps()/ledgr_cost_describe()inspection helpers, explicitledgr_timing_next_open()timing, and deterministic cost identity throughcost_model_hashpluscost_plan_json. - Breaking pre-CRAN changes: legacy
fill_modelinputs are replaced bytiming_modelplus requiredcost_model; zero-cost execution must be explicit withledgr_cost_zero(); new fill rows usefeeinstead of the legacycommission_fixedfield; and legacy stored configs withfill_modelare rejected with classed errors rather than translated. - Fixed the v0.1.8.11 auditr THEME-004 identity findings:
config_hashis now independent of DuckDB store path and alias declaration order, andalias_map_hashis independent of concrete feature parameter values. Concrete feature identity is captured byfeature_set_hash, which is exposed alongsidecost_model_hashandcost_plan_jsonon documented run and promotion surfaces. These identity fixes intentionally change stored-run hashes for affected experiments. - Refreshed bounded documentation surfaces for the cost API and related auditr findings: installed disclaimer resolution, stable cost/timing/legacy condition-class help,
LEDGR_LAST_BAR_NO_FILLhelp, quoted-spread cost teaching, runnable cost examples, and sweep documentation that cost models are fixed experiment inputs in v1. At the time of v0.1.9.1, sweep artifact persistence, target risk, and walk-forward were still future v0.1.9.x packets.
ledgr 0.1.8.11
- Added the maintainer manual under
inst/design/manual/, with two-layer Synthesis and Implementation Trace articles for execution fold core, observability/determinism, snapshots/data, sweep, features, the v0.1.8.x performance arc, and benchmark methodology. - Audited and reorganized the design surface:
contracts.md, the design index, RFC index, roadmap, horizon, generated documentation,inst/contents, and release-gate context were refreshed for the post-v0.1.8.10 architecture. - Wound down legacy
adr/,architecture/, andmaintainer_review/source-note directories to README-only ledgers after migrating their load-bearing content into the maintainer manual. - Refreshed user-facing documentation for the scoped B2 spot-FIFO sweep opt-in, strategy accessor substrate, and research-software disclaimer posture. This release adds no execution semantics, public API, or benchmark speed claims.
ledgr 0.1.8.10
- Added the matrix-canonical strategy context substrate:
ctx$idx(), universe-alignedctx$vecOHLCV/positions views, andctx$vec$feature(feature_id)for bulk feature reads. Existing scalar helpers remain supported. -
ctx$positionsremains a named pulse-start snapshot, but its name order is now canonicalized toctx$universeeven when the internal fold state was initialized from a differently ordered named position vector. - Moved FIFO lot accounting ownership into the fold state transition for fresh sweep execution while preserving event rows and durable reconstruction / readback as verifier and fallback paths. Fresh memory-backed sweeps now use the handler’s inline summary facts when available instead of replaying the event stream for equity/fills/metrics.
- Hoisted fixed yyjsonr read/write option objects out of canonical JSON helper bodies, preserving canonical byte-format v2 while removing avoidable per-call options construction overhead.
- Added a scoped public opt-in for the memory-backed sweep spot-FIFO accelerator with
ledgr_sweep(..., compiled_accounting_model = "spot_fifo"). The default remains canonical R (compiled_accounting_model = NULL); committed durable runs still fail closed for"spot_fifo"until a separate durable-integration gate lands.ledgr_run()validates the same enum and reports that durable deferral explicitly.
ledgr 0.1.8.9
- Switched canonical JSON serialization and parsing from jsonlite to yyjsonr and intentionally bumped the canonical JSON byte format to v2. Config, snapshot, strategy, reproduction, and ledger metadata hashes that include canonical JSON bytes are not byte-comparable with pre-v0.1.8.9 values.
- Updated strategy provenance package fingerprints to track yyjsonr instead of jsonlite. This is an expected identity-surface change for the v0.1.8.9 dependency migration, not a strategy-semantics change.
- Continued the single-core optimization round with measured per-lane attribution for fills reconstruction, durable and memory output handlers, position valuation, and target-delta handling.
- Recorded the local v0.1.8.8-to-v0.1.8.9 benchmark closeout. On the high-density xlarge workload-grid cell, durable wall time moved from 445.02s to 232.03s, per-fill engine cost fell 51.9%, and per-fill extraction cost fell 88.2%; these numbers are local-host/current-source evidence, not a public speed ranking.
ledgr 0.1.8.8
- Added optional parallel sweep dispatch over the existing fold core, keeping
workers = 1on the sequential reference path and making worker setup, package loading, deterministic row ordering, warning/error association, and discard-all interrupt behavior explicit. - Added deterministic per-pulse seed plumbing through
ctx$pulse_seedand rejected ambient-RNG strategy behavior for resume/parallel equivalence surfaces while preserving deterministic strategies. - Introduced an internal typed execution-spec payload and validation boundary so run, sweep, fold, and worker dispatch share one structured execution contract without adding public API surface.
- Refreshed fold-core diagnostics and maintainer evidence, including bucketed fold-loop profiling and current-source closeout artifacts for future optimization work.
- Reorganized
dev/bench/, added a repo-local peer benchmark with boundary-equivalent ingestion/engine/results timing, real engine invocation gates, divergence attribution artifacts, and explicit caveats that the report is an internal sanity check rather than a public ranking. - Added a ledgr-only self-profiling workload grid across SMA density, universe size, history length, and persistence mode. The baseline confirms that high-fill-density workloads are dominated by fold-loop fill/event throughput and fills reconstruction, and that the memory-backed path is not currently a universal fast path at large scale.
- Deferred the maintainer-manual skeleton and stale-document cleanup ticket to a future maintainer-manual / architecture-documentation release.
ledgr 0.1.8.7
- Removed legacy execution gunk from modern runs: execution now reaches the fold only through sealed snapshots, R6 strategies are gone, and run-time
data_hashidentity no longer participates in sealed-run resume or replay. - Dropped
cliandR6, addedcollapsebehind a deterministic wrapper, and kept the modern strategy contract as plain functions returning full named numeric target vectors. - Reworked the durable and sweep event buffers from worst-case preallocation to grow-by-doubling, preserving event IDs, event order, POSIXct UTC timestamps, per-row
meta_json, and durable/memory event surfaces. - Rejected sub-second snapshot input, carried trusted whole-second POSIXct values through hot fill paths, and replaced session-local feature cache JSON+hash keys with deterministic length-prefixed lookup keys while keeping durable identity hashes fenced.
- Rewrote fills reconstruction/read-back around primitive column buffers instead of per-row data.frames plus
do.call(rbind, ...), preserving FIFO semantics and DB-backed/memory-backed parity. - Formalized the sweep fast path versus promotion/materialization slow path and added
ledgr_candidate_reproduction_key()so compact sweep candidates expose the data needed for later explicit promotion. - Recorded the post-lane local benchmark closeout. On this host and one TTR-backed SMA workload, the canonical ledgr peer row is now faster than the local Backtrader and quantstrat rows, but the result is scoped to that workload, timing boundary, and machine; it is not a public peer-superiority claim.
- Deferred parallel dispatch, compiled fold-core work, matrix-canonical public strategy surfaces, target risk, walk-forward, cost/liquidity, OMS, and public benchmark dashboards.
ledgr 0.1.8.6
- Reduced feature setup/materialization cost by deduplicating feature cache-key inputs, making
ctx$feature_tableschema-only by default, and preserving long-row inspection through explicit/on-demand paths. - Kept
ctx$features_widecontract-compatible while making wide-view data.frame manifestation cheaper and removing an intermediate all-pulse wide matrix allocation. - Added a structured local benchmark suite with current-source guards, warmup/repeat metadata, machine-readable outputs, LEAN side-by-side caveats, two-mode width sweeps, and matched local peer benchmark support.
- Recorded post-fix attribution and peer-comparison evidence: the matched Backtrader SMA crossover row is faster than ledgr on this host, and profiling names the event-buffering/emission path as the dominant remaining hot lane.
- Deferred DuckDB feature storage, typed persistent event columns, snapshot administration, research-loop helper APIs, auditr-report intake, target risk, parallel dispatch, walk-forward, public cost/liquidity APIs, OMS, and public benchmark dashboards.
- Added the v0.1.8.7 Optimization Round 2 handoff: RFC-first fold-core primitive contract, run-artifact materialization policy, event-emission lane, cache-key/setup lane, reconstruction lane, and ADR 0004 dependency decisions.
ledgr 0.1.8.5
- Rebuilt the documentation set around the research workflow: sealed snapshots, declared experiments, executable runs, exploratory sweeps, candidate promotion, reopenable artifacts, and provenance review.
- Migrated installed and pkgdown article sources to Quarto, removed the retired Getting Started middle layer, and aligned README and pkgdown navigation with the new reading flow.
- Added or refreshed canonical articles for experiment storage, reproducibility tiers, execution semantics, sweeps, strategy development, indicators, custom indicators, metrics and accounting, leakage, and research-to-production boundaries.
- Tightened documentation contracts around active aliases, feature-vs-strategy parameter namespaces, sweep inspection, promotion caveats, metric context, snapshot lifecycle, source provenance, warmup behavior, and target-holding execution semantics.
- Kept runtime semantics stable. Walk-forward evaluation, target-risk and capital-affordability constraints, public cost-model factories, parallel dispatch, out-of-core feature storage, benchmark context, external point-in-time regressors, and paper/live adapters remain planned roadmap layers rather than v0.1.8.5 features.
ledgr 0.1.8.4
- Added active parameterized feature aliases so feature declarations can use
ledgr_param()placeholders and resolve them per run or sweep candidate while strategies read stable alias names such asfastandslow. - Added separate feature-grid and strategy-grid helpers plus executable grid composition so feature-resolution parameters and strategy-runtime parameters stay conceptually distinct.
- Stored alias-map provenance with run and sweep artifacts, including deterministic alias-map hashes, while keeping concrete feature identity separate from user-facing alias names.
- Updated pulse-debug and feature-inspection surfaces so parameterized feature maps, active aliases, and resolved concrete feature IDs can be inspected before and after execution.
- Added
ledgr_demo_sma_crossover_strategy()as a small Tier-1 teaching fixture for README, getting-started, sweep, and strategy-development examples. - Routed the v0.1.8.3 auditr report into bounded fixes for sweep print copy, preflight global-assignment diagnostics, warmup-guard examples, real-data troubleshooting notes, and active-alias documentation.
- Kept automatic ranking, objective selection, walk-forward, target risk, cost/liquidity policy, OMS, parallel dispatch, split stores, live data logs, point-in-time regressors, and scaffold helpers out of this release.
ledgr 0.1.8.3
- Made
ledgr_sweep()substantially faster on single-core workloads by adding a shared runtime feature projection, fast pulse-context helper reuse, prebuilt static pulse views, typed memory events, and single-pass sweep summary reconstruction while preserving the sharedledgr_run()/ledgr_sweep()fold-core contract. - Final v0.1.8.3 measurements show the reference sweep workload improving from 45.585s to 13.220s (3.45x faster) and the wider feature-payload workload improving from 65.360s to 12.130s (5.39x faster). The repeated committed-run comparison is also faster than baseline after the persistent buffered-write path was preserved under the unified output-handler contract.
- Preserved public context field semantics for
ctx$bars,ctx$feature_table, andctx$features_widewhile moving their expensive per-pulse construction out of the fold hot loop. - Hardened strategy preflight against indirection bypasses and clarified Tier 3 forbidden-call diagnostics without adding a public force-override path.
- Polished metric-context, sweep failure inspection, snapshot sealing, indicator, strategy-development, and troubleshooting documentation based on routed maintainer-review findings.
- Recorded the accepted primitive-internals and collapse-acceleration synthesis for future v0.1.9 planning, while keeping collapse out of v0.1.8.3 runtime dependencies.
ledgr 0.1.8.2
- Added
ledgr_metric_context(),ledgr_risk_free_rate(),ledgr_calendar(), US-equity and crypto calendar templates, metric-context hashing, and inspection accessors so risk-free-rate and annualization assumptions are explicit, stored, and auditable. - Threaded metric context through experiments, committed runs, summaries, single-run metrics, comparisons, sweeps, candidates, and promotion context without adding metric context to execution config identity.
- Added a plain serializable metric kernel that precomputes annualization and period risk-free-rate inputs before metric computation, including comparison and sweep paths.
- Fixed strategy preflight so forbidden nondeterministic calls such as
Sys.time()and global assignment with<<-fail early as Tier 3 before run or sweep artifacts are written, while resolved immutable external scalars remain Tier 2. - Improved metric, sweep, promotion, indicator, CSV, timestamp, TTR bundle, feature-inspection, and strategy-development documentation based on auditr intake, including clearer current-workflow guidance for parameterized indicator sweeps before the future active-alias API.
- Polished selected diagnostics without changing error classes, including timestamp format guidance, CSV snapshot failure next actions, duplicate bundle alias remediation, unsupported feature-result table routing, and TTR output-naming guidance.
- Completed the indicator codebase Phase 2 file-shape cleanup by renaming the built-in and adapter indicator files and splitting indicator development helpers from pulse snapshot helpers while preserving public APIs, exports, feature IDs, fingerprints, and behavior.
- Recorded pre-CRAN compatibility policy and accepted the active parameterized feature aliases synthesis for the future v0.1.8.4 sweep-authoring ergonomics cycle.
ledgr 0.1.8.1
- Added
ledgr_ind_ttr_outputs()for multi-output TTR authoring. The helper returns aledgr_indicator_bundlethat flattens into ordinary single-output indicators before runtime, preserving the existing feature and sweep provenance contracts. - Added derived default bundle names such as
bbands_dn, explicitprefixsupport,outputsfiltering,prefix = NULLraw-name opt-in, and a named-vectornamingescape hatch for custom bundle IDs. - Extracted package-level determinism and fingerprint helpers from
R/indicator.Rinto a dedicated determinism module without changing public APIs, existing indicator IDs, or fingerprint pins. - Expanded installed documentation around the feature lifecycle, feature IDs, feature-map aliases, warmup feasibility, result inspection surfaces, sweep provenance, failed-candidate inspection, snapshot metadata, CSV validation locality, and strategy-helper troubleshooting.
- Completed runnable example and discoverability polish across the main vignettes, including a complete custom-indicator run/inspect workflow and clearer installed article links from package and function help.
- Polished selected diagnostics: final-bar no-fill warnings now state origin, consequence, and next action; duplicate indicator registration errors now name both safe actions; Tier 3 preflight errors now state that no public force override exists on
ledgr_run()orledgr_sweep(). - Documented current metric assumptions without introducing metric-context storage: the current public metric path uses a default risk-free rate of zero and cadence-inferred annualization.
- Refreshed stale version wording in user-facing documentation so current tutorials describe the current research workflow instead of old patch-release labels.
ledgr 0.1.8.0
- Added sequential
ledgr_sweep()for lightweight parameter-grid exploration over the same private fold core used byledgr_run(), with in-memory sweep output rather than per-candidate DuckDB writes. - Added
ledgr_param_grid(),ledgr_precompute_features(),ledgr_candidate(), andledgr_promote()support for explicit sweep construction, candidate extraction, and committed run promotion. - Added row-level
execution_seed, compact row-level provenance, and durablerun_promotion_contextrecords so promoted runs remain traceable to their source sweep candidate and selection view. - Added explicit execution seed support for
ledgr_run()and deterministic sweep candidate seed derivation from the master seed, candidate label, and candidate parameters. - Refactored fold execution internals around a shared execution core, memory and persistent output handlers, a reserved future target-risk slot, and a private fill-timing/cost-resolution boundary.
- Added parity coverage proving sweep, promotion, and direct run behavior agree for deterministic strategies, seeded stochastic strategies, feature-factory sweeps, fill timing, standard metrics, and config identity.
- Added the
sweepsvignette and README workflow section documenting train/sweep/evaluate discipline, caller-owned ranking, failure rows, seeds, provenance, promotion context, and deferred non-goals.
ledgr 0.1.7.9
- Added
ledgr_feature_contract_check()so users can inspect feature warmup feasibility before running a strategy, including explicit handling for deferred feature factories. - Improved strategy-author ergonomics around empty selections, feature maps, strategy context helpers, custom indicators, and zero-trade/warmup diagnosis.
- Polished public documentation flow, article ordering, snapshot/store examples, result-inspection guidance, and release-site hygiene.
- Fixed opening-position lot accounting so cost basis is honored consistently across fills, trades, metrics, equity reconstruction, derived state, and run comparison output.
- Documented current per-leg
spread_bpssemantics without changing fill-model behavior. - Removed dead live equity-array bookkeeping and routed remaining execution engine audit findings to explicit release or roadmap decisions.
ledgr 0.1.7.8
- Added strategy reproducibility preflight with
ledgr_strategy_preflight(), classifying functional strategies astier_1,tier_2, ortier_3before ordinaryledgr_run()execution. - Integrated Tier 3 enforcement into the existing run path so unresolved unqualified helper dependencies stop execution with a classed error before run artifacts are written.
- Recorded strategy preflight results in run provenance and exposed the reproducibility tier through existing run-info, comparison, and strategy extraction surfaces.
- Added public design articles for reproducibility, leakage, and custom indicator authoring, including the
trust = FALSEstrategy-extraction boundary and the residual risk of custom vectorizedseries_fncode. - Routed the v0.1.7.7 auditr follow-up findings so reproducibility, leakage, provenance, and custom-indicator boundary work shipped in v0.1.7.8 while broader strategy-author ergonomics remained deferred to v0.1.7.9.
- Documented the fold-core/output-handler contract that future sweep mode must inherit so
ledgr_sweep()can remove persistence overhead without becoming a second execution engine.
ledgr 0.1.7.7
- Added the first ledgr-owned risk-adjusted standard metric,
sharpe_ratio, computed from adjacent public equity-row excess returns with a scalar annual risk-free rate converted geometrically to a per-period return. - Tightened standard metric return-series handling so invalid adjacent equity returns make annualized volatility and Sharpe-style metrics
NAinstead of silently dropping structurally invalid return rows.
ledgr 0.1.7.6
- Completed a DuckDB persistence architecture review covering connection ownership, checkpoint placement, transaction boundaries, shutdown behavior, and DuckDB metadata assumptions.
- Hardened schema validation boundaries so runtime validators remain read-only with respect to ledgr data rows while constraint enforcement is tested in isolated disposable databases.
- Added explicit live DML coverage for
runs.statusandsnapshots.statusconstraints, including valid status values and safe cleanup after expected DuckDB constraint failures. - Added fresh-connection persistence tests proving completed runs, public run metadata mutations, and low-level CSV snapshot create/import/seal/load workflows are readable after reopen.
- Documented a narrow local WSL/Ubuntu DuckDB gate and preserved branch,
main, and tag-triggered CI as separate release evidence. - Routed the v0.1.7.5 auditr retrospective into later roadmap milestones so v0.1.7.6 stays focused on persistence architecture.
ledgr 0.1.7.5
- Hardened the TTR adapter with parity tests across every supported
ledgr_ttr_warmup_rules()entry and resolved the MACD warmup boundary with direct TTR evidence. - Added a user-facing warmup diagnostic for zero-trade runs where a registered feature never becomes finite because an instrument has too few bars.
- Fixed: schema validation probes now issue an explicit rollback after expected constraint violations, preventing DuckDB connection-state contamination on Ubuntu under coverage instrumentation.
- Improved result-inspection documentation with a compact closed-trade example that distinguishes equity, fills, trades, ledger rows, and summary metrics.
- Added a complete low-level CSV snapshot bridge from create/import/seal through verified load, metadata inspection, experiment construction, and
ledgr_run(). - Strengthened indicator, helper, feature-map, and
ctx$features()discoverability while keeping feature-map aliases distinct from engine feature IDs. - Documented ledgr’s adapter posture as a deterministic backtesting core that connects to R finance ecosystem packages through explicit adapter boundaries.
ledgr 0.1.7.4
- Breaking: the raw long feature table on pulse contexts moved from
ctx$featurestoctx$feature_table;ctx$featuresis now the feature-map bundle accessor. This keeps raw inspection data and strategy authoring accessors separate. - Added feature-map authoring UX with
ledgr_feature_map(),ctx$features(), andpassed_warmup()while preserving the existing target-vector execution contract andfeatures = list(...)registration. - Added feature-inspection views with
ledgr_feature_contracts(),ledgr_pulse_features(), andledgr_pulse_wide()so users can inspect contracts, long pulse rows, and stable wide pulse rows. - Resolved auditr documentation findings around hidden vignette helpers, helper-page discovery, feature IDs, warmup diagnosis, TTR-backed indicators, leakage examples, and first-path navigation.
- Fixed the low-level CSV snapshot create/import/seal workflow so sealing derives missing runnable metadata from imported bars and instruments without changing snapshot hash identity.
- Cleaned installed-documentation hygiene, including stale retired
ttr-indicatorsartifacts, package/help-page article links, pkgdown reference entries, and documentation-contract tests.
ledgr 0.1.7.3
- Correctness hardening: aligned equity-curve state, final positions, and summary/comparison metrics against ledger fills, with focused regressions for the v0.1.7.2 auditr accounting finding.
- Metric explainability: documented every metric shown by
summary(),ledgr_compare_runs(), and related result surfaces, and added independent oracle tests that recompute expected values from public result tables. - Documentation discoverability: made installed articles discoverable from package-level and function-level help pages for headless and agent workflows.
- Concept documentation: added installed accounting/metrics and indicators articles, consolidated TTR-backed and built-in indicator teaching, and tighten helper, feature-ID, warmup, and no-trade guidance.
ledgr 0.1.7.2
- Stabilised comparison metrics and zero-row result schemas:
n_tradesnow counts closed trade rows consistently acrosssummary(),ledgr_compare_runs(),ledgr_run_list(), andledgr_results(bt, what = "trades"), whilewhat = "fills"continues to expose execution fill rows. - Improved result-access connection lifecycle: ordinary durable result inspection now opens and closes read connections per operation, and
close()is documented as long-session resource management rather than data-safety ceremony. - Added the strategy-helper value type foundation:
ledgr_signal,ledgr_selection,ledgr_weights, andledgr_target, withledgr_targetunwrapping through the existing target-vector validator. - Added the minimal strategy-helper reference layer:
signal_return(),select_top_n(),weight_equal(), andtarget_rebalance()for long-only helper pipelines that still terminate in normal target quantities. - Improved strategy diagnostics and feature-ID documentation: strategy errors now include pulse timestamp, run, instrument, and available-feature context while preserving the original error as the parent condition.
- Overhauled the strategy-development article around the ledgr pulse mental model, helper functions, feature IDs, warmup behavior, interactive pulse debugging, and run comparison.
- Improved documentation discovery and pkgdown positioning: README now shows noninteractive installed-vignette lookup paths, background positioning articles remain pkgdown-only, and experiment-store docs clarify durable paths, persistent snapshot IDs, labels, tags, CSV snapshot creation, and handle lifecycle framing.
ledgr 0.1.7.1
- Stabilised the installed-package UX after the v0.1.7 experiment-first reset, with clearer start-here documentation, modern base-pipe examples, and a runnable offline workflow built around
ledgr_demo_bars. - Added
ledgr_utc()as a small UTC timestamp helper for examples and user workflows, avoiding repeatedas.POSIXct(..., tz = "UTC")boilerplate. - Made
ledgr_demo_barsandledgr_sim_bars()tibble-friendly for modern R examples. - Added display-only result timestamp printing controls through
options(ledgr.print_ts_utc = "auto"), so all-midnight EOD result tables can print compact dates while underlyingts_utcvalues remain POSIXct UTC. - Verified MACD TTR warmup against direct TTR output for
macd,signal, andhistogramoutputs with bothpercent = TRUEandpercent = FALSE; under the tested TTR version,macdis first valid atnSlow, whilesignalandhistogramare first valid atnSlow + nSig - 1. - Expanded strategy and TTR indicator articles with clearer
ctx,params, feature ID, warmupNA, and quantity-target sizing guidance. - Clarified the experiment-store mental model: sealed snapshots freeze market data, while indicators, features, runs, labels, tags, comparisons, and telemetry are derived artifacts that can be added later.
- Included package vignettes in source builds and declared the vignette build dependencies so GitHub installs with
build_vignettes = TRUEexposevignette(package = "ledgr").
ledgr 0.1.7
Breaking changes
- Began the v0.1.7 experiment-first API reset. The public research workflow now centers on
ledgr_experiment()andledgr_run()rather thandb_path-first calls and directledgr_backtest()usage. - The v0.1.7 strategy contract is
function(ctx, params). Strategies without tunable parameters receiveparams = list(). - The v0.1.7 context target constructors are
ctx$flat()andctx$hold(); the olderctx$targets()andctx$current_targets()helpers now fail with migration guidance.
New features
- Added
ledgr_opening()for explicit opening cash, positions, and optional cost basis. - Added
ledgr_experiment()as the central object for the experiment-first workflow. - Added
ledgr_run()as the public single-run API forledgr_experimentobjects, including run-timefeatures = function(params)evaluation and an explicitseed = NULLidentity field. - Converted experiment-store APIs to snapshot-first signatures and extended
ledgr_snapshot_load()so a durable file with exactly one sealed snapshot can be resumed without retyping the snapshot id. - Added
ledgr_param_grid()as a typed, non-executing parameter-grid object with stable canonical-JSON labels for future sweep/tune workflows. - Added curated print methods for
ledgr_run_list()andledgr_compare_runs()while keeping the underlying objects tibble-compatible. - Added
ledgr_demo_barsandledgr_sim_bars()as deterministic offline demo data for examples and documentation. - Made durable
ledgr_backtesthandles safer to clean up: explicitclose(bt)checkpoints before disconnecting, and a finalizer safety net attempts one auto-checkpoint if a durable handle is garbage-collected without close. - Rewrote README and vignettes around the v0.1.7 experiment-first workflow and added a v0.1.6 to v0.1.7 migration guide.
- Added
ledgr_opening_from_broker()as a reserved adapter hook. v0.1.7 does not ship built-in broker integrations.
ledgr 0.1.6
- Added
ledgr_compare_runs()for comparing completed stored runs from a durable experiment store without rerunning strategies. - Added
ledgr_extract_strategy()for safe strategy-source inspection and optional hash-verified recovery from the experiment store. - Added
ledgr_run_tag(),ledgr_run_untag(), andledgr_run_tags()for mutable run grouping metadata that does not change experiment identity. - Added dedicated experiment-store and strategy-development articles covering run management,
ctx, feature IDs, comparison, and safe source inspection. - Added
ledgr_feature_id()andprint.ledgr_indicator()so strategy authors can discover exact built-in and TTR feature IDs before writingctx$feature()calls. - Expanded the TTR indicator article with feature ID examples for built-in indicators, RSI, ATR, BBands, and MACD.
ledgr 0.1.5
- Added the v0.1.5 experiment-store schema foundation, including schema-version metadata, additive migration hooks, run provenance and telemetry tables, and defensive future-schema checks.
- Added
strategy_paramssupport forfunction(ctx, params)strategies and durable run provenance capture for strategy source hashes, parameter hashes, dependency versions, R version, and reproducibility tier. - Added experiment-store discovery APIs:
ledgr_run_list(),ledgr_run_info(), andledgr_run_open()for listing, inspecting, and reopening completed runs without recomputation. - Added
ledgr_run_label()andledgr_run_archive()for metadata-only run management without changing experiment identity or deleting artifacts. - Added
ledgr_results()as a package-prefixed wrapper for result tables. - Made mistyped
ctx$feature()lookups fail loudly with available feature IDs instead of silently returningNA, while preserving warmupNAbehavior for known features. - Persisted compact run telemetry summaries, including execution mode, elapsed time, pulse count, feature-cache hit/miss counts, and
persist_features;print.ledgr_backtest()andledgr_run_info()now surface execution mode. - Relaxed snapshot ID diagnostics so explicit durable custom IDs do not warn unless they use the generated
snapshot_prefix in a malformed way.
ledgr 0.1.4
- Stabilised the research workflow ahead of the experiment-store APIs, with durable snapshot reuse, safer strategy helpers, faster indicator precomputation, and broader TTR indicator support.
- Added
ledgr_snapshot_load()for reopening existing sealed snapshots from a durable DuckDB file, with optional hash verification. - Updated
ledgr_snapshot_list()so it accepts either a DBI connection or a DuckDB file path. - Added
ctx$current_targets()to runtime and interactive pulse contexts for hold-unless-signal strategy patterns. - Made internal backtest configs an S3
ledgr_configobject with validation and diagnostic printing, while keeping public workflows centered onledgr_backtest(). - Marked
ledgr_data_hash()as a legacy v0.1.0 helper and moved internal run/snapshot-adapter hash call sites to explicitly named internal helpers. - Added optional vectorized indicator
series_fnsupport for full-series feature precomputation, including vectorized built-in indicators. - Added a session-scoped feature cache keyed by snapshot hash, instrument, indicator fingerprint, feature-engine version, and date range, with
ledgr_clear_feature_cache()for explicit cleanup. - Added
ledgr_ind_ttr()andledgr_ttr_warmup_rules()for low-code TTR indicator construction with explicit warmup and fingerprint metadata. - Expanded low-code TTR support to common close, high/low, HLC, and HLCV indicators including WMA, ROC, momentum, CCI, BBands, aroon, DonchianChannel, MFI, CMF, and rolling statistic functions.
- Added
ledgr_deregister_indicator()for cleaning up session-scoped indicator registry entries during interactive work and tests. - Changed fn-only custom indicator fallback from expanding full-history windows to bounded stable windows to avoid accidental O(n^2) feature work.
- Clarified v0.x compatibility policy, strategy reproducibility tiers, next-open fill semantics, and low-level API lifecycle notes in design and reference documentation.
ledgr 0.1.3
- Reworked the README into a 5-minute installed-package path with runnable synthetic data, rendered output, target-vector strategy examples, and an explicit determinism trust check.
- Added a getting-started vignette that walks through the research loop: in-memory bars, strategy authoring, result inspection, pulse debugging, indicators, Yahoo convenience data, CSV snapshots, and durable DuckDB artifacts.
- Added human-readable pulse context helpers for strategy code, including
ctx$close(),ctx$position(),ctx$targets(), and related OHLCV accessors. - Improved target-vector validation errors so unnamed, non-numeric, missing, and extra instrument targets point users to the required contract: a named numeric target vector with names matching
ctx$universe. - Audited exported reference documentation and examples so public examples run offline, use temporary files/databases, guard optional dependencies, and avoid network access.
- Added metric definitions to the reference documentation, including the closed-trade meaning of win rate and average trade.
- Added local and CI release gates for the README cold-start example, acceptance tests,
rcmdcheck, coverage, and pkgdown site builds. - Added a GitHub Pages pkgdown deployment workflow for publishing the package site after the repository is made public.
- Hardened DuckDB run persistence by checkpointing runner-owned write connections before disconnect/shutdown, fixing cross-connection visibility on Ubuntu CI.
- Documented the longer-term experiment-store model: DuckDB files as durable research artifacts, immutable run IDs, strategy identity, reproducibility tiers, and future run discovery/comparison APIs.
ledgr 0.1.2
- Added a data-first
ledgr_backtest(data = bars, ...)convenience path that creates a sealed snapshot and then runs the canonical engine. - Added indicator registry, built-in indicators, adapters, pulse development tools, basic metrics, fill extraction, equity curves, and plotting.
- Strengthened strategy and indicator fingerprints so mutable registry state cannot silently change deterministic replays.
- Exported and documented the v0.1.2 public API surface.
ledgr 0.1.1
- Added snapshot provenance workflow: create, import (CSV), list/info, hash, and seal snapshots.
- Runner can source data from a SEALED snapshot and fails loud on tampering (hash mismatch).
ledgr 0.1.0
- Initial deterministic backtest core spine (schema, config/data hashing, features, strategy contract, fill model, ledger writer, derived state reconstruction, runner, acceptance tests).
- Strategy state persistence/restore for resume-safe strategy state.
- Equity curve reconstruction emits one row per pulse timestamp.
- Added public API functions
ledgr_db_init()andledgr_state_reconstruct().