Ruang Parameter Dua Paksi: Mengapa Sebahagian Besar Sapuan Anda Sepatutnya Hampir Percuma
Sebahagian daripada siri "Backtest Tanpa Ilusi".
Sumpahan kedimensian biasanya diceritakan sebagai amaran: setiap parameter yang anda tambah menggandakan ruang carian, jadi strategi 18 dimensi mustahil untuk disapu. Pembingkaian itu secara senyap mengandaikan setiap dimensi sama kosnya untuk dinilai. Hakikatnya tidak. Dalam enjin dua/tiga kerangka masa kami, dua pertiga daripada parameter hampir percuma untuk disapu, manakala satu pertiga lagi menanggung hampir keseluruhan bil pengiraan. Sebaik sahaja anda nampak pembahagian itu, "ruang carian terlalu besar" tidak lagi menjadi rungutan yang tepat. Soalan yang betul ialah: dimensi yang mana yang sebenarnya anda bayar?
Dimensi yang tidak perlu dikira semula
Penanda aras carian parameter kami menjalankan strategi momentum berbilang kerangka masa: purata bergerak Hull dan varian terlicin tiga kalinya (HMA3) pada dua atau tiga kerangka masa, dengan get pemisahan arah yang memutuskan bila sesuatu silangan cukup "bersih" untuk diambil tindakan. Ruang carian penuh bagi varian triple-TF ialah lapan belas dimensi — tempoh dan panjang HMA bagi setiap kerangka masa, ditambah satu set ambang pemisahan untuk masuk dan keluar pada setiap kerangka masa.
Cara naif untuk menyapunya ialah satu gelung tunggal: pilih vektor parameter, bina setiap penunjuk dari kosong, jalankan simulasi, beri skor, ulang. Kami bermula di situ. Ia lambat, dan ia lambat atas sebab yang ternyata memalukan sebaik sahaja kami menamakannya: bagi sebahagian besar percubaan bersebelahan, kami mengira semula penunjuk yang langsung tidak berubah.
Anjakkan ambang pemisahan masuk daripada 0.03 kepada 0.035 dan jalankan semula. Purata bergerak Hull merentasi setahun bar 1 minit adalah serupa bit demi bit dengan percubaan sebelumnya — ambang itu langsung tidak muncul dalam takrifannya. Namun gelung naif tetap membinanya semula, merentasi lebih setengah juta bar, tiga kerangka masa dalamnya, setiap kali tanpa kecuali. Kami membelanjakan hampir keseluruhan bajet pengiraan untuk menerbitkan semula kuantiti yang invarian terhadap parameter yang sebenarnya kami ubah.
Pemerhatian itulah keseluruhan artikel ini. Sebaik sahaja anda perasan bahawa sesetengah parameter mengubah penunjuk manakala sesetengah yang lain hanya mengubah peraturan keputusan yang dikenakan ke atas penunjuk tetap, ruang parameter itu berhenti menjadi awan dimensi yang rata dan menjadi dua lapisan tersarang dengan tanda harga yang jauh berbeza.
Dua paksi, bukan satu

