Contents — 26 sections
Project Overview
Time-series models on market data — a rigorous lesson in forecasting, and an honest one about why beating the market is nearly impossible.
Predicting stock movements with machine learning is one of the most popular — and most misunderstood — AI projects, and this one is built to teach it honestly. It applies time-series models to market data to forecast direction or volatility, and in doing so teaches the real, valuable craft of time-series forecasting — while being uncompromisingly clear about the central truth beginners are rarely told: reliably beating the market is extraordinarily hard, bordering on impossible, and any project that claims easy profits is misleading you.
The genuine learning is in the time-series machine learning: framing prices/returns as sequences, engineering features (moving averages, momentum, volatility, technical indicators), and training models (from classical ARIMA to LSTMs and other sequence models) to forecast a future value or the direction of the next move. Crucially, it teaches the discipline that separates rigorous forecasting from self-deception: proper backtesting, avoiding look-ahead bias (never using future information to predict the past), realistic train/test splits that respect time, and honest evaluation including transaction costs.
The value is a serious grounding in time-series forecasting and financial-ML rigor. And the honesty is the whole point and non-negotiable: markets are extremely efficient and near-random in the short term, so most price movement is noise; models that look brilliant in backtests routinely fail live because of overfitting, subtle look-ahead bias, ignored costs, and regime change (the market's behaviour shifts); and this is emphatically not financial advice and must never be used to risk real money on the belief it will beat the market. A responsible version measures itself against honest baselines (a naïve "tomorrow ≈ today" predictor, or buy-and-hold) and treats a small, hard-won edge — or none — as the realistic outcome. Framed this way, it is an excellent, rigorous lesson in time-series ML and financial reality — the opposite of a get-rich scheme.
What this project does
- Forecasts market direction/volatility with time-series models
- Frames prices/returns as sequences with features
- Trains classical (ARIMA) or sequence (LSTM) models
- Backtests properly (time-respecting, costs included)
- Avoids look-ahead bias and overfitting traps
- Compares against honest baselines
- Teaches time-series ML and financial reality honestly
Real-World Applications
| Setting | How it is used |
|---|---|
| Time-series ML learning | Forecasting methods and rigorous evaluation. |
| Financial-ML rigor | Backtesting, look-ahead bias, costs, baselines. |
| General forecasting | Techniques transfer to demand/energy/etc. |
| Research / education | An honest look at market predictability. |
Deployment contexts where a build of this kind earns its keep.
Features & Capabilities
- Time-series forecasting (classical + deep)
- Feature engineering (MA, momentum, volatility)
- Rigorous backtesting (no look-ahead, with costs)
- Time-aware train/test splits
- Baseline comparisons (naïve/buy-and-hold)
- Overfitting/regime-change awareness
- Honest: not financial advice; beating the market is very hard
Difficulty, Time & Required Skills
| Attribute | Value |
|---|---|
| Difficulty level | Advanced |
| Estimated completion time | 12–18 hours |
| Indicative build cost | Software; compute-light to moderate |
| Primary discipline | Predictive ML |
| Reference platform | CPU/GPU workstation or server |
Skills you should have (or will pick up)
- Time-series framing and feature engineering
- Classical and deep sequence models
- Rigorous backtesting (no look-ahead, costs)
- Time-aware evaluation and baselines
- Financial-ML honesty (efficiency, overfitting, regimes)
Bill of Materials
Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.
| Component | Key specification | Qty | Approx. cost |
|---|---|---|---|
| Compute | CPU for classical/features; GPU for deep sequence models | 1 | — |
| Time-series libraries | ARIMA / LSTM / feature tools + backtesting | 1 | — |
| Market data Avoid look-ahead | Historical prices/returns (clean, point-in-time) | 1 | — |
| Baselines Beat these honestly, or not | Naïve and buy-and-hold benchmarks | 1 | — |
Estimated total: ₹0, excluding tools, shipping and consumables.
Tools and consumables
- Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
- Digital multimeter — continuity, DC volts and current ranges
- Wire strippers, flush cutters and a small set of precision screwdrivers
- Heat-shrink tubing and a heat gun (or a lighter, carefully)
- A laptop with a USB port and the toolchain listed above
Hardware Specifications
This is a pure-software forecasting system — no electronic hardware to specify. The "platform" is a computer: a CPU handles classical models, features and backtesting; a GPU accelerates deep sequence models (LSTMs).
Memory scales with the length/breadth of market data; storage holds historical series and backtest results. This is a learning/research tool, not a trading system, and carries no financial-advice function. Everything else is the software stack, models and libraries below.
Software Requirements & Development Environment
Reference toolchain: Python 3.11 + time-series ML. Anything newer normally works; anything older may lack the board definitions used here.
- Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
- Add
https://espressif.github.io/arduino-esp32/package_esp32_index.jsonunder File → Preferences → Additional Board Manager URLs, then install esp32 from the Boards Manager. - Set the correct port under Tools → Port. On Linux add yourself to the
dialoutgroup:sudo usermod -aG dialout $USERand log out and back in. - Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
- Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.
Required libraries
| Library | Why it is needed | Install |
|---|---|---|
| Python 3.11+ | Runtime for the analysis, training and service code. | sudo apt install python3 python3-venv python3-pip |
| pandas 2.2+ | Tabular data loading, cleaning and time-series resampling. | pip install pandas |
| scikit-learn 1.5+ | Classical models, preprocessing pipelines and evaluation metrics. | pip install scikit-learn |
| PyTorch 2.4+ | Model definition, autograd and GPU training. | pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 |
| NumPy 1.26+ | Vectorised array maths underpinning every other library here. | pip install numpy |
Block Diagram
The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.
Circuit Diagram & Wiring
The "wiring" is the forecasting data flow — historical market data becomes time-series features, a model forecasts direction/volatility, and a rigorous backtest (time-aware, cost-inclusive) evaluates it against honest baselines.
| Peripheral | Peripheral pin | Controller pin | Signal |
|---|---|---|---|
| Market data | prices/returns | — | Historical series |
| Features | engineer | — | MA/momentum/vol |
| Model | forecast | — | Direction/volatility |
| Backtest | evaluate | — | vs baselines (costs) |
Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.
Wiring explanation
- Use clean, point-in-time historical data (avoid future leakage).
- Engineer time-series features (moving averages, momentum, volatility).
- Train a classical or deep sequence model to forecast direction/volatility.
- Backtest with time-aware splits and transaction costs; no look-ahead bias.
- Compare against naïve and buy-and-hold baselines — honestly.
System Architecture
Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.
Working Principle
This project is unusual in that its most important lesson is a warning, and teaching it honestly matters more than any accuracy number. Predicting stock prices with ML is enormously popular and enormously over-promised: countless tutorials show a model that appears to forecast prices beautifully, and countless beginners conclude they can beat the market. The reality — which a responsible project must foreground — is that reliably beating the market is extraordinarily hard, bordering on impossible, and the impressive-looking results are almost always artefacts of methodological errors. The value of the project is real, but it lies in learning rigorous time-series forecasting and financial-ML discipline, not in getting rich.
The genuine, transferable skill is time-series machine learning. You frame the market as a sequence (prices, or better, returns), engineer features that summarise recent behaviour (moving averages, momentum, volatility, technical indicators), and train models to forecast a future value or, more sensibly, the direction of the next move or its volatility. The model space spans classical statistics (ARIMA and friends) to deep sequence models (LSTMs, temporal networks). These are powerful, broadly useful techniques — the exact same craft applies to forecasting energy demand or sales — which is why the project is worth doing even though the market itself resists prediction.
What separates rigorous forecasting from self-deception is methodological discipline, and financial time series punish sloppiness brutally. Backtesting must respect time: you train on the past and test on the future, never shuffling, because the sequence is the whole point. Look-ahead bias — accidentally using information that would not have been available at prediction time (a future price, a feature computed with future data, survivorship-biased data) — is the classic error that makes a useless model look prophetic, and it is subtle and everywhere. Realistic evaluation must include transaction costs (a strategy that "works" before costs often loses after them), and must be measured against honest baselines: does the fancy model actually beat a naïve "tomorrow ≈ today" predictor, or beat simply buying and holding? Most do not.
The deep reason the market resists prediction is worth internalising: markets are highly efficient, meaning available information is already priced in, so short-term price movement is dominated by noise and is close to a random walk — there is very little learnable signal, and any edge is quickly arbitraged away. On top of that, models suffer overfitting (with enough features and tuning you can fit historical noise perfectly, and it means nothing out of sample) and regime change (the market's statistical behaviour shifts — a model trained on one regime fails in the next). So a backtest that looks brilliant routinely dies live. The honest conclusion, and the correct framing, is that this is not financial advice and must never be used to risk real money on the belief that it beats the market; the realistic outcome of a rigorous project is a tiny, fragile edge or, far more often, none — which is itself the valuable, true lesson. Built this way — real time-series ML, ruthless backtesting discipline, honest baselines, and clear-eyed humility about efficiency, overfitting and regimes — it is an excellent education in forecasting and financial reality, and the exact opposite of the get-rich scheme it is so often mistaken for.
The maths behind it
Time-series forecasting
frame as a sequence (use RETURNS, not raw prices):
features: moving avgs, momentum, volatility, indicators
forecast: next return / direction / volatility
models: ARIMA ... LSTM / temporal nets
The genuine, transferable skill.
Rigorous backtesting (or you fool yourself)
train on PAST, test on FUTURE (never shuffle time)
NO look-ahead: only use info available at prediction time
include TRANSACTION COSTS
compare vs BASELINES: naïve (t+1 ≈ t), buy-and-hold
Most "great" backtests fail these.
Why it (usually) does not work
efficient markets → short-term ≈ random walk (mostly noise)
overfitting → fit historical noise, meaningless out-of-sample
regime change → behaviour shifts; model breaks live
NOT financial advice. Realistic edge: tiny/fragile, or none.
Program Flowchart
The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.
Assembly Instructions
Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.
Frame the time series and features
Use returns, engineer features (moving averages, momentum, volatility) without future information, and split by time.
Train and backtest rigorously
Train a classical or deep sequence model time-aware, and backtest with no look-ahead bias and transaction costs.
Compare to honest baselines
Measure against a naïve predictor and buy-and-hold; treat "no edge" as the realistic, valuable result.
Step-by-Step Implementation Guide
Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.
Backtest honestly against baselines
Evaluate the strategy time-ordered with costs and no look-ahead, comparing to buy-and-hold and reporting an honest verdict.
pythonbacktest.pyimport numpy as np def backtest(returns, signals, cost=0.001): # signals[t] must use ONLY information available up to time t (no look-ahead) strat = signals[:-1] * returns[1:] # realise NEXT return strat -= cost * np.abs(np.diff(np.r_[0, signals]))[:-1] # transaction costs return {"strategy": float(np.nansum(strat)), "buy_and_hold": float(np.nansum(returns[1:]))} # honest baseline def verdict(r): return ("small edge — be sceptical (overfit/regime luck?)" if r["strategy"] > r["buy_and_hold"] else "no edge vs buy-and-hold — the usual honest result")# signals[t] must use ONLY information available up to time t (no look-ahead)The cardinal rule: signals may only use past information, or the backtest is fiction — look-ahead bias is what makes useless models look prophetic.strat = signals[:-1] * returns[1:] # realise NEXT returnA signal at time t is realised on the next period's return — acting after the decision, respecting time.strat -= cost * np.abs(np.diff(np.r_[0, signals]))[:-1] # transaction costsTransaction costs are subtracted, since strategies that "work" before costs routinely lose after them.if r["strategy"] > r["buy_and_hold"]The strategy is judged against simply buying and holding — the honest baseline most strategies fail to beat.Report honestly — not advice
Present out-of-sample, cost-inclusive results versus baselines, and treat a tiny/no edge as the truthful outcome — never as investment advice.
Complete Source Code
The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.
#!/usr/bin/env python3
"""
Stock Trend Predictor (time-series ML — taught HONESTLY)
Applies time-series models (ARIMA ... LSTM) to market data to forecast
direction/volatility. The REAL value is rigorous forecasting: time-aware
splits, NO look-ahead bias, transaction costs, and honest BASELINES.
Markets are near-random short-term; reliably beating them is nearly
impossible. NOT financial advice. Do NOT risk real money on this.
"""
import numpy as np
def make_features(returns, lookback=10):
# features from PAST returns only (no future information)
X, y = [], []
for t in range(lookback, len(returns) - 1):
window = returns[t-lookback:t]
X.append([window.mean(), window.std(), # momentum, volatility
returns[t-1], np.sign(window.sum())]) # last, trend sign
y.append(np.sign(returns[t+1])) # next direction
return np.array(X), np.array(y)
def time_split(X, y, frac=0.7):
n = int(len(X) * frac)
return X[:n], y[:n], X[n:], y[n:] # train PAST, test FUTURE
def backtest(returns, signals, cost=0.001):
strat = signals[:-1] * returns[1:] # act on signal
strat -= cost * np.abs(np.diff(np.r_[0, signals]))[:-1] # costs
return {"strategy": float(np.nansum(strat)),
"buy_and_hold": float(np.nansum(returns[1:]))} # baseline
if __name__ == "__main__":
X, y = make_features(RETURNS)
Xtr, ytr, Xte, yte = time_split(X, y) # never shuffle time
model.fit(Xtr, ytr) # keep it simple
signals = model.predict(Xte)
result = backtest(RETURNS[-len(signals):], signals) # net of costs
edge = result["strategy"] > result["buy_and_hold"]
print("small edge (be sceptical)" if edge else
"no edge vs buy-and-hold — the honest, usual result")
# NOT financial advice; beating the market reliably is extraordinarily hard.
Configuration & Calibration
Configuration steps
- Configure data (point-in-time returns), features (no look-ahead) and lookback.
- Configure the model (classical/deep) — keep it simple to resist overfitting.
- Configure time-aware splits, transaction costs and backtesting.
- Configure baselines (naïve, buy-and-hold) for honest comparison.
Calibration procedure
An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.
No leakage
Audit features/splits for any use of future information; fix look-ahead bias.
Realistic backtest
Include transaction costs; test out-of-sample; compare to baselines.
Overfitting
Watch the in-sample vs out-of-sample gap; prefer simple models.
Dataset, Model & Training
Dataset
Clean, point-in-time historical market data (prices/returns). Survivorship bias and look-ahead in data are common, dangerous errors.
Features are engineered from the series; the honesty is in the evaluation, not the data volume.
| Dataset | Size | Licence | Use here |
|---|---|---|---|
| Historical prices/returns | Long series | Data-provider terms | Model input (point-in-time) |
| Technical indicators | Derived | — | Features (MA/momentum/vol) |
| Baselines | — | — | Naïve / buy-and-hold comparison |
| Out-of-sample period | Held-out future | — | Honest backtest |
Data preprocessing
- Use returns (stationary) rather than raw prices; align point-in-time.
- Engineer features WITHOUT future information (no look-ahead).
- Split by TIME (train past, test future); never shuffle.
| Layer / stage | Shape or configuration | Purpose |
|---|---|---|
| Feature engineering | MA/momentum/volatility | Summarise recent behaviour |
| Model | ARIMA … LSTM/temporal | Forecast direction/volatility |
| Time-aware split | train past / test future | No leakage |
| Backtester | costs + no look-ahead | Honest performance |
| Baselines | naïve / buy-and-hold | Reality check |
Hyperparameters
| Hyperparameter | Value | Why |
|---|---|---|
| Target | direction/volatility | Sensible vs raw price |
| Lookback window | tuned | Context vs noise |
| Model complexity | low (careful) | Overfitting risk |
| Costs | included | Realistic net result |
Training process
- Train time-aware; keep models simple to resist overfitting.
- Never let future data leak into features/splits.
- Evaluate net of costs against naïve and buy-and-hold baselines.
Evaluation, Metrics & Deployment
The honest metrics are out-of-sample, cost-inclusive performance versus baselines — not in-sample accuracy, which is trivially inflated.
| Metric | Value | What it tells you |
|---|---|---|
| Out-of-sample (net of costs) | usually ≈ baseline | The honest number |
| vs naïve baseline | rarely beats | Reality check |
| vs buy-and-hold | rarely beats | Reality check |
| Overfitting gap | watch | In-sample vs out-of-sample |
Figures from the reference training run described above — reproduce them before trusting your own changes.
Inference example
import numpy as np
def backtest(returns, signals, cost=0.001):
# Time-ordered; signals[t] uses ONLY info up to t (no look-ahead).
strat = signals[:-1] * returns[1:] # act on signal, realise next return
strat = strat - cost * np.abs(np.diff(np.r_[0, signals]))[:-1] # transaction costs
return {
"strategy_return": float(np.nansum(strat)),
"buy_and_hold": float(np.nansum(returns[1:])), # honest baseline
}
def honest_verdict(result):
beat = result["strategy_return"] > result["buy_and_hold"]
return ("A small edge (be sceptical: overfit? regime luck?)" if beat
else "No edge vs buy-and-hold — the usual, honest result.")
# NOT financial advice. Beating the market reliably is extraordinarily hard.
Testing Procedure & Expected Output
Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.
| Test | What you should see |
|---|---|
| Train time-aware | No shuffling; past→future split |
| Audit for look-ahead | No future info in features/splits |
| Backtest with costs | Net performance, often ≈ baseline |
| Compare to buy-and-hold | Rarely beats it (honest) |
| Check in vs out-of-sample | Large gap = overfitting |
| Interpret a "great" result | Suspect a bug/leakage first |
Bench-test checklist. If a row fails, stop and fix it before moving on.
Expected output
Honest, cost-inclusive, out-of-sample results versus baselines — usually showing little or no edge.
{
"strategy_return_net": 0.031,
"buy_and_hold_return": 0.058,
"look_ahead_bias": "audited: none",
"costs_included": true,
"verdict": "no edge vs buy-and-hold — the honest, usual result",
"note": "NOT financial advice"
}
A rigorously backtested strategy underperforming buy-and-hold net of costs — the honest, common outcome, and exactly the valuable lesson: reliably beating the market is extraordinarily hard.
Troubleshooting: Common Errors & Fixes
Performance Optimisation
- Do the time-series ML rigorously — that is the real value.
- Eliminate look-ahead bias; split by time; include costs.
- Compare to naïve and buy-and-hold baselines honestly.
- Prefer simple models; watch for overfitting and regime change.
- Replace every
delay()with amillis()comparison — blocking delays are the single most common cause of dropped readings. - Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
- Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
- Use
uint8_t/uint16_twhere the range allows; on an 8-bit AVR a 32-bit add costs four times as much. - Profile before optimising — print
micros()deltas around each stage and fix the slowest one first.
Safety Precautions
- This is NOT financial advice — never risk real money on the belief it beats the market.
- Beating the market reliably is extraordinarily hard; treat "no edge" as the honest, expected result.
- A great-looking backtest almost always hides look-ahead bias, leakage, ignored costs or overfitting.
- Do not present forecasts as reliable predictions of the future.
- Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
- Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
- Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.
Maintenance
- Re-audit for leakage whenever features/data change.
- Re-test out-of-sample; expect regime-driven decay.
- Keep baselines and cost assumptions realistic.
- Resist the temptation to over-tune to history.
- Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
- Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
- Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.
Future Improvements & Upgrades
A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.
- Apply the same rigor to non-market forecasting (energy/demand).
- Add proper walk-forward validation and uncertainty estimates.
- Study market microstructure and why edges vanish.
- Explore risk/volatility forecasting (more tractable than direction).
- Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
- Add connectivity — an ESP32 and an MQTT publish turn a local gadget into something you can graph, alert on and analyse over months.
- Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
- Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
- Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
- Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.
Frequently Asked Questions
References & Learning Resources
These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.
- Time series forecastingReference
- Efficient-market hypothesisReference
- Look-ahead bias / backtestingReference
- OverfittingReference
- ARIMA / LSTMReference