Multi-Task Learning for Simultaneous Price, Volume, and Volatility Prediction
Multi-task learning (MTL) biasanya dijual dengan satu pernyataan: bagikan encoder di antara target yang berkorelasi dan tugas utama akan menjadi lebih baik. Dalam trading, target yang berkorelasi itu jelas — return, volume, dan volatilitas terealisasi semuanya berasal dari order flow yang sama — dan pernyataan itu hampir tidak pernah diuji. Pertanyaan yang menarik bukan apakah tugas-tugas tersebut berkaitan. Pertanyaannya adalah apakah gradient yang dibagikan selaras, dan apa yang terjadi pada fold ketika tidak selaras.
Artikel ini menempatkan dua hal di pusat pembahasan yang biasanya hanya diperlakukan sebagai catatan kaki dalam tulisan MTL:
- Penyeimbangan loss adalah eksperimen, bukan detail. Bobot tetap, pembobotan ketidakpastian Kendall, dan GradNorm adalah tiga model yang berbeda. Jalankan ketiganya pada fold yang sama dan laporkan bobot yang dipelajari bersama metrik tugas utama untuk masing-masing.
- Negative transfer dapat diukur sebelum metrik terlihat. Kesamaan kosinus antara gradient tugas pada encoder bersama memberi tahu Anda selama training apakah tugas tambahan menarik representasi ke arah yang diinginkan tugas utama. Beri tanda pada kosinus, lalu periksa apakah tandanya memprediksi hasil pada fold tersebut.
Hal lain dalam pipeline — proses volatilitas, training loop, kontrol kebocoran, protokol validasi — sudah dibahas di tempat lain di blog ini dan ditautkan, bukan diturunkan ulang.
Setup

Diberikan fitur input (OHLCV, indikator teknikal, order flow), terdapat tiga target:
- Tugas 1 (utama): return periode berikutnya
- Tugas 2 (tambahan): log volume periode berikutnya
- Tugas 3 (tambahan): volatilitas terealisasi periode berikutnya
Model multi-tugas menghasilkan ketiganya secara bersamaan, , dan risiko multi-tugas adalah jumlah berbobot dari risiko setiap tugas:
Seluruh artikel ini membahas dan dampak gradient setiap tugas terhadap tugas lainnya.
Mengapa training bersama mungkin membantu, dalam satu paragraf. Tugas tambahan membatasi representasi bersama agar menjelaskan lebih dari satu fenomena pasar, yang sekaligus menjadi kontrol kapasitas dan bias induktif; dan karena volume serta volatilitas diamati langsung sementara "expected return" tidak, head tambahan memberikan sinyal gradient yang lebih bersih daripada head utama. Argumen untuk satu model yang menghasilkan banyak output dibahas panjang — bersama machinery interpretabilitasnya — dalam temporal fusion transformers untuk peramalan multi-horizon, yang membuat argumen encoder-bersama-banyak-head yang sama untuk quantile multi-horizon.
Arsitektur secara singkat
Hard parameter sharing: encoder bersama memberi input kepada head khusus tugas , sehingga . Inilah versi yang diukur di sini, karena inilah versi ketika konflik gradient pada terdefinisi dengan baik.
Soft parameter sharing memberi setiap tugas encoder sendiri dengan penalti coupling — lebih banyak parameter, lebih banyak fleksibilitas, dan tidak ada satu vektor parameter bersama untuk mengukur konflik. Cross-stitch networks berada di tengah, dengan mencampur fitur per tugas melalui matriks terpelajar pada setiap level. Keduanya layak dicoba jika hard sharing menunjukkan konflik, dan keduanya berada di luar cakupan pengukuran di bawah.
Eksperimen yang Penting: Tiga Skema Penyeimbangan Loss