Bahagikan parameter mengikut apa yang penilaian semulanya memaksa anda kira semula:
-
Paksi mahal — parameter penunjuk. Tempoh kerangka masa dan panjang HMA. Ubah salah satu daripadanya dan anda mesti membina semula penunjuk merentasi keseluruhan siri harga: sampel semula kerangka masa lebih tinggi, jalankan pemberat Hull, kira semula siri silangan dan pemisahan pada setiap silang. Ini ialah sapuan O(n) bagi setiap bar sejarah, dan anda membayarnya sepenuhnya bagi setiap kombinasi yang berbeza. Dalam strategi triple-TF, paksi ini terdiri daripada enam parameter:
(period, hma_length)bagi setiap kerangka masa tinggi, pertengahan dan rendah. -
Paksi murah — ambang keputusan. Get pemisahan arah yang memutuskan, berdasarkan siri silangan dan pemisahan yang sudah dikira, sama ada untuk masuk atau keluar. Ubah satu ambang dan tiada satu penunjuk pun bergerak. Anda hanya menjalankan semula satu laluan O(n) ke atas isyarat prakira, menyemak get dan merekod pengisian. Dalam strategi triple-TF, paksi ini terdiri daripada dua belas parameter (empat ambang pemisahan — masuk-beli, masuk-jual, keluar-beli, keluar-jual — pada setiap satu daripada tiga kerangka masa). Varian dual-TF mempunyai lapan (empat ambang pada setiap satu daripada dua kerangka masa).
Jadi bentuk sebenar ruang ini ialah: 6 mahal + 12 murah = 18 untuk triple-TF, dan 4 mahal + 8 murah = 12 untuk dual-TF. Dua pertiga daripada kedimensian itu berada pada paksi murah. Dan kedua-dua paksi ini bukan sekadar berbeza dari segi bilangan — kos seunitnya berbeza melebihi tiga tertib magnitud, dan angka itulah yang menjadi tumpuan artikel ini seterusnya.
Strukturnya tersarang secara ketat. Setiap titik pada paksi mahal — satu (period, hma_length) konkrit bagi setiap kerangka masa — mentakrifkan satu set tatasusunan isyarat tetap. Di atas asas tetap itu terletak keseluruhan helaian paksi murah: beribu-ribu vektor ambang, setiap satunya laluan pantas ke atas tatasusunan yang sama. Anda membayar asas itu sekali dan melunaskan kosnya merentasi keseluruhan helaian.
Mengapa penunjuk invarian terhadap ambang
Caching ini hanya berfungsi kerana satu fakta matematik, bukan butiran pelaksanaan yang kebetulan bernasib baik, dan ia patut dinyatakan dengan tepat kerana ia adalah andaian yang memikul segala beban. Tatasusunan penunjuk ialah fungsi kepada (period, hma_length) semata-mata. Ambang pemisahan langsung tidak muncul dalam takrifannya.
Secara konkrit, bagi setiap kerangka masa, enjin ini mengira terlebih dahulu empat tatasusunan sejajar ke atas indeks asas: purata bergerak Hull hma, versi terlicin tiga kalinya hma3, siri silangan cross (+1 beli / −1 jual / 0), dan peratusan separation pada setiap silang. Setiap satunya diterbitkan daripada harga dan dua parameter penunjuk itu. Ambang — nombor yang anda bandingkan separation dengannya — dikenakan kemudian, pada masa keputusan. Ia langsung tidak menyentuh tatasusunan tersebut.
Ini bukan penyusunan semula yang selamat secara remeh. Ia selamat hanya kerana kami berhati-hati tentang bila sesuatu maklumat menjadi tersedia, iaitu kebimbangan yang dibincangkan dalam kerja mengenai bias pandang-depan di tempat lain dalam siri ini. Penunjuk kerangka masa lebih tinggi pada bar asas i dikira daripada lilin TF tinggi yang telah tertutup, ditambah satu lilin yang sedang terbentuk dengan harga tutup berjalannya sama dengan harga tutup asas semasa close[i] — nilai yang diketahui pada bar i. Tiada apa-apa dari masa depan yang bocor masuk. Jadi isyarat prakira pada bar i adalah tepat seperti apa yang akan dilihat oleh bot langsung pada bar i, dan ia kekal sah tidak kira ambang mana yang anda uji terhadapnya kemudian. Menyimpan isyarat bocor ke dalam cache hanya akan menyimpan kebocorannya; menyimpan isyarat kausal ke dalam cache menyimpan sesuatu yang benar-benar boleh anda dagangkan.
Sifat invarian ini memberi kita satu pemfaktoran. Tuliskan penilaian satu vektor parameter penuh θ = (indicator_params, threshold_params) sebagai:
signals = build_indicators(indicator_params) # EXPENSIVE, depends only on indicator_params
score = simulate(signals, threshold_params) # CHEAP, reuses signals across all thresholds
build_indicators tidak membaca threshold_params. Fakta tunggal itulah yang mengesahkan cache ini: kekalkan indicator_params tetap, ubah threshold_params sesuka hati, dan signals ialah pemalar yang anda kira sekali sahaja.
Seni bina: kira sekali, sapu berkali-kali

