Skip to content
vp.Ven Petrov
WorkWritingAboutAt a glance
← Projects & notes
MNQ time-series researchExperiment · 5 min read

Time-series prediction experiments

An exploration of high-resolution market data: choosing what to acquire, checking its limits, comparing time intervals and testing whether a prediction survived later data and the cost of acting on it.

Ven PetrovResearch results

I built a custom NinjaTrader data extractor, but at the time I could access only three months of history. I looked for a longer, high-resolution time series to explore whether the data contained patterns a model could use and test them across different periods.

The work started before model training, with decisions about which data to obtain and how much history I could afford. It continued into a Python research pipeline using MNQ price and volume data, Polars for preparation and LightGBM for prediction. Claude Code handled implementation and runs under my direction.

What detail was worth acquiring?

Earlier acquisition work used Databento. I asked to see actual cost estimates before building the download pipeline, then asked how much one-second history the available credit could cover. More history would give the evaluation more periods to work through.

The choice was between individual transaction records and summaries of each second. The recorded estimates put two years of MNQ transaction data well beyond the budget. The acquisition used roughly two and a half years of one-second summaries instead.

Each summary, often called a bar, contains the opening, highest, lowest and closing price plus total volume. It preserves much more timing detail than daily data, while losing the sequence of individual transactions within that second. It also doesn't provide the number of transactions or the actual buying and selling activity separately. Those limits determine which questions the dataset can answer.

I had noticed differences between the NQ and MNQ activity data in the earlier work. In the later model project, I corrected an assumption that NQ should be the evaluation dataset: checks of the MNQ model needed more MNQ data. NQ could be a separate comparison later.

Make the input consistent, then question the resolution

The preparation code checks required fields, converts timestamps to UTC, sorts observations and removes duplicate timestamps. A separate inspection script reports date ranges, price ranges, average volume and duplicate counts. The target builder leaves an example out when its required future observation is missing or falls across a session boundary.

These checks help make the input consistent. They don't establish complete coverage. The acquisition notes flagged three degraded days, and those warnings need explicit treatment in a fresh evaluation. The small one-day file used to check that the pipeline runs also overlaps the later evaluation period; the research runner excludes it from the dataset.

The next question was how much of the original timing detail to retain. Combining five seconds into one row reduces the data to process, but could remove a pattern. I questioned that change and asked for comparisons: first five and ten seconds, then progressively wider intervals, eventually extending the search up to an hour.

Aggregation keeps the first opening price, the highest and lowest prices, the last closing price and summed volume for each interval. The selected configuration used 20-minute summaries to predict the next 20-minute price return. Starting with detailed data let us test that choice instead of committing to one interval at acquisition.

The interval search reused the research period. It selected a configuration; it didn't independently establish that the result would continue.

Test whether a prediction would be useful

For a finance application, a prediction must also survive the cost of acting on it. The evaluator selected the strongest positive and negative estimates as simulated trades. Tightening that selection changed the research result:

Selection in the research period Simulated trades Approximate result after primary costs
Top and bottom 10% 3,907 -$905
Top and bottom 3% 2,605 +$2,351

The primary assumption charged $1.50 for entering and leaving a trade. At $2.25, the research gain fell to about $397. That extra $0.75 across 2,605 simulated trades consumed $1,953.75. These are approximate historical simulation outputs; the conversion from returns to dollars uses a representative price.

Evaluation moved through time: train on six earlier months, evaluate on roughly the next month, then move forward and repeat. Within the September 2023 through November 2025 research dataset, 14 of 21 evaluation windows were positive under primary costs.

The selected configuration was then carried into three additional windows covering December 2025 through February 2026. All three lost money: about $737, $750 and $241. Together that was -$1,728 across 353 simulated trades, or about -$1,993 with the higher cost assumption.

SAVED FOLD CSV / APPROXIMATE SIMULATED P&L, USD

21 research folds, then three losing folds.