Loss naif sensitif terhadap skala. Jika loss return berada sekitar dan loss volume sekitar , volume menguasai gradient dan head return kelaparan. Tiga respons:
Bobot tetap. Tetapkan setelah menstandarkan setiap target. Baseline yang jujur — jika ia menang, skema adaptif hanyalah formalitas.
Pembobotan ketidakpastian (Kendall et al., 2018). Pelajari skala noise homoskedastik untuk setiap tugas:
Tugas dengan ketidakpastian tinggi otomatis diberi bobot lebih rendah; suku menghalangi solusi trivial . Perhatikan bahwa ini adalah perangkat pembobotan loss saat training, bukan interval prediktif — untuk ketidakpastian yang benar-benar dapat digunakan dalam menentukan ukuran posisi, lihat conformal prediction.
GradNorm (Chen et al., 2018). Seimbangkan magnitudo gradient, bukan skala loss. Pada setiap langkah: hitung dan rata-rata , hitung laju training relatif , lalu perbarui . Semua tugas kemudian training dengan laju yang sebanding terlepas dari skala loss.
Kode khusus MTL adalah head, forward yang mengembalikan daftar, dan agregasi loss. Stack Linear/BatchNorm/ReLU/Dropout, boilerplate Adam/cosine/clip, dan epoch loop adalah pola standar yang ditunjukkan dalam DeepLOB dan dihilangkan di sini.
import torch
import torch.nn as nn
class MultiTaskTradingModel(nn.Module):
"""Hard parameter sharing: one encoder, K heads."""
def __init__(self, encoder: nn.Module, repr_dim: int, n_tasks: int = 3):
super().__init__()
self.shared_encoder = encoder # any MLP/CNN/GRU trunk
self.task_heads = nn.ModuleList(
nn.Linear(repr_dim, 1) for _ in range(n_tasks)
)
def forward(self, x):
h = self.shared_encoder(x)
return [head(h).squeeze(-1) for head in self.task_heads]
def shared_repr(self, x):
return self.shared_encoder(x)
class UncertaintyWeightedLoss(nn.Module):
"""Kendall et al. (2018) homoscedastic weighting."""
def __init__(self, n_tasks: int = 3):
super().__init__()
self.log_vars = nn.Parameter(torch.zeros(n_tasks)) # log(sigma^2)
def forward(self, losses: list) -> torch.Tensor:
return sum(
torch.exp(-self.log_vars[i]) * loss + self.log_vars[i]
for i, loss in enumerate(losses)
)
def get_weights(self) -> list:
with torch.no_grad():
return [torch.exp(-lv).item() for lv in self.log_vars]
UncertaintyWeightedLoss memiliki parameter, jadi harus dimasukkan ke optimizer bersama model: optim.Adam(list(model.parameters()) + list(uw.parameters()), ...). Melupakan ini adalah cara paling umum untuk "menjalankan uncertainty weighting" tetapi diam-diam menjalankan bobot tetap.
Yang harus dilaporkan
Untuk setiap skema, pada setiap fold: bobot tugas akhir yang dipelajari, metrik tugas utama, dan — karena skema pembobotan adalah pilihan model — berapa banyak skema yang dibandingkan sebelum memilih satu.
| Skema | Metrik tugas utama vs single-task | |||
|---|---|---|---|---|
| Tetap () | 1.00 | 1.00 | 1.00 | — |
| Pembobotan ketidakpastian | — | — | — | — |
| GradNorm | — | — | — | — |
Tiga skema dikali beberapa fold sudah merupakan pencarian model kecil. Setiap peningkatan yang dilaporkan di sini harus bertahan dalam koreksi multiple-testing yang dijelaskan di deflated Sharpe dan multiple testing sebelum berarti apa pun.
Negative Transfer: Beri Tanda pada Gradient

Inilah bagian yang layak dipertahankan. Negative transfer terjadi ketika tugas tambahan membuat tugas utama lebih buruk, dan memiliki diagnostik langsung: sudut antara gradient tugas dalam ruang parameter bersama.
Ukur hanya pada encoder bersama — head bersifat khusus tugas menurut konstruksi dan selalu "setuju" secara trivial.
import torch.nn.functional as F
def shared_grad(model, x, y, task_idx, criterion=nn.MSELoss()):
"""Gradient of task `task_idx` w.r.t. the shared encoder, flattened."""
model.zero_grad(set_to_none=True)
loss = criterion(model(x)[task_idx], y)
loss.backward()
return torch.cat([
p.grad.detach().flatten()
for p in model.shared_encoder.parameters()
if p.grad is not None
])
def task_conflict(model, x, y_by_task, task_names):
"""Pairwise cosine similarity between per-task shared-encoder gradients."""
grads = {
name: shared_grad(model, x, y_by_task[name], i)
for i, name in enumerate(task_names)
}
return {
(a, b): F.cosine_similarity(
grads[a].unsqueeze(0), grads[b].unsqueeze(0)
).item()
for i, a in enumerate(task_names)
for b in task_names[i + 1:]
}
Panggil ini pada batch held-out dengan cadence tetap selama training, bukan sekali di akhir. Sebuah pasangan dapat mulai selaras lalu menyimpang ketika encoder menjadi khusus; satu angka pada akhir training menyembunyikan hal itu.
Temuan yang perlu dicari — dan dipublikasikan apa pun hasilnya:
| Pasangan | cos sim, training awal | cos sim, training akhir | MTL membantu tugas utama? |
|---|---|---|---|
| return ↔ volume | — | — | — |
| return ↔ volatility | — | — | — |
| volume ↔ volatility | — | — | — |
Jika gradient volume dan volatilitas saling selaras sementara keduanya berkonflik dengan gradient return, kesimpulan yang tepat adalah bahwa dua tugas tambahan membentuk blok koheren yang tidak termasuk tugas return — dan perbaikannya adalah pengelompokan tugas, bukan kapasitas yang lebih besar. Ketika konflik nyata, solusi standar adalah PCGrad (Yu et al., 2020), yang memproyeksikan setiap gradient berkonflik ke bidang normal gradient lain; CAGrad (Liu et al., 2021), yang mencari arah descent yang tidak merugikan tugas mana pun; atau menghapus tugas tambahan sepenuhnya.
Perhatikan apa yang sengaja tidak ada: plot t-SNE dari representasi bersama yang diwarnai berdasarkan nilai target. Itu dekoratif — angka kosinus di atas menyatakan semua yang hendak ditunjukkan embedding, dan menyatakannya sebagai angka.
Protokol Validasi

