YuriyKolesnikov
diffquant
Pythonโœจ New

End-to-End Differentiable Trading Pipeline

Last updated Apr 28, 2026
13
Stars
2
Forks
0
Issues
0
Stars/day
Attention Score
45
Language breakdown
No language data available.
โ–ธ Files click to expand
README

DiffQuant

End-to-End Differentiable Trading Pipeline

Python 3.10+ PyTorch 2.0+ License: MIT


Contents


Most ML trading systems face the same structural gap: the model optimizes a proxy โ€” MSE, cross-entropy, TD-error โ€” while performance is measured in realized PnL. A better-fitting proxy does not guarantee better actual returns.

DiffQuant closes this gap by design. The pipeline from raw market features through a differentiable mark-to-market simulator to the Sharpe ratio is a single computation graph. loss.backward() optimizes what the strategy actually earns, not a surrogate for it.

Research article (English ยท Medium):
DiffQuant: End-to-End Sharpe Optimization Through a Differentiable Trading Simulator

ะกั‚ะฐั‚ัŒั (ะ ัƒััะบะธะน ยท Habr):
DiffQuant: ะฟั€ัะผะฐั ะพะฟั‚ะธะผะธะทะฐั†ะธั ะบะพัั„ั„ะธั†ะธะตะฝั‚ะฐ ะจะฐั€ะฟะฐ ั‡ะตั€ะตะท ะดะธั„ั„ะตั€ะตะฝั†ะธั€ัƒะตะผั‹ะน ั‚ะพั€ะณะพะฒั‹ะน ัะธะผัƒะปัั‚ะพั€


How it works

The full pipeline is a single differentiable computation graph:

features[tโˆ’ctx:t] โ†’ PolicyNetwork โ†’ position_t โ†’ DiffSimulator โ†’ โˆ’Sharpe โ†’ โˆ‚/โˆ‚ฮธ

The simulator implements exact mark-to-market accounting as tensor operations โ€” no surrogate losses, no reward shaping. The entire chain is differentiable:

rett      = (closet โˆ’ close{tโˆ’1}) / close{tโˆ’1}
grosst    = position{tโˆ’1} ร— ret_t
costt     = smoothabs(ฮ”pos_t) ร— (commission + slippage)
netpnlt  = grosst โˆ’ costt

smooth_abs(x) = โˆš(xยฒ + ฮต) replaces |x| to preserve Cโˆž differentiability through transaction cost computation โ€” critical when the model operates near flat.

Policy head: direction ร— gate

position = tanh(directionraw / ฯ„dir) ร— sigmoid(gateraw / ฯ„gate)

direction encodes the alpha signal; gate encodes whether to trade at all. When confidence is low, gate โ†’ 0 and position โ†’ 0 regardless of direction โ€” the differentiable analogue of action masking. Gate bias is initialized to โˆ’1.0, so the model starts in a near-flat regime and opens positions only when accumulated gradient evidence justifies the exposure. This stabilizes early training when policy outputs are noisy.

Training objective

L = ฮปโ‚ยท(โˆ’Sharpe) + ฮปโ‚‚ยทturnover + ฮปโ‚ƒยทdrawdown + ฮปโ‚„ยทterminal + ฮปโ‚…ยท(flatsoft โˆ’ flattarget)ยฒ + ฮปโ‚†ยท|mean(pos)|

Each term addresses a specific failure mode: turnover prevents commission drag; drawdown discourages extended underwater periods; terminal penalizes open risk at episode end; flat_target prevents permanent flat collapse; bias penalizes always-long or always-short behavior, which proved critical for symmetric long/short learning on trending BTC training data.


Validation protocol

Both training validation and backtest use continuous walk-forward evaluation, identical to live execution mechanics:

for t in range(ctx_len, N):
    window   = features[t โˆ’ ctx : t]       # past ctx bars only
    position = model(normalize(window))     # single forward pass
    pnlt    = prevpos ร— ret_t โˆ’ commission ร— |ฮ”pos|
    # position carried to next bar โ€” no resets