Enjin ini (scripts/engine_multitf.py, komit bfc8aaa dalam backtester kami) melaksanakan pemfaktoran tersebut dengan dua komponen yang namanya menyatakan dengan tepat apa yang dilakukannya.
SignalCache — paksi mahal, dimemoisasi. Ia adalah sebuah dictionary yang berkunci pada (period_bars, hma_length). Minta daripadanya isyarat untuk sesuatu kerangka masa dan ia memulangkan TFSignals yang tersimpan dalam cache jika kombo penunjuk itu pernah dibina sebelumnya; jika tidak, ia membinanya sekali dan menyimpannya. Oleh sebab kuncinya hanyalah parameter penunjuk, setiap konfigurasi ambang yang berkongsi kombo penunjuk yang sama — dan dalam sapuan ambang yang padat, itu bermakna beribu-ribu konfigurasi — mengena entri cache yang sama. Kerangka masa lebih tinggi paling mendapat manfaat daripada hal ini: grid kasar dengan, katakan, empat calon tempoh didarab segelintir panjang HMA hanyalah bilangan kecil binaan mahal yang berbeza, dan setiap satunya diguna semula merentasi keseluruhan helaian ambang yang tersusun di atasnya.
sweep_separations — paksi murah, dikelompokkan. Ia mengambil tatasusunan isyarat daripada cache dan satu matriks vektor ambang (sps, berbentuk [m, 12]) lalu menjalankan kesemuanya melalui satu kernel terkompil tunggal. Setiap baris ialah satu laluan O(n): susuri bar, kenakan get, rekodkan pengisian bebas kebocoran pada open[i+1], jumlahkan PnL dan masa dalam posisi. Tiada penunjuk yang dibina semula di dalam gelung ini — ia membaca cross dan separation terus daripada cache. Simulasi dalamannya dikompil secara JIT (Numba), jadi setelah dipanaskan, kos setiap konfigurasi didominasi oleh satu imbasan linear ke atas bar, bukan oleh overhed Python.
Kedua-dua komponen ini bergabung menjadi carian tersarang yang semula jadi: gelung luar menyusuri paksi mahal (setiap lelaran membina dan menyimpan satu kombo penunjuk ke dalam cache), dan gelung dalam memancarkan satu kelompok lebar merentasi paksi murah ke atas tatasusunan cache tersebut. Dalam kod, bentuknya tepat seperti itu — satu gelung mahal yang kecil membalut satu kelompok murah yang lebar:
cache = SignalCache(base_close, base_ts) # keyed by (period, hma_length)
for htf_p, htf_h, mtf_p, mtf_h, ltf_p, ltf_h in indicator_grid: # EXPENSIVE axis (coarse)
htf = cache.get(htf_p, htf_h) # built once, then a cache hit forever
mtf = cache.get(mtf_p, mtf_h)
ltf = cache.get(ltf_p, ltf_h)
sps = sample_thresholds(m=4000) # CHEAP axis: [m, 12] threshold vectors
pnl, n_trades, bars_in_pos = sweep_separations( # one compiled batch, no indicator work
base_close, base_open, htf, ltf, sps, mtf=mtf)
Anda menyentuh pembina mahal itu sesedikit yang dibenarkan oleh grid penunjuk, dan anda biarkan sapuan murah menanggung volumnya — beribu-ribu baris sps terhadap tatasusunan yang tidak pernah bergerak. Itulah keseluruhan pengoptimumannya — tiada penghampiran, tiada ketepatan yang hilang, cuma keengganan mengira semula apa yang tidak berubah.
Apa kos sebenar "percuma": angka-angkanya