Pengukuran di atas tidak berguna jika protokolnya ceroboh, dan MTL memperburuk jebakan biasa karena ada tiga target yang dapat bocor, bukan satu.
Data nyata, bukan simulator. Target harus berasal dari data OHLCV/trade aktual. GARCH toy yang di-hardcode menghasilkan volatilitas yang berkorelasi dengan return secara konstruksi, dan justru itulah yang sedang diuji — eksperimen akan mengukur generatornya sendiri. Jika Anda menginginkan proses volatilitas yang dipasang, peramalan volatilitas GARCH untuk kripto memasang GARCH(1,1) dengan maximum likelihood pada BTC/ETH nyata dan memvalidasi residual terstandarisasi, sementara GARCH asimetris dan efek leverage menjelaskan mengapa simulator respons simetris Gaussian sejak awal salah menyatakan volatilitas kripto. Data sintetis hanya dapat dipertanggungjawabkan jika menyediakan ground truth terkontrol — korelasi tugas yang diketahui dan ditetapkan penulis yang ingin Anda pulihkan — yang merupakan eksperimen berbeda dari yang ada di sini.
Scaler hanya dipasang pada train. Pasang feature scaler dan ketiga target scaler di dalam setiap fold training lalu terapkan pada validasi; fit_transform global sebelum splitting membocorkan momen test set ke training. Kegagalan persis ini dicatat dalam taksonomi look-ahead bias.
Fold walk-forward yang dipurge dan diberi embargo. Satu chronological split 80/20 tidak dapat membedakan peningkatan MTL dari efek fold — itulah seluruh argumen walk-forward optimization, yang menunjukkan tiga split menghasilkan tiga kesimpulan. Gunakan kembali generator purged_walk_forward expanding-window dari pemodelan spread dengan machine learning: generator itu membuang gap sebesar horizon baris di kedua sisi setiap batas, hal yang penting di sini karena window volatilitas terealisasi yang tumpang tindih bocor melewati batas bahkan ketika target return tidak.
Baseline klasik. Jaringan MTL yang mengalahkan tiga jaringan single-task belum membuktikan apa pun jika model gradient-boosting atau ridge per target mengalahkan keempatnya. Pasang satu model per target dengan LightGBM atau ridge pada fold dan fitur yang sama, lalu laporkan dalam tabel yang sama.
| Model | Metrik tugas utama | Catatan |
|---|---|---|
| Ridge, per target | — | Baseline klasik |
| LightGBM, per target | — | Baseline klasik |
| MLP single-task, per target | — | Tiga jaringan terpisah |
| MTL, skema loss terbaik | — | Satu jaringan, tiga head |
Kapan MTL Layak Digunakan di Sini

Kondisi ketika MTL seharusnya menang, dinyatakan sebagai hipotesis yang perlu diperiksa terhadap fold di atas, bukan sebagai checklist:
- Label tambahan lebih bersih daripada label utama. Volume diamati langsung; "expected return" tidak. Jika head return terutama menyesuaikan noise, sinyal gradient dari head tambahan adalah satu-satunya bagian objective yang terdefinisi dengan baik.
- Data training terbatas dibandingkan kapasitas encoder, sehingga constraint tambahan benar-benar melakukan regularisasi dan bukan sekadar berebut parameter.
- Latency inferensi penting dan satu forward pass mengalahkan tiga.
Dan argumen tandingannya, yang juga dapat diuji: jika nilai cos_sim(return, ·) terus-menerus negatif, encoder bersama ditarik menjauh dari tugas utama dan head tambahan menjadi pajak, bukan regularizer.
Kesimpulan

Return, volume, dan volatilitas berasal dari microstructure yang sama, jadi representasi bersama adalah prior yang wajar — tetapi prior bukan hasil. Dua hal yang benar-benar dapat ditetapkan oleh setup ini adalah skema penyeimbangan loss yang disukai data (dengan bobot terpelajar dilaporkan, bukan hanya pemenangnya) dan apakah gradient tugas pada encoder bersama selaras, diukur sepanjang training dan bukan diasumsikan hanya karena target berkorelasi.
Jika fold walk-forward yang dipurge menunjukkan jaringan MTL gagal mengalahkan model gradient-boosting per target, itulah temuannya dan harus dipublikasikan demikian — templatenya adalah hasil negatif yang jujur. Hasil negatif mengenai negative transfer tetap merupakan hasil tentang negative transfer.
Penulis
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.