The same WalkForwardEvaluator runs during training (every val_freq epochs) and at final evaluation. There is no separate validation logic anywhere else in the codebase. This is intentional.


Quick start

git clone https://github.com/YuriyKolesnikov/diffquant
cd diffquant
pip install -r requirements.txt

Download 1-min BTCUSDT 2021โ€“2025 from HuggingFace

huggingface-cli download ResearchRL/diffquant-data --local-dir data_source/ --repo-type dataset

Verify gradient flow and trend learning before training

python sanitycheck.py --config configs/experiments/itransformerhybrid.py

Expected output:

PASS gradient_flow all params receive gradient

PASS longbias meanposition=+0.19 expected_sign=+

PASS shortbias meanposition=-0.16 expected_sign=-

ALL PASSED

Train primary experiment

python train.py --config configs/experiments/itransformer_hybrid.py --device cuda

Optional: find optimal thresholds on the val set

python optimizethresholds.py --config configs/experiments/itransformerhybrid.py --trials 100 --objective sharpe

Evaluate on held-out test and backtest splits

python evaluate.py --config configs/experiments/itransformer_hybrid.py

Evaluate with final model checkpoint

python evaluate.py --config configs/experiments/itransformerhybrid.py --checkpoint output/itransformerhybrid/models/final.pth

Hyperparameter search

python optimize.py --config configs/experiments/itransformer_hybrid.py --trials 100

Compare all completed experiments

python compare.py

Structure

diffquant/
โ”œโ”€โ”€ configs/
โ”‚   โ”œโ”€โ”€ base_config.py          # MasterConfig โ€” single source of all hyperparameters
โ”‚   โ””โ”€โ”€ experiments/            # One file per experiment; overrides selectively
โ”œโ”€โ”€ data/
โ”‚   โ”œโ”€โ”€ pipeline.py             # loadorbuild() โ€” MD5-cached dataset construction
โ”‚   โ”œโ”€โ”€ aggregator.py           # 1-min โ†’ N-min, clock-aligned resampling
โ”‚   โ”œโ”€โ”€ features.py             # Log-returns, volume ratios, cyclic time encoding
โ”‚   โ”œโ”€โ”€ splitter.py             # Temporal split by datetime boundary
โ”‚   โ”œโ”€โ”€ dataset.py              # TradingDataset (full ctx+hor sequences)
โ”‚   โ””โ”€โ”€ normalization.py        # Per-sample z-score, no look-ahead
โ”œโ”€โ”€ model/
โ”‚   โ”œโ”€โ”€ backbone/
โ”‚   โ”‚   โ”œโ”€โ”€ itransformer.py     # Channel-wise attention (Liu et al., ICLR 2024)
โ”‚   โ”‚   โ””โ”€โ”€ lstm_encoder.py     # Bidirectional LSTM encoder
โ”‚   โ”œโ”€โ”€ policy_head.py          # direction ร— gate two-headed output
โ”‚   โ””โ”€โ”€ policy_network.py       # Backbone + head โ†’ position โˆˆ (โˆ’1, +1)
โ”œโ”€โ”€ simulator/
โ”‚   โ”œโ”€โ”€ diffsimulator.py       # Mark-to-market PnL, smoothabs, SimConfig
โ”‚   โ””โ”€โ”€ losses.py               # sharpe / sortino / hybrid
โ”œโ”€โ”€ training/
โ”‚   โ””โ”€โ”€ trainer.py              # DiffTrainer โ€” episode rollout + walk-forward val
โ”œโ”€โ”€ evaluation/
โ”‚   โ”œโ”€โ”€ walk_forward.py         # Continuous evaluation engine (val + backtest)
โ”‚   โ””โ”€โ”€ backtest.py             # Full reporting wrapper
โ”œโ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ metrics.py              # All financial metrics โ€” one location
โ”‚   โ”œโ”€โ”€ logging_utils.py        # MetricsLogger โ€” val JSONL + full reports
โ”‚   โ”œโ”€โ”€ utils.py                # Auxiliary functions
โ”‚   โ””โ”€โ”€ visualization.py        # Equity curves, position distribution
โ”œโ”€โ”€ sanity/
โ”‚   โ””โ”€โ”€ checks.py               # Gradient flow + trend bias checks
โ”œโ”€โ”€ train.py
โ”œโ”€โ”€ evaluate.py
โ”œโ”€โ”€ sanity_check.py
โ”œโ”€โ”€ optimize.py                 # Optuna hyperparameter search
โ”œโ”€โ”€ optimize_thresholds.py      # Optuna threshold selection on val
โ””โ”€โ”€ compare.py                  # Experiment comparison table