Historical simulated profit and loss across 24 foldsResearch folds one through twenty-one total $2,350.90. Later folds twenty-two through twenty-four total -$1,728.24. All three later folds are negative. Exact values are in the expandable table.RESEARCH · 01–21LATER-2,500-1,25001,2502,500Fold 1: -$308.07, 52 simulated trades1Fold 2: -$1,379.34, 408 simulated tradesFold 3: $697.12, 24 simulated tradesFold 4: $455.43, 22 simulated trades4Fold 5: -$435.00, 84 simulated tradesFold 6: $1,509.77, 193 simulated tradesFold 7: $841.68, 22 simulated trades7Fold 8: -$791.77, 396 simulated tradesFold 9: -$498.26, 107 simulated tradesFold 10: $62.12, 174 simulated trades10Fold 11: $634.76, 22 simulated tradesFold 12: $357.03, 25 simulated tradesFold 13: -$93.28, 168 simulated trades13Fold 14: $96.96, 24 simulated tradesFold 15: $195.83, 193 simulated tradesFold 16: -$2,122.38, 228 simulated trades16Fold 17: $26.21, 24 simulated tradesFold 18: $331.58, 24 simulated tradesFold 19: $643.97, 24 simulated trades19Fold 20: $281.04, 36 simulated tradesFold 21: $1,845.50, 355 simulated tradesFold 22: -$736.84, 113 simulated trades22Fold 23: -$750.43, 218 simulated trades23Fold 24: -$240.97, 22 simulated trades24fold
Research · 21 folds$2,350.90
Later · 3 folds-$1,728.24
Inspect all 24 fold values
FoldPeriodP&L · USDTrades
1Research-$308.0752
2Research-$1,379.34408
3Research$697.1224
4Research$455.4322
5Research-$435.0084
6Research$1,509.77193
7Research$841.6822
8Research-$791.77396
9Research-$498.26107
10Research$62.12174
11Research$634.7622
12Research$357.0325
13Research-$93.28168
14Research$96.9624
15Research$195.83193
16Research-$2,122.38228
17Research$26.2124
18Research$331.5824
19Research$643.9724
20Research$281.0436
21Research$1,845.50355
22Later-$736.84113
23Later-$750.43218
24Later-$240.9722
Recomputed from saved walk-forward CSVs. Different period lengths; not annualized or live returns. The evaluator uses a representative price, outer validation for early stopping and whole-fold prediction percentiles. See the article for these limits.

The research notes had called the earlier result a breakthrough. The later entry says DO NOT TRADE this configuration as-is. The later data changed the conclusion.

What needs a cleaner evaluation?

The time split removes the prediction horizon plus an additional hour from the end of training. For the 20-minute prediction, that leaves an 80-minute gap before evaluation. This prevents training targets near the boundary from extending into that evaluation window.

Approximate evaluation windows

Train on earlier bars, evaluate on later bars

Fold 1
6 months · traingapevaluate
Fold 2
6 months · traingapevaluate
Fold 3
6 months · traingapevaluate
The split includes temporal purging and a gap. Outer-fold early stopping and whole-fold thresholds still limit what this evaluation establishes.

Two implementation details still weaken that separation. The final model uses the later evaluation answers to decide when training stops. The evaluator also calculates its selection cutoffs from all eligible predictions in each evaluation window:

# Shortened illustration of the current selection rule.
upper_threshold = np.percentile(predictions, 97)
lower_threshold = np.percentile(predictions, 3)

At the start of that period, the rest of those predictions would not yet exist. A new evaluation needs training decisions and selection cutoffs determined using earlier information.

The existing 126 causality and time-split tests check useful properties, including whether adding future rows changes an earlier feature value. They don't resolve these dependencies or establish profitable execution.

For an AI team working with time-series data, the useful decisions here begin before the model: what information to buy, what aggregation removes, how to handle incomplete observations and what a later period can test. This experiment produced a rejected configuration and a clearer specification for the next evaluation.

Project sources & checks
Data acquisition decisions

Earlier MNQ acquisition work records Databento cost estimates, completed one-second OHLCV downloads and degraded-day warnings. Direct Claude Code requests establish the preference to estimate costs first and investigate longer history.

Local repository and code review

mnq-nq-xgboos-lightgbm: research/notes.md, scripts/01_prepare_data.py, scripts/02_run_walkforward.py, src/mnq_research/data_io.py, labels.py, splits.py and train.py.

Recorded experiment outputs

Recomputed totals from metrics_by_fold.csv in runs 20260410_124942 and 20260410_125941; these are approximate historical simulations.

Focused verification

126 existing causality and temporal-split tests passed locally. This does not establish that the entire evaluation is free of bias.

