tick-data-storage — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited tick-data-storage (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Skill: Tick Data Storage | Domain: trading | Category: data | Level: advanced Tags:trading,data,tick,parquet,hdf5,storage
import MetaTrader5 as mt5
import pandas as pd
import time
from pathlib import Path
def capture_ticks(symbol: str, duration_sec: int = 3600,
save_path: str = "data/ticks/") -> pd.DataFrame:
"""Capture live ticks from MT5 for specified duration."""
mt5.initialize()
Path(save_path).mkdir(parents=True, exist_ok=True)
ticks = []
start = time.time()
last_tick = None
while time.time() - start < duration_sec:
tick = mt5.symbol_info_tick(symbol)
if tick and tick != last_tick:
ticks.append({
"time": pd.Timestamp(tick.time, unit="s"),
"bid": tick.bid, "ask": tick.ask,
"last": tick.last, "volume": tick.volume,
"spread": round((tick.ask - tick.bid) * 10000, 1),
})
last_tick = tick
time.sleep(0.05) # 20Hz polling
mt5.shutdown()
df = pd.DataFrame(ticks).set_index("time")
# Save as Parquet (efficient columnar storage)
fname = f"{save_path}{symbol}_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.parquet"
df.to_parquet(fname, compression="snappy")
return dfdef ticks_to_ohlcv(ticks: pd.DataFrame, freq: str = "5min") -> pd.DataFrame:
"""Aggregate tick data into OHLCV bars at any frequency."""
mid = (ticks["bid"] + ticks["ask"]) / 2
ohlcv = mid.resample(freq).agg(
open="first", high="max", low="min", close="last"
)
ohlcv["volume"] = ticks["volume"].resample(freq).sum()
ohlcv["avg_spread"] = ticks["spread"].resample(freq).mean()
return ohlcv.dropna()| Format | Size | Speed | Use Case |
|---|---|---|---|
| CSV | Large | Slow | Human-readable, small datasets |
| Parquet | Small (10×) | Fast | Production — columnar, compressed |
| HDF5 | Medium | Very fast | ML pipelines — random access |
| Feather | Small | Fastest | In-memory → disk → in-memory cycles |
# Recommended: Parquet for persistence, Feather for ML pipelines
import pyarrow.parquet as pq
def load_ticks(symbol: str, date: str, path: str = "data/ticks/") -> pd.DataFrame:
pattern = f"{path}{symbol}_{date}*.parquet"
import glob
files = glob.glob(pattern)
return pd.concat([pd.read_parquet(f) for f in files]).sort_index()def spread_analysis(ticks: pd.DataFrame) -> dict:
"""Analyze bid/ask spread patterns throughout the day."""
ticks["hour"] = ticks.index.hour
hourly = ticks.groupby("hour")["spread"].agg(["mean", "max", "min"])
widest = hourly["mean"].idxmax()
tightest = hourly["mean"].idxmin()
return {
"avg_spread_pips": round(ticks["spread"].mean(), 2),
"widest_hour_utc": widest,
"tightest_hour_utc": tightest,
"scalp_recommended_hours": hourly[hourly["mean"] < 1.0].index.tolist(),
"avoid_hours": hourly[hourly["mean"] > 2.0].index.tolist(),
}Source: "The Strange Math That Predicts (Almost) Anything" — Veritasium (2025) Added: 2026-03-17 · Relevant skills: statistics-timeseries, quant-ml-trading, ml-trading
"The next state depends ONLY on the current state — not the entire past."
Three components:
| Property | Definition | Trading Use |
|---|---|---|
| Transition Matrix (P) | P[i][j] = prob of moving from state i → j | Regime switching probabilities |
| Stationary Distribution (π) | Long-run % time spent in each state | Expected time in trending vs ranging |
| Mixing Time | Steps until process forgets initial state | Regime persistence measure |
| Ergodicity | Every state reachable from every other | Ensures stationary dist. exists |
Trending Ranging Volatile
Trending [ 0.70 0.20 0.10 ]
Ranging [ 0.25 0.60 0.15 ]
Volatile [ 0.30 0.30 0.40 ]→ Stationary distribution gives long-run % time in each regime
import numpy as np
# Transition matrix
P = np.array([
[0.70, 0.20, 0.10], # from Trending
[0.25, 0.60, 0.15], # from Ranging
[0.30, 0.30, 0.40], # from Volatile
])
# Stationary distribution (solve π = πP)
eigenvalues, eigenvectors = np.linalg.eig(P.T)
stationary = eigenvectors[:, np.isclose(eigenvalues, 1)].real.flatten()
stationary /= stationary.sum()
print(stationary) # [0.47, 0.35, 0.18] → 47% trending, 35% ranging, 18% volatile
# Simulate Markov chain
def simulate(P, start_state, n_steps):
states = [start_state]
for _ in range(n_steps):
states.append(np.random.choice(len(P), p=P[states[-1]]))
return states1. Monte Carlo Method (Manhattan Project, 1940s)
# Monte Carlo equity curve simulation
def monte_carlo(returns, n_sims=1000, n_days=252):
results = []
for _ in range(n_sims):
simulated = np.random.choice(returns, n_days, replace=True)
results.append(np.cumprod(1 + simulated))
return np.array(results)2. Google PageRank
3. Card Shuffling (Mixing Time)
4. Hidden Markov Models (HMM) for Regime Detection
from hmmlearn import hmm
model = hmm.GaussianHMM(n_components=3, covariance_type="diag", n_iter=100)
returns = np.array(daily_returns).reshape(-1, 1)
model.fit(returns)
hidden_states = model.predict(returns)
# 0=low vol, 1=trending, 2=high vol/crisis5. Markov Decision Processes (MDP) — Reinforcement Learning
| Markov Chain | LLM (Claude/GPT) | |||
|---|---|---|---|---|
| Memory | Memoryless (current state only) | Long-range context window | ||
| Prediction | P(next | current) | P(next | all prior tokens) |
| Complexity | O(states²) | O(sequence²) | ||
| Interpretable | Yes | No |
| Application | How |
|---|---|
| Regime detection | HMM on returns → hidden states |
| Regime persistence | Transition matrix diagonal |
| Strategy switching | Trigger on regime change |
| Monte Carlo backtests | Simulate paths from historical returns |
| Position sizing | Weight by stationary distribution |
| Risk modeling | Tail state probabilities |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.