Experiments

| Config | Backbone | Loss | Purpose | |---|---|---|---| | itransformer_sharpe | iTransformer | โˆ’Sharpe only | Ablation: loss function contribution | | itransformer_hybrid | iTransformer | Hybrid | Primary experiment | | lstm_hybrid | LSTM (bidir.) | Hybrid | Backbone comparison |

The three configs share the same data, simulator, and evaluation protocol. Any performance difference is attributable to architecture or loss function alone.


Configuration

# Minimal override example
from configs.base_config import MasterConfig

cfg = MasterConfig(experimentname="itransformerhybrid") cfg.backbone.type = "itransformer" cfg.loss.type = "hybrid" cfg.loss.lambda_turnover = 0.01 cfg.loss.lambda_bias = 0.25 # penalises always-long / always-short cfg.loss.lambdaflattarget = 0.5 # prevents permanent flat collapse cfg.data.preset = "ohlcv" # open, high, low, close, volume cfg.data.addrollingvol = True # causal rolling volatility channel cfg.data.timeframe_min = 30 # aggregate 1-min source to 30-min bars

Feature presets: "ohlc" | "ohlcv" (default) | "full" | "custom".


Dataset

| | | |---|---| | Asset | BTCUSDT Binance Futures (USDโ“ˆ-M perpetual) | | Source resolution | 1-minute bars (close-time convention) | | HuggingFace | ResearchRL/diffquant-data | | Period | 2021-01-01 โ€” 2025-12-31 |

Dataset: HuggingFace Hub

Temporal splits (all non-overlapping):

| Split | Period | Purpose | |---|---|---| | Train | 2024-01-01 โ†’ 2025-03-31 | Gradient updates | | Val | 2025-04-01 โ†’ 2025-06-30 | Model selection during training | | Test | 2025-07-01 โ†’ 2025-09-30 | Out-of-sample evaluation | | Backtest | 2025-10-01 โ†’ 2025-12-31 | Final held-out evaluation |

Aggregation from 1-min to any target timeframe uses origin="epoch" alignment, ensuring bars always land on clock boundaries (:00, :05, :10, โ€ฆ for 5-min). The primary experiment uses 30-min bars: context = 96 bars (48 hours), horizon = 24 bars (12 hours).

The training window is intentionally limited to 15 months (January 2024 โ€“ March 2025) rather than the full historical record. This keeps the training regime temporally close to the evaluation periods and minimises distribution shift. Extending to earlier data is straightforward via SplitConfig.train_start and is the recommended first ablation.


Experimental status

DiffQuant is an active research project. The results below represent the first promising configuration found during initial experimentation. The pipeline is hyperparameter-sensitive โ€” loss weights, learning rate, training window, and feature composition interact non-trivially. Results vary across configurations and market regimes. The codebase is designed to support systematic reproduction and further experimentation.

This is research, not a production-ready system.


Results

Experiment: itransformer_hybrid