On this page
What detail was worth acquiring?Make the input consistent, then question the resolutionTest whether a prediction would be usefulWhat needs a cleaner evaluation?Sources & checks
PythonLightGBMData engineeringValidation

Connected to this

All writing
NQ/MNQ backtest engine · Technical note

A missing closing bar left the simulator’s short position open

Small-cap edge research · Project story

Turning small-cap trading ideas into repeatable research

Ven PetrovSoftware engineer · Barcelona
GitHub ↗LinkedIn ↗Email ↗

Find a project or a note

17 results

Transfutur ERP · ExperienceERP for transport and logisticsAs the sole engineer, I built an ERP from zero for transport and logistics. It supported roughly €5–6 million in annual invoicing across two offices, and I kept it running for about a year.Financial review lab · Project storyExploring a financial report with Python and FastAPII started with one income change, then opened up the same 54-page report: products, regions, cash flow, core adjustments and a dataset whose figures lead back to the source.Crude-oil narrative tracker · Project storyCrude-oil research from global news and market dataI wanted help following global events that could matter for crude oil. The prototype combines news research with calculated market and position values, while keeping recent assessments separate from the longer numeric history.MNQ time-series research · ExperimentTime-series prediction experimentsAn exploration of high-resolution market data: choosing what to acquire, checking its limits, comparing time intervals and testing whether a prediction survived later data and the cost of acting on it.NQ/MNQ backtest engine · Technical noteA missing closing bar left the simulator’s short position openThe strategy reset for a new day while a shared counter still counted yesterday’s short. A small replay shows why closing the position has to happen before clearing its state.Small-cap edge research · Project storyTurning small-cap trading ideas into repeatable researchI asked Claude Code for a fresh research system that could test named price patterns, compare later periods and carry its methodology into new sessions. One candidate shows how features, entry timing and costs fit together.Gadgeon Europe applications · ExperienceConnected-device dashboards and mobile apps at Gadgeon EuropeI worked on Ionic and Angular applications that brought several APIs into dashboards for energy monitoring and connected-device control, with releases and maintenance across multiple client versions.ctxforge · Project storyctxforge: discovering what to build before codingA context discovery framework that asks an LLM to act like a senior engineering interviewer: understand the idea, uncover requirements and agree the scope before an agent starts building.Sociedades Inactivas enquiries · Technical noteSaving an enquiry before trying to send its emailOn the Sociedades Inactivas catalogue, a visitor can ask about a company or send a general enquiry. The form separates saving that request from delivering its notification, including when the visitor retries.Astro local-services website · Project storyA local-services site assembled from city, service and FAQ recordsThe Astro site describes flooring and other local services across different cities. Shared templates check availability, read the matching content and select one FAQ set for both visitors and structured data.3D Spaces website migration · Project storyMoving 3D Spaces to Next.js while its email stayed on the old serverI rebuilt the virtual-tour website with AI coding collaborators, preserving its content and URLs. The cutover also had to protect the mailbox, enquiry flow and existing analytics history.Capture CRM · Project storyConnecting classified listings to contacts in a small CRMA Chrome extension and Next.js dashboard bring classified listings, platform profiles and contact history together. Saving a listing again can add the person who was missing from an earlier import.Product-description comparison bench · ExperimentComparing one model call with separate vision and writing stepsA small product-description tool lets me inspect the visual analysis before the copy is written, change the model used for each step and compare request measurements.Between Us · Project storyBetween Us: why I kept the first take and widened the shotI compared dialogue takes, asked for wider shots and more physical acting, then changed the workflow when its studio limited the scenes I wanted to make.Restaurant-service voice agent · ExperimentDesigning a restaurant-service call around the real offerI worked through a Spanish outbound agent prompt, corrected a free visit to the actual paid service and compared how two drafts handled transfers and bookings.Vapi and Telnyx voice integration · Technical noteThe Vapi agent could speak, but it could not hear me clearlyMy Telnyx number connected to the assistant, but the caller audio was distorted. Three codec tests recovered the connection without fixing that original problem.Warloop: Echoes of Solmara · Project storyWarloop: turning generated character art into a playable prototypeGood-looking character images became tiny figures, a backwards walk and misplaced bosses in the game. I worked through the gap between generated art and playable behavior.
esc to close · Tab to move through results