Analytics calculations¶
fincore.analytics turns normalized market records into normalized metric
events. The purpose is not to predict directly. The purpose is to transform raw
price/yield observations into clean features that downstream systems can store,
stream, compare, graph, or use for inference.
Data flow¶
The current flow is:
raw source row
-> normalized bar
-> metric specification
-> Rust numeric calculation
-> normalized metric event
The analytics layer accepts:
bars from
DataClient.fetch_bars()bar stream envelopes from
DataClient.replay_bars()orstream_bars()external rows from databases, files, queues, or APIs through
field_map
Python handles input normalization, metric specifications, and streaming state. Rust handles the numeric calculations over each symbol’s ordered bars.
Normalized input bar¶
The Rust metric core expects a normalized bar-like record:
{
"symbol": "AAPL",
"event_time": "2024-01-02T00:00:00+00:00",
"interval": "1d",
"close": 101.25,
"volume": 123456.0,
}
Only symbol, event_time, and close are required for the current
metric set. interval and volume are carried into metric metadata when
available.
For external rows, use a field map:
engine.compute(
db_rows,
field_map={
"symbol": "ticker",
"event_time": "ts",
"close": "close_price",
"volume": "share_volume",
},
)
Metric events¶
Every calculation emits the same event shape:
{
"event_type": "metric",
"metric": "return.simple",
"metric_name": "return.simple",
"source": "fincore.analytics",
"symbol": "AAPL",
"event_time": "2024-01-03T00:00:00+00:00",
"value": 0.05,
"unit": "ratio",
"window": {
"kind": "bars",
"size": 1,
},
"inputs": {
"input_field": "close",
"close": 105.0,
"previous_event_time": "2024-01-02T00:00:00+00:00",
},
"metadata": {
"interval": "1d",
"volume": 123456.0,
},
}
This shape is intentionally event-like because it is easy to send through Kafka, store in TimescaleDB, or convert into graph/snapshot features later.
Windows and warm-up¶
window means number of bars, not clock time.
If bars are daily, window=5 means five daily observations. If bars are
five-minute bars, window=5 means five five-minute observations.
Some metrics require previous bars. During the warm-up period, no event is emitted for that metric.
Examples:
return.simpleneeds two bars.momentum.accelerationneeds three bars.volatility.rolling(window=5)needs enough bars to form five returns.auc.window(window=5)needs five bars.
Current metrics¶
return.simple¶
Formula:
(current_close - previous_close) / previous_close
- Meaning:
The percentage-style price move from the previous bar to the current bar.
- Unit:
ratio
Example:
previous_close = 100
current_close = 105
return.simple = (105 - 100) / 100 = 0.05
- Interpretation:
0.05means a 5 percent move up.-0.02means a 2 percent move down.- Why it matters:
Raw prices are not directly comparable across instruments. A 5 point move means something different for a 20 dollar stock and a 500 dollar stock. Simple returns normalize the move by the previous price.
return.log¶
Formula:
ln(current_close / previous_close)
- Meaning:
The logarithmic return from the previous bar to the current bar.
- Unit:
log_ratio
Example:
previous_close = 100
current_close = 105
return.log = ln(105 / 100) = 0.04879
- Interpretation:
Positive values mean price rose; negative values mean price fell.
- Why it matters:
Log returns add cleanly across time. This is useful in quantitative finance when combining returns over multiple bars.
momentum.roc¶
Formula:
(current_close - close_N_bars_ago) / close_N_bars_ago
- Meaning:
Rate of change over a selected lookback window.
- Unit:
ratio
Example with window=3:
close_3_bars_ago = 100
current_close = 112
momentum.roc = (112 - 100) / 100 = 0.12
- Interpretation:
0.12means the instrument is up 12 percent over the selected window.- Why it matters:
This is a basic momentum signal. It describes whether price has been persistently moving up or down across a short history, not only between the latest two bars.
momentum.derivative¶
Formula:
current_close - previous_close
- Meaning:
First derivative of price, approximated as the price difference between two adjacent bars.
- Unit:
price_delta
Example:
previous_close = 100
current_close = 104
momentum.derivative = 104 - 100 = 4
- Interpretation:
Positive means price is moving upward. Negative means price is moving downward.
- Why it matters:
This treats price as a curve. The first derivative is the curve’s local direction and speed. Unlike return, it is not normalized by price.
momentum.acceleration¶
Formula:
current_velocity = current_close - previous_close
previous_velocity = previous_close - close_two_bars_ago
acceleration = current_velocity - previous_velocity
- Meaning:
Second derivative of price, approximated as the change in consecutive price deltas.
- Unit:
price_delta
Example:
close_two_bars_ago = 100
previous_close = 103
current_close = 108
previous_velocity = 103 - 100 = 3
current_velocity = 108 - 103 = 5
momentum.acceleration = 5 - 3 = 2
- Interpretation:
Positive acceleration means the move is strengthening. Negative acceleration means the move is weakening, or the opposite direction is strengthening.
- Why it matters:
Direction alone is often not enough. Acceleration helps distinguish
rising and strengtheningfromrising but fading.
volatility.rolling¶
Formula:
returns = simple returns over the rolling window
volatility.rolling = sample_standard_deviation(returns)
- Meaning:
Recent variability of returns.
- Unit:
ratio
Example:
returns = [0.01, -0.02, 0.015, 0.005]
volatility.rolling = standard deviation of returns
- Interpretation:
Higher values mean recent movement is more unstable. Lower values mean recent movement is tighter.
- Why it matters:
Volatility is a core market feature. It can describe risk, instability, regime changes, and whether price movement is calm or disorderly.
- Implementation note:
The current implementation uses sample standard deviation over simple returns in the selected bar window.
drawdown.current¶
Formula:
(current_close - running_peak_close) / running_peak_close
- Meaning:
Distance from the highest close observed so far in the processed sequence.
- Unit:
ratio
Example:
running_peak_close = 120
current_close = 108
drawdown.current = (108 - 120) / 120 = -0.10
- Interpretation:
-0.10means the instrument is 10 percent below its running peak.0means the current close is at the running peak.- Why it matters:
Drawdown describes damage from a peak. It is useful for trend health, downside pressure, and later risk/regime features.
auc.window¶
Formula:
sum((price_i + price_i+1) / 2 for each adjacent pair in the window)
- Meaning:
Trapezoidal approximation of area under the close-price curve over a rolling window.
- Unit:
price_bars
Example with closes [100, 110, 120]:
area_1 = (100 + 110) / 2 = 105
area_2 = (110 + 120) / 2 = 115
auc.window = 105 + 115 = 220
- Interpretation:
A larger value means the price curve occupied a higher accumulated level over the window.
- Why it matters:
AUC is a compact curve-shape feature. It captures accumulated price level across time, not just the latest point. Later, AUC variants can be applied to returns, momentum, or distance from a baseline.
Batch mode¶
Batch mode computes metrics over a finite list of bars:
from fincore.analytics import MetricEngine, MetricSpec
engine = MetricEngine()
metric_events = engine.compute(
bars,
metrics=[
"return.simple",
"return.log",
MetricSpec("momentum.roc", window=3),
MetricSpec("volatility.rolling", window=5),
MetricSpec("auc.window", window=5),
],
)
Use batch mode for notebooks, backfills, database extracts, and historical research.
Streaming mode¶
Streaming mode updates the engine one event at a time:
engine = MetricEngine()
async for event in client.replay_bars("Apple", "2024-01-01", "2024-01-10"):
metric_events = engine.update(event)
for metric_event in metric_events:
print(metric_event)
The engine keeps a rolling buffer per symbol:
AAPL -> recent AAPL bars
MSFT -> recent MSFT bars
BHP.AX -> recent BHP.AX bars
This lets it calculate rolling and derivative metrics on the fly.
How to read metric events¶
When reading a metric event, ask:
metric: What calculation is this?symbol: Which instrument does it describe?event_time: Which market timestamp does it belong to?value: What is the calculated number?unit: How should the number be interpreted?window: How much history was used?inputs: Which source field was used?metadata: What context came from the original market record?
For future inference, this gives a consistent feature stream:
normalized bars
-> metric events
-> aligned temporal snapshots
-> linked graph state
-> inference engine