Configuration summary:

  • Backbone: iTransformer (dmodel=32, nlayers=4) โ€” 52K parameters
  • Features: ohlcv + rolling_vol (6 channels), 30-min bars
  • Training data: Jan 2024 โ†’ Mar 2025 (15 months, 910 non-overlapping samples)
  • Loss: Hybrid (Sharpe + drawdown + flat_target + bias)
  • Training: 30 epochs, lr=1e-3, mirror_augmentation=True
Why a small model and non-overlapping samples: 910 training samples is intentionally small. With stride=horizon_len=24, each sample covers a distinct 12-hour market window with no overlap, preventing the model from memorizing sequential price paths. A 52K-parameter model on 910 independent episodes is deliberately capacity-constrained to resist microstructure noise.

Training dynamics

| Epoch | Val Sharpe | Val Return | Flat% | Turnover/bar | |---|---|---|---|---| | 2 | โˆ’6.49 | โˆ’14.1% | 1.6% | 0.0335 | | 8 | โˆ’0.64 | โˆ’0.4% | 77.5% | 0.0003 | | 12 | +0.46 | +0.9% | 17.7% | 0.0035 | | 20 | +1.21 | +5.2% | 15.4% | 0.0101 | | 30 | +1.25 | +5.8% | 13.2% | 0.0135 |

Training passes through two sequential failure modes before settling into a workable regime: hyperactivity at epoch 2 (turnover = 0.0335, val Sharpe = โˆ’6.49), followed by flat collapse at epoch 8 (flat fraction = 77.5%). The hybrid loss resolves both. Best checkpoint saved at epoch 30, with val Sharpe still improving at run end, consistent with the hypothesis that the model had not yet fully converged.

Train Loss Val Sharpe

Val Flat Fraction Val Max Drawdown

Walk-forward evaluation

All evaluation uses the continuous walk-forward protocol, identical to live execution mechanics. No look-ahead, no episode resets.

Test โ€” Julyโ€“September 2025 (3 months, out-of-sample)

| Metric | Value | |---|---| | Sharpe (ann.) | +1.735 | | Sortino (ann.) | +2.173 | | Calmar | 1.346 | | Total return | +8.22% | | Max drawdown | 6.10% | | Commission paid | 2.50% | | Rebalances | 79 | | Long / Short / Flat | 66.9% / 17.3% / 15.8% |

Backtest โ€” Octoberโ€“December 2025 (final hold-out, never touched during training)

| Metric | Value | |---|---| | Sharpe (ann.) | +1.152 | | Sortino (ann.) | +1.250 | | Calmar | 0.874 | | Total return | +6.91% | | Max drawdown | 7.91% | | Commission paid | 2.60% | | Rebalances | 76 | | Long / Short / Flat | 53.3% / 20.9% / 25.8% |

Test Walk-Forward Evaluation

Backtest Walk-Forward Evaluation

Test Position Analysis Backtest Position Analysis

Key observations

Positive Sharpe on both held-out periods. Test (+1.73) and backtest (+1.15) are both positive. A single positive out-of-sample quarter can be coincidence; consistency across two non-overlapping quarters is a stronger signal.

Asymmetric learning from asymmetric data. The model was trained exclusively on Jan 2024 โ€“ Mar 2025, a predominantly bullish period for BTC. With mirroraugmentation=True, the training loop augments 50% of batches by inverting price returns, forcing symmetric long/short learning. Result: 17โ€“21% short exposure in evaluation despite the long-biased training regime. Without augmentation, most prior configurations produced shortfraction close to zero.

Direction accuracy near 50% โ€” and that is not a weakness. Long correct: 50.1โ€“51.2%, short correct: 48.8โ€“52.6%. The model's edge does not come from directional prediction accuracy but from asymmetric sizing: correctavgret (+0.065%) systematically exceeds incorrectavgret (โˆ’0.061%) in magnitude. The gate mechanism selectively suppresses low-confidence trades, improving the signal-to-noise ratio of executed positions.

