Strategy pulse context accessors
ledgr_strategy_context.RdThe strategy pulse context is the object passed to every
function(ctx, params) strategy at one decision time. It exposes only
data that is known at that pulse: current bars, registered current feature
values, current simulated positions, cash, equity, and helpers for building
target vectors.
Details
Common accessors:
ctx$ts_utc: current decision timestamp.ctx$universe: character vector on the current decision axis. In availability-aware runs this can include held nonmembers that remain necessary for valuation and disposition.ctx$members: the current allocation population. In a dense run it isctx$universe.ctx$vec$member,ctx$vec$target_restricted,ctx$vec$target_restriction_reasonandctx$vec$admissible: the eligibility planes, aligned toctx$universe, in every run. An instrument is admissible when it is a member and not target-restricted. In a dense run every instrument is a member, none is restricted, every reason is""and every instrument is admissible, so a rule masked withctx$vec$admissibleworks unchanged in both kinds of run. The context helpers, includingledgr_passed_warmup(ctx, values), consider admissible instruments only, and fail when an availability context lacks a well-formedctx$vec$admissibleplane.ctx$idx(id): 1-based universe position for one instrument.ctx$open(id),ctx$high(id),ctx$low(id),ctx$close(id),ctx$volume(id): current bar values for one instrument.ctx$vec$open,ctx$vec$high,ctx$vec$low,ctx$vec$close,ctx$vec$volume: current bar values for the full universe, aligned toctx$universe.ctx$bar(id): the current OHLCV row for one instrument.ctx$feature(id, feature_id): one feature value for one instrument, addressed by an active alias or concrete engine feature ID.ctx$vec$feature(feature_id): one active alias or concrete feature ID across the full universe, aligned toctx$universe.ctx$features(id, feature_map): mapped feature values for one instrument, returned as a named numeric vector keyed by feature-map aliases.ctx$position(id): current simulated position for one instrument.ctx$vec$position: current simulated positions for the full universe, aligned toctx$universe.ctx$cashandctx$equity: current simulated portfolio state.ctx$state_prev: thestate_updatethe strategy returned at the previous pulse.ctx$flat(): full named target vector initialized to zero.ctx$hold(): full named target vector initialized to current positions.ctx$tradable(): convenience subset of current members that are admissible and priced in an availability-aware run; in a dense run it returns instruments with a finite positive close. A permissible stale mark can satisfy the predicate without supplying a current sizing close, and the result does not promise execution at the next open.
Singular reads resolve an active feature-map alias first, then a concrete
engine ID from ledgr_feature_id(). ctx$features() returns one
instrument's mapped values named by alias. Unknown names fail loudly and list
active aliases separately from concrete IDs; warmup for a known feature is
represented by NA_real_. No plural whole-universe accessor is added:
read each required alias once with ctx$vec$feature().
For universes of at least 100 instruments, reading one name through
ctx$feature() once per instrument emits one
ledgr_scalar_accessor_loop warning per run and names
ctx$vec$feature(feature_id) as the whole-universe alternative. An
active-map ctx$features(id) loop warns at the same axis-sized threshold
and names the active aliases to read through that plane. Explicit-map bundle
reads remain unrecorded because the plane does not accept an explicit map.
ctx$idx(id) fails loudly for unknown instruments by default. Use
ctx$idx(id, missing = "na") only when an explicit NA_integer_
sentinel is part of the strategy logic.
ctx$position(id) is the scalar pulse-start state read and
ctx$vec$position is its unnamed decision-axis-aligned plane. Use
ctx$idx(id) to cross between them. ctx$hold() constructs named
portfolio intent from that state; editing its result does not mutate either
state read.
A strategy that returns list(targets = ..., state_update = ...) receives
that state_update as ctx$state_prev at the next pulse; returning a
bare target keeps the previous state. State must be JSON-serializable plain
data. In a dense run ctx$state_prev is NULL on the first pulse. In
an availability-aware run it is a list from the first pulse and carries
asset_state, a list keyed by the instrument IDs in ctx$universe:
entries for instruments that leave the axis are dropped, returning instruments
start from list(), and a returned entry for an ID outside the axis fails
with ledgr_invalid_strategy_state.
The structured result may also include intent_reason, either NULL
or one non-empty character scalar. When supplied, ledgr records the value once
at portfolio level for that invoked pulse in
ledgr_results(run, "diagnostics"); availability-aware
ledgr_run_explain() joins it to the existing instrument explanation.
Prefer short snake-case values. This optional note does not change strategy
state, targets, execution, accounting or identity, and is not a general factor
or prediction store.
Strategies should return full named numeric target vectors. The helpers
ctx$flat() and ctx$hold() are the safest starting points because
they already have one entry for every instrument in ctx$universe.
In availability-aware runs, ctx$universe is the decision axis and
ctx$members is the current allocation population; held nonmembers may
remain on the former without entering the latter. In a dense run the two are
the same. Use ctx$tradable() as
the explicit convenience predicate for admissible and priced members. A stale
permissible mark can satisfy that predicate while still failing current-close
sizing, so it guarantees neither a sizing close nor execution at the next open.
Complete Public Surface
The table below is authoritative for callback-visible, non-private members.
Daxis means ctx$universe; every plane is positional and unnamed.
Cross a plane with IDs through ctx$idx(). Dot-prefixed engine state is
private and deliberately absent.
scope | member | applies | shape | axis | units | missingness | counterpart
top | run_id | all | character_scalar | none | identifier | never | none
top | ts_utc | all | character_scalar | none | UTC_time | never | none
top | universe | all | character_vector | Daxis | instrument_id | empty_allowed | vec.id
top | bars | all | data_frame | current_Daxis_rows | market_data | rows_may_be_absent | bar
top | feature_table | all | data_frame | current_Daxis_rows | feature_value | warmup_NA_or_schema_only | feature
top | cash | all | numeric_scalar | portfolio | currency | never | none
top | equity | all | numeric_scalar | portfolio | currency | never | none
top | seed | all | nullable_scalar | none | integer_seed | NULL_allowed | none
top | pulse_seed | all | nullable_scalar | none | integer_seed | NULL_allowed | none
top | state_prev | all | nullable_list | portfolio_and_Daxis | strategy_state | NULL_on_first_dense_pulse | none
top | availability_active | availability | logical_scalar | none | flag | never | none
top | members | all | character_vector | Daxis_subset | instrument_id | empty_allowed | vec.member
top | bar | all | function | instrument_id | OHLCV_row | absent_row_errors | bars
top | open | all | function | instrument_id | price | NA_allowed | vec.open
top | high | all | function | instrument_id | price | NA_allowed | vec.high
top | low | all | function | instrument_id | price | NA_allowed | vec.low
top | close | all | function | instrument_id | price | NA_allowed | vec.close
top | volume | all | function | instrument_id | source_units | NA_allowed | vec.volume
top | position | all | function | instrument_id | quantity | never | vec.position
top | idx | all | function | instrument_id | Daxis_index | optional_NA | vec.id
top | flat | all | function | Daxis | target_quantity | never | none
top | hold | all | function | Daxis | target_quantity | never | vec.position
top | tradable | all | function | Daxis_subset | instrument_id | empty_allowed | mode_specific_planes
top | features_wide | all | data_frame | current_Daxis_rows | feature_value | warmup_NA | features
top | feature | all | function | instrument_id | feature_value | warmup_NA | vec.feature
top | features | all | function | instrument_id | feature_value | warmup_NA | vec.feature
top | vec | all | list | Daxis | mixed | plane_specific | scalar_counterparts
plane | id | all | character_vector | Daxis | instrument_id | never | universe
plane | open | all | numeric_vector | Daxis | price | NA_allowed | open
plane | high | all | numeric_vector | Daxis | price | NA_allowed | high
plane | low | all | numeric_vector | Daxis | price | NA_allowed | low
plane | close | all | numeric_vector | Daxis | price | NA_allowed | close
plane | volume | all | numeric_vector | Daxis | source_units | NA_allowed | volume
plane | position | all | numeric_vector | Daxis | quantity | never | position
plane | feature | all | function | Daxis | feature_value | warmup_NA | feature
plane | member | all | logical_vector | Daxis | flag | never | members
plane | held | availability | logical_vector | Daxis | flag | never | none
plane | target_restricted | all | logical_vector | Daxis | flag | never | none
plane | target_restriction_reason | all | character_vector | Daxis | reason_token | empty_string_allowed | none
plane | admissible | all | logical_vector | Daxis | flag | never | tradable
plane | priced | availability | logical_vector | Daxis | flag | never | tradable
plane | mark_age | availability | integer_vector | Daxis | sessions | NA_when_unpriced | none
plane | risk_mark | availability | numeric_vector | Daxis | price | NA_when_unpriced | none
plane | mark_source | availability | character_vector | Daxis | source_token | empty_when_unpriced | none
Feature Object Compatibility
ledgr_experiment(features = ...) accepts static indicators, lists of
indicators, named lists, and feature maps as the canonical public feature
shapes. Compatibility feature factories of the form function(params)
remain available for old flat-grid and advanced exact-ID workflows, but new
parameterized feature sweeps should use active aliases with
ledgr_feature_map(). Pulse inspection helpers accept static feature
shapes. Inside the strategy context, ctx$feature() reads one engine
feature ID and ctx$features() requires a ledgr_feature_map.
Lower-level or legacy helpers may be narrower; the experiment-first workflow is
the canonical public path for feature maps.
Articles
Strategy authoring:
vignette("strategy-development", package = "ledgr")
system.file("doc", "strategy-development.html", package = "ledgr")
Indicator IDs and feature maps:
vignette("indicators", package = "ledgr")
system.file("doc", "indicators.html", package = "ledgr")
Examples
features <- ledgr_feature_map(
ret_5 = ledgr_ind_returns(5),
sma_10 = ledgr_ind_sma(10)
)
strategy <- function(ctx, params) {
targets <- ctx$flat()
for (id in ctx$universe) {
x <- ctx$features(id, features)
if (ledgr_passed_warmup(x) && x[["ret_5"]] > params$min_return) {
targets[id] <- params$qty
}
}
targets
}