JSONL snapshot recorder for trading data.
Writes timestamped snapshots (tickers, OI, greeks, walls) as one JSON object per line for replay and backtesting analysis.
Entry Format
{"ts":"2026-02-23T12:00:00Z","type":"ticker","data":{"symbol":"BTC/USDT","last":95000.5}}
{"ts":"2026-02-23T12:00:00.123456Z","type":"greeks","data":{"delta":0.55}}ts— ISO8601 timestamptype— optional snapshot label (omitted when not provided)data— the payload (any JSON-serializable term)
Usage
# Write snapshots
JSONL.append("/tmp/session.jsonl", %{symbol: "BTC/USDT", last: 95000.5}, type: :ticker)
JSONL.append("/tmp/session.jsonl", %{delta: 0.55}, type: :greeks)
# Read back
{:ok, entries} = JSONL.read_all("/tmp/session.jsonl")
# Stream lazily
"/tmp/session.jsonl"
|> JSONL.stream()
|> Stream.filter(&match?({:ok, _}, &1))
|> Enum.each(fn {:ok, entry} -> process(entry) end)
# File metadata
{:ok, info} = JSONL.info("/tmp/session.jsonl")
# => %{count: 2, first_ts: ~U[...], last_ts: ~U[...], duration_ms: 123, types: %{"ticker" => 1, "greeks" => 1}}API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
info | 1 | Return metadata about a JSONL file (count, timestamps, duration, type frequencies). | path: value |
read_all | 1 | Read all valid entries from a JSONL file (skips malformed lines). | path: value |
stream | 1 | Stream entries lazily from a JSONL file. | path: value |
write_many | 2 | Write multiple entries to a JSONL file in a single operation. | path: value, entries: value |
append | 3 | Append a single entry to a JSONL file. | path: value, data: value |
decode_entry | 1 | Decode a JSONL line string into an entry map with atom keys. | line: value |
encode_entry | 2 | Encode a data payload into a JSONL line string. | data: value |
Summary
Functions
Appends a single entry to a JSONL file.
Decodes a JSONL line string into an entry map with atom keys.
Encodes a data payload into a JSONL line string.
Returns metadata about a JSONL file.
Reads all valid entries from a JSONL file.
Streams entries lazily from a JSONL file.
Writes multiple entries to a JSONL file.
Functions
Appends a single entry to a JSONL file.
Creates the file and parent directories if they don't exist.
Options
Same as encode_entry/2: type: and ts:.
Examples
:ok = JSONL.append("/tmp/session.jsonl", %{last: 95000.5}, type: :ticker)
Decodes a JSONL line string into an entry map with atom keys.
Known keys (:ts, :type, :data) are converted to atoms.
The :ts value is parsed back to a DateTime.
Examples
{:ok, entry} = JSONL.decode_entry(~s({"ts":"2026-02-23T12:00:00Z","type":"ticker","data":{"last":95000.5}}))
entry.type
# => "ticker"
entry.ts
# => ~U[2026-02-23 12:00:00Z]
Encodes a data payload into a JSONL line string.
Returns a single-line JSON string (no trailing newline).
Options
type:— atom label (:ticker,:orderbook, etc.), converted to stringts:— DateTime override (default:DateTime.utc_now())
Examples
entry = JSONL.encode_entry(%{symbol: "BTC/USDT", last: 95000.5}, type: :ticker)
# => ~s({"ts":"2026-02-23T12:00:00Z","type":"ticker","data":{"symbol":"BTC/USDT","last":95000.5}})
entry = JSONL.encode_entry(%{delta: 0.55})
# => ~s({"ts":"...","data":{"delta":0.55}})
Returns metadata about a JSONL file.
Return Value
%{
count: integer(),
first_ts: DateTime.t() | nil,
last_ts: DateTime.t() | nil,
duration_ms: integer(),
types: %{String.t() => pos_integer()}
}Examples
{:ok, info} = JSONL.info("/tmp/session.jsonl")
info.count
# => 150
info.types
# => %{"ticker" => 100, "greeks" => 50}
Reads all valid entries from a JSONL file.
Malformed lines are skipped silently. Returns only successfully decoded entries.
For strict parsing, use stream/1 and handle errors explicitly.
Examples
{:ok, entries} = JSONL.read_all("/tmp/session.jsonl")
@spec stream(Path.t()) :: Enumerable.t()
Streams entries lazily from a JSONL file.
Each element is {:ok, entry_map} or {:error, reason}.
Raises File.Error if the file doesn't exist (streaming a nonexistent file
is a programming error).
Blank lines are skipped. Malformed lines yield {:error, reason}.
Examples
"/tmp/session.jsonl"
|> JSONL.stream()
|> Stream.filter(&match?({:ok, _}, &1))
|> Enum.each(fn {:ok, entry} -> process(entry) end)
Writes multiple entries to a JSONL file.
Creates the file and parent directories if they don't exist.
Each entry is a tuple of {data, opts} matching encode_entry/2 args.
Examples
entries = [
{%{last: 95000.5}, type: :ticker},
{%{delta: 0.55}, type: :greeks}
]
:ok = JSONL.write_many("/tmp/session.jsonl", entries)