Gate activation remains low. Mean gate โ‰ˆ 0.12โ€“0.13. The model operates at partial exposure rather than full commitment, consistent with the conservative risk posture driven by lambdabias and flattarget regularization.


Limitations

Single asset. The pipeline trains one model per instrument. Multi-asset portfolio construction requires architectural extension.

Flat commission model. Costs are commissionrate + slippagerate applied uniformly. For positions large enough to move price, enable marketimpacteta in SimulatorConfig (quadratic Almgren-Chriss term).

Hyperparameter sensitivity. Loss weights, training window, feature composition, and learning rate interact non-trivially. This is not a plug-and-play recipe. It is a research framework that requires systematic tuning.

Limited statistical base. Two quarters is sufficient for a meaningful signal, but not for strong claims about statistical significance or long-term stability.

No live execution layer. There is no broker-facing execution module. Connecting to an exchange API requires additional engineering outside the current scope.


Roadmap

Multi-asset portfolio extension. The current architecture optimizes a single position. Extending to a portfolio requires a cross-asset attention layer and a portfolio-level Sharpe objective that accounts for position correlations. The differentiable simulator generalizes naturally: grosst = ฮฃ weights{i,t-1} ร— ret_{i,t}.

Richer loss functions. The hybrid loss is a first approximation. Planned extensions include Calmar-based objectives, conditional drawdown penalties, and regime-aware loss weighting that adjusts ฮป values based on detected volatility regime.

Additional backbones. The LSTM encoder is implemented but not yet benchmarked against iTransformer under identical conditions. Planned ablations include PatchTST, Mamba, and linear attention variants.

Online data pipeline. A scheduled data collection layer (Binance WebSocket โ†’ local store โ†’ feature pipeline โ†’ model inference) to support paper trading and live monitoring.

Execution and risk layer. A broker-facing execution module with position sizing, stop-loss enforcement, and exchange connectivity. This is the final engineering step before any live deployment and is outside the current research scope.


Related work

**Buehler, H., Gonon, L., Teichmann, J., Wood, B. (2019). Deep Hedging. Quantitative Finance, 19(8), 1271โ€“1291.** The foundational framework for training neural network policies end-to-end through a differentiable financial objective. DiffQuant adapts this paradigm from derivatives hedging to directional alpha generation.

**Liu, Y., Hu, T., Zhang, H., Wu, H., Wang, S., Ma, L., Long, M. (2024). iTransformer: Inverted Transformers Are Effective for Time Series Forecasting. ICLR 2024 Spotlight.** The backbone used in the primary DiffQuant experiment. Treats each feature channel as a token, capturing cross-variable dependencies rather than local temporal patterns.

**Moody, J., Saffell, M. (2001). Learning to trade via direct reinforcement. IEEE Transactions on Neural Networks, 12(4), 875โ€“889.** The original formulation of direct PnL optimization as a training objective, predating the deep learning era. DiffQuant extends this to a fully differentiable end-to-end pipeline with modern architectures.

**Khubiev, K., Semenov, M., Podlipnova, I., Khubieva, D. (2026). Finance-Grounded Optimization For Algorithmic Trading. arXiv:2509.04541.** The closest parallel work: introduces Sharpe, PnL, and MaxDD as training loss functions for return prediction. DiffQuant differs by coupling the loss to a fully differentiable simulator, so gradient flows through the trading mechanics, not just through a prediction head.


Citation

@software{Kolesnikov2026diffquant,
  author  = {Kolesnikov, Yuriy},
  title   = {{DiffQuant}: End-to-End Differentiable Trading Pipeline},
  year    = {2026},
  url     = {https://github.com/YuriyKolesnikov/diffquant},
  version = {0.1.0}
}

MIT License. See LICENSE.

๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท YuriyKolesnikov/diffquant ยท Updated daily from GitHub