Kami mengukur kedua-dua paksi ini pada beban kerja demo: setahun penuh bar 1 minit ETHUSDT (~527k bar), triple-TF, dengan penunjuk telah dipanaskan dan kompilasi JIT dikecualikan daripada pemasaan.
Pada paksi murah, sweep_separations mengekalkan kadar ~5,600 konfigurasi ambang sesaat. Itu ialah simulasi lengkap — get, pengisian, PnL, pendedahan — bagi setiap konfigurasi, merentasi setengah juta bar, pada kira-kira 180 mikrosaat setiap konfigurasi. Sebab ia boleh sepantas itu ialah ia langsung tidak melakukan kerja penunjuk: setiap konfigurasi membaca tatasusunan cross dan separation yang sama daripada cache.
Sekarang nilaikan harga alternatifnya. Membina set penunjuk triple-TF sekali mengambil masa dalam lingkungan beberapa ratus milisaat (~0.3 s) — pensampelan semula tiga kerangka masa, pemberat Hull, pengekstrakan silangan dan pemisahan merentasi keseluruhan tahun itu. Jika anda mengira semula penunjuk di dalam gelung konfigurasi — reka bentuk gelung tunggal yang naif — setiap satu daripada 5,600 konfigurasi sesaat itu akan membayar kos penuh binaan penunjuk. Kos setiap konfigurasi melonjak daripada ~180 mikrosaat kepada ~0.3 saat:
| Kos setiap konfigurasi | Konfigurasi/saat | |
|---|---|---|
| Paksi murah (isyarat bercache) | ~180 µs | ~5,600 |
| Kira semula penunjuk setiap konfigurasi | ~0.3 s | ~3.4 |
Nisbahnya ialah ~1,600x. Menyapu paksi murah dengan isyarat daripada cache adalah kira-kira tiga tertib magnitud lebih murah daripada reka bentuk naif yang membina semula penunjuk bagi setiap vektor ambang. Secara konkrit: satu kelompok beberapa ribu konfigurasi ambang yang selesai dalam masa kurang sesaat pada laluan bercache akan mengambil masa hampir sejam jika setiap konfigurasi membina semula penunjuknya. Keputusan sama, ketepatan sama, tiada jalan pintas dalam matematiknya — satu-satunya perbezaan ialah satu daripadanya mengira semula suatu invarian manakala satu lagi tidak.
Ini bukan pengoptimuman mikro yang anda tabur pada penghujung projek. Ia mengubah carian mana yang boleh dilaksanakan. Pada 5,600 cfg/s, paksi ambang cukup padat untuk diterokai dengan betul — anda mampu membuat grid halus atau sampel rawak/QMC yang panjang bagi setiap kombo penunjuk — sementara paksi mahal kekal sebagai segelintir binaan yang sengaja dibiarkan kasar. Bajet pengiraan mengalir ke tempat parameter yang benar-benar ada kosnya.
Sumpahan kedimensian, dinilai harga semula

Kembali kepada pembingkaian yang membuka artikel ini. Sumpahan kedimensian mengatakan ruang carian membesar secara eksponen mengikut bilangan parameter, jadi lebih banyak dimensi secara mutlaknya lebih buruk. Itu benar tentang saiz grid. Ia mengelirukan tentang kos untuk meliputinya, kerana ia menetapkan harga yang serupa bagi setiap dimensi.
Sebaik sahaja anda asingkan paksi-paksi itu, bilangannya terbaca dengan cara berbeza. Strategi triple-TF ialah 18 dimensi, tetapi hanya 6 daripada dimensi itu yang mahal. 12 yang lain ialah dimensi paksi murah yang mengembangkan grid tanpa mengembangkan bil pengiraan secara bermakna — anda boleh melontar beribu-ribu konfigurasi kepadanya dengan kos sesen dua. Strategi dual-TF ialah 12 dimensi dengan hanya 4 dimensi mahal dan 8 dimensi murah. Dalam kedua-dua kes, sebahagian besar "sumpahan" itu tertumpu pada paksi yang hampir tiada kosnya untuk disapu.
Jadi cara yang jujur untuk menaakul tentang kos carian bukanlah "berapa banyak parameter" tetapi "berapa banyak parameter mahal, dan sekasar mana gridnya boleh dibuat." Paksi mahal ialah tempat eksponen itu benar-benar menyakitkan, dan di situlah anda mahukan grid yang kecil dan dipilih dengan teliti — beberapa calon tempoh, julat panjang HMA yang sederhana — mungkin diperhalusi secara kasar-ke-halus, selaras dengan semangat penelusuran resolusi adaptif yang kami gunakan di tempat lain. Paksi murah pula ialah tempat anda boleh bermewah, kerana setiap konfigurasi ambang tambahan hanyalah 180 mikrosaat.
Penilaian harga semula ini terpakai jauh melangkaui strategi HMA kami. Corak ini — sesetengah parameter mengubah ciri, kebanyakan parameter mengubah peraturan yang dikenakan ke atas ciri tetap — berulang di mana-mana dalam dagangan sistematik. Panjang penunjuk, kekerapan pensampelan semula dan tetingkap imbas belakang adalah mahal; ambang masuk/keluar, jarak henti rugi, pendarab saiz posisi dan get pengesahan adalah murah. Setiap kali sesuatu parameter hanya membentuk semula sempadan keputusan ke atas isyarat yang sudah dikira, ia tergolong pada paksi murah, dan ia patut disapu di situ. Kemenangan caching yang berkaitan daripada keluarga yang sama muncul dalam caching parquet berbilang kerangka masa dan dalam tangga kelajuan enjin yang lebih luas.
Di mana makan percuma itu ada bilnya
Paksi murah itu murah dari segi pengiraan. Ia tidak murah dari segi statistik, dan mencampuradukkan kedua-duanya ialah cara anda menukarkan kemenangan prestasi menjadi mesin overfitting.
Setiap konfigurasi ambang yang anda nilai ialah satu percubaan, dan apabila anda menjalankan beribu-ribu percubaan pada satu set data, yang terbaik antaranya kelihatan bagus sebahagiannya kerana nasib. Menjadikan percubaan itu 1,600x lebih murah tidak menghilangkan faktor nasib — ia menjadikannya lebih mudah terkumpul. Sapuan ambang yang padat ialah tepat situasi di mana inflasi ujian berbilang menggigit paling kuat: banyak calon, satu sejarah, dan peraturan pemilihan yang melaporkan nilai maksimum. Enjin terpantas di dunia akan dengan senang hati menghulurkan kepada anda vektor ambang yang memadan hingar tetingkap ujian anda dengan cantiknya lalu gagal di luar sampel.
Jadi disiplinnya mesti berkembang seiring dengan kelajuan. Sebaik sahaja kos pengiraan satu percubaan jatuh menghampiri sifar, perakaunan statistik menjadi kekangan yang mengikat, dan anda perlu membayarnya secara eksplisit:
- Deflasikan mengikut bilangan percubaan. Nilaikan pemenang berbanding berapa banyak konfigurasi yang anda cuba, bukan berbanding sifar. Nisbah Sharpe Terdeflasi dan kebarangkalian overfitting backtest wujud tepat untuk tujuan ini — kedua-duanya menukarkan "kami mencuba 4,000 vektor ambang" menjadi potongan ke atas kelebihan yang dilaporkan.
- Sahkan di luar sampel, bagi setiap lipatan. Sapuan murah tetap mesti dijalankan di dalam pembahagian walk-forward yang jujur; ambang yang hanya menang dalam sampel adalah tidak bernilai, tidak kira sepantas mana anda menjumpainya. Enjin kami memastikan pengisian bebas kebocoran (
open[i+1]) tepat supaya paksi murah tidak boleh membeli prestasi dengan mengintip masa depan. - Utamakan dataran berbanding puncak. Oleh sebab paksi ambang itu padat dan pantas, anda boleh memetakan permukaan tindak balasnya, bukan sekadar argmax-nya. Rantau ambang yang luas yang kesemuanya berfungsi ialah kelebihan sebenar; satu maksimum tunggal yang menjulang tajam ialah artifak suaian — perbezaan dataran-lawan-puncak, yang menjadi mampu dilakukan berkat kelajuan yang sedang kami perihalkan ini.
Bacalah struktur dua paksi ini dengan cara yang betul: ia tidak membeli anda lebih keyakinan, ia membeli anda lebih banyak percubaan pada kos keyakinan yang sama bagi setiap percubaan. Itu benar-benar bernilai — liputan padat paksi murah itulah yang membolehkan anda menemui dataran dan mencirikan permukaannya — tetapi hanya jika anda memastikan lejar statistik kekal jujur. Kelajuan menghapuskan alasan pengiraan untuk tidak mencari dengan teliti; ia tidak menghapuskan kewajipan untuk mendiskaun apa yang ditemui oleh carian teliti itu.
Cara menyusun carian anda sendiri
Untuk menerapkan ini pada strategi anda sendiri, kerjanya kebanyakannya adalah pengelasan — mengasingkan parameter anda ke paksi yang betul — diikuti dengan gelung tersarang:
- Labelkan setiap parameter mengikut apa yang ia paksa anda kira semula. Jika mengubahnya mengubah tatasusunan penunjuk/ciri, ia mahal. Jika ia hanya mengubah perbandingan, ambang, atau peraturan penyaizan yang dikenakan ke atas tatasusunan tetap, ia murah. Jika ragu-ragu, tanya: adakah parameter ini muncul di mana-mana dalam takrifan penunjuk? Jika tidak, ia murah.
- Kuncikan cache pada paksi mahal sahaja. Memoisasikan pembinaan ciri mengikut parameter penunjuknya (sebagaimana yang dilakukan
SignalCachedengan(period, hma_length)). Percubaan bersebelahan yang berkongsi kombo penunjuk kemudiannya menggunakan semula tatasusunan yang sama secara percuma. - Kelompokkan paksi murah ke atas ciri yang tersimpan dalam cache. Jalankan konfigurasi ambang sebagai gelung terkompil yang padat ke atas isyarat prakira, bukan sebagai penilaian semula penuh. Dari sinilah datangnya daya pemprosesan anda — dan dalam kes kami, ~5,600 cfg/s itu.
- Sarangkan gelung: mahal di luar, murah di dalam. Pastikan grid mahal kecil dan bersengaja (kasar, atau kasar-ke-halus); biarkan sapuan murah padat. Belanjakan bajet di tempat parameter yang ada kosnya.
- Bajetkan percubaan berdasarkan overfitting, bukan berdasarkan jam. Kini apabila jam bukan lagi hadnya, biarkan Sharpe terdeflasi / PBO yang menjadi had. Tentukan berapa banyak percubaan paksi murah yang anda mampu tanggung secara statistik, dan sahkan pemenangnya di luar sampel.
Pulangan kejuruteraan dan pagar keselamatan statistik ialah dua sisi satu idea: mengasingkan paksi membolehkan anda mencari dimensi murah secara menyeluruh, dan itulah tepat sebabnya anda kemudian mesti mendiskaun betapa menyeluruhnya carian anda.
Intipati
- Bukan semua dimensi sama kosnya. Parameter sesebuah strategi terbahagi kepada paksi mahal (penunjuk — dikira semula merentasi keseluruhan siri) dan paksi murah (ambang — satu laluan O(n) ke atas isyarat prakira). Dalam enjin kami, itu bermakna 6 mahal + 12 murah untuk triple-TF, 4 + 8 untuk dual-TF.
- Penunjuk invarian terhadap ambang, dan sifat invarian itulah keseluruhan pengoptimumannya. Pembinaan ciri hanya bergantung pada parameter penunjuk, jadi anda bina sekali, simpan dalam cache mengikut
(period, hma_length), dan guna semula merentasi setiap konfigurasi ambang yang berkongsi kombo itu. - Paksi murah berjalan ~1,600x lebih murah. ~5,600 konfigurasi ambang sesaat (~180 µs setiap satu) pada isyarat bercache, berbanding ~0.3 s setiap konfigurasi jika anda membina semula penunjuk setiap kali. Ketepatan yang sama — satu-satunya perbezaan ialah keengganan mengira semula suatu invarian.
- Sumpahan kedimensian sebenarnya ialah sumpahan kedimensian mahal. Sebahagian besar bilangan parameter berada pada paksi yang hampir percuma untuk disapu. Pastikan grid mahal kekal kasar; bermewahlah pada yang murah.
- Kelajuan mengalihkan kekangan yang mengikat daripada pengiraan kepada statistik. Sapuan padat lagi pantas ialah mesin ujian berbilang. Deflasikan mengikut bilangan percubaan, sahkan bagi setiap lipatan, dan utamakan dataran berbanding puncak — makan percuma itu benar, tetapi bil statistiknya bukan pilihan.
Enjin penuhnya — SignalCache, sweep_separations, simulasi multi-TF bebas kebocoran, dan ujian pariti yang memakukannya pada semantik lilin berjalan secara langsung — terletak dalam scripts/engine_multitf.py (komit bfc8aaa) dalam backtester kami. Lain kali apabila seseorang memberitahu anda bahawa strategi 18 parameter terlalu besar untuk dicari, tanya mereka berapa banyak daripada parameter itu yang benar-benar menggerakkan penunjuk. Biasanya hanya satu pertiga daripadanya, dan selebihnya hampir percuma.
Pengarang
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.