← Maqolalarga qaytish
July 6, 2026
5 daqiqa o'qish

GPU aniqlik tuzogi: Apple Metal ustidagi fp32 backtest qanday qilib jimgina axlat qaytaradi

GPU aniqlik tuzogi: Apple Metal ustidagi fp32 backtest qanday qilib jimgina axlat qaytaradi
#algotrading
#backtest
#gpu
#apple-silicon
#ksuzuvchi-nuqta
#sonli-turgunlik
#mlx
Part 8 of 10 · Collection
High-Performance Backtest Engines

"Illyuziyalarsiz backtestlar" turkumining bir qismi.

Biz parametr boyicha saralovchi backtestimizni GPU'ga kochirdik va u 2,796× tezlashdi. U qaytargan raqamlar ishonarli edi. Ular, birinchi ishlaydigan versiyada, tola axlat ham edi — ikki yuz baravar xato — va hech narsa qulamadi, hech narsa ogohlantirmadi, hech narsa notogri korinmadi.

Bu Apple Silicon ustidagi GPU backtestingni CPU kodidan farqli oqetgan tarzda xavfli qiladigan tuzoq: Metal'da float64 yoq. Backtestingiz Apple GPU'sida tegadigan har bir raqam 32-bitli float, buni sorasangiz ham, soramasangiz ham. Va harakatlanuvchi ortachani vektorlashtirishning eng jozibali yoli — har bir unumdorlikka etibor beruvchi kvant qolga oladigan O(n) prefiks-yigindi hiylasi — aynan fp32 narx masshtabida omon qololmaydigan formulirovka. U xato bermaydi. U tola tezlikda ishlaydi va sizga 211 baravar xato harakatlanuvchi ortachaga qurilgan ishonarli korinuvchi kapital egri chizigini topshiradi.

Yechim eng qiziq qismi, chunki bu "koproq bit ishlating" emas (ishlata olmaysiz) va bu "koproq ehtiyot boling" ham emas (soddadil kod allaqachon ehtiyotkor). Yechim — oxshash matematik miqdorni boshqacha yigindi bilan hisoblash — togridan-togri oynali konvolyutsiya — bu har bir oraliq qiymatni fp32 aniq ifodalay oladigan darajada kichik saqlaydi. Bir xil WMA, etti muhim raqamgacha bir xil natija va CPU'dagi bir oqimli numba'dan 55.9× tezroq. Bu maqola otkazuvchidir: nega Metal sizga fp32'ni majburlaydi, aynan qaerda ravshan formulirovka toshib ketadi, nega togrisi toshmaydi va biz qanday isbotladik — kelishuvni egri chiziqlarga qarab emas, savdolarni sanab — tez versiya va halol fp64 versiyasi mos kelishini.

Bu erdagi barcha raqamlar Apple M2 Max'da olchangan, 150,000 bar × 80 parametr kombinatsiyasi, 3 tadan eng yaxshisi, reponing scripts/bench_param_sweep.py (M5 GPU metodi, commit 97eadaf), scripts/engine_multitf_gpu.py (04d71e8) va scripts/GPU_NOTES.md dagi dizayn qaydlaridan.

Metal'da float64 yoq

Apple'ning Metal GPU quvuri float64 ochirilgan holda — har bir yol 32-bitga majburlangan, butun sonlar faqat 1.6×10⁷ gacha aniq

CPU'da Python/numpy backtesti sukut boyicha ikki barobar aniqlikda ishlaydi. float64 sizga 52-bitli mantissa beradi: butun sonlar 2⁵³ ≈ 9×10¹⁵ gacha aniq ifodalanadi va nisbiy aniqlik taxminan 1.1×10⁻¹⁶. Siz bu haqda deyarli hech qachon oylamaysiz, chunki narx masshtabida — BTC yopilishi taxminan 30,000, 10¹⁴ ga etuvchi jamlangan yigindi — fp64'da zaxira yetarlicha bor.

Apple'ning GPU'si bu zaxirani taklif qilmaydi, chunki u float64'ni umuman taklif qilmaydi. Metal Shading Language'da double turi yoq; MLX, PyTorch-MPS va Apple Silicon'ga moljallangan har qanday boshqa freymvork buni meros qilib oladi. Uni yoqadigan bayroq ham, sekin-lekin-togri zaxira yol ham yoq. Agar hisoblashingiz GPU'ga tegsa, u fp32'da sodir boladi:

  • Mantissa: 23 bit (yashirin bosh bit bilan 24). Butun sonlar faqat 2²⁴ = 16,777,216 ≈ 1.6×10⁷ gacha aniq.
  • Nisbiy aniqlik: ~1.2×10⁻⁷. Taxminan etti muhim onlik raqam, bundan koproq emas.

Ana shu 1.6×10⁷ shift — butun hikoya. Bu saxiy tuyuladi — on olti million — toki backtest muntazam ravishda bundan ancha katta oraliq miqdorlar qurishini payqamaguningizcha va oraliq qiymat 1.6×10⁷ dan otgan lahzada fp32 hatto ketma-ket butun sonlarni ham ifodalay olmaydi, sizga kerak bolgan kasr strukturasini aytmasa ham. Aniqlik silliq degradatsiyaga uchramaydi; u jardan quladi va jar sizning malumotingiz ikkilanmasdan otib ketadigan raqamda.

Tuzoq shundaki, fp32 deyarli har doim yaxshi. Backtestning katta qismi — narxlar, qaytimlar, PnL, Sharpe — etti raqam yetarli bolgan diapazonda bemalol yashaydi. Shuning uchun soddadil kochirish ishlaydi, tutun testidan otadi, esli-hushli korinuvchi natija chiqaradi. Nosozlik aniq bitta amalda joylashgan, oraliq qiymat 10⁷ dan otib ketadigan yagona joyda va bu amal hamma birinchi vektorlashtiradigan amaldir.

Jozibali formulirovka: WMA bitta prefiks-yigindi otishida

Bizning strategiyamiz Hull harakatlanuvchi ortachalariga tayanadi. HMA — birga bogliq uchta ogirlashtirilgan harakatlanuvchi ortacha; HMA3 varianti — tortta. Uzunligi p bolgan oyna ustidagi chiziqli yadroli ogirlashtirilgan harakatlanuvchi ortacha:

WMAt=k=1pkxtp+kp(p+1)/2.\mathrm{WMA}_t = \frac{\sum_{k=1}^{p} k \cdot x_{t-p+k}}{p(p+1)/2}.

150k bar boyicha minglab parametr kombinatsiyalarini saralaganda, WMA konvolyutsiyalari aynan narxdir. Shuning uchun instinkt — CPU'da togri instinkt — har bir WMA'ni prefiks-yigindilar yordamida O(n·p) ornida O(n) qilishdir. Siz ikkita jamlangan yigindini bir marta oldindan hisoblaysiz,

S1[t]=jtxj,S2[t]=jtjxj,S_1[t] = \sum_{j \le t} x_j, \qquad S_2[t] = \sum_{j \le t} j \cdot x_j,

va keyin har qanday oynaning chiziqli ogirlashtirilgan yigindisi S1 va S2 ning bir necha ayirmasi va indeks siljishlariga qisqaradi. Oyna boyicha halqa yoq, oyna boyicha reduksiya yoq — ikkita cumsum otishi va butun WMA matritsasi massiv arifmetikasidan chiqib keladi. U ajoyib vektorlashadi, u GPU'ning parallel-skan primitiviga mukammal moslashadi va fp64'da u aynan togri.

Bu shuningdek fp32'da qila oladigan eng yomon narsadir va sabab S2 da yashiringan.

S2 = mx.cumsum(j * price)     # j is the global bar index: 0, 1, 2, ... , n-1

j · price hadi — muammo. j 150,000 gacha, price esa taxminan 30,000 bolganda, oxirgi hadning ozi 4.5×10⁹ boladi va S2 — 150,000 ta shunday hadning jamlanuvchi yigindisi. U narx masshtabida qolmaydi. U fp32 allaqachon sanay olmay qolgan hududga koтариладi.

Qaerda toshib ketadi: tuzoqning arifmetikasi

Ikkita ulkan ~10¹⁴ fp32 yigindisi kichik oynali qiymatni tiklash uchun ayriladi, u esa ularning yaxlitlash xatosi ostida yoqoladi — halokatli qisqarish

Kattaliklar tartiblarini yonma-yon qoyaylik, chunki butun nosozlik shu erda yashaydi.

S2 = cumsum(j · price) taxminan price · n²/2 ≈ 30,000 · (150,000)²/2 ≈ 3×10¹⁴ ga etadi. Uni ~10¹⁴ deylik. Endi fp32 aniq-butun shiftini eslang: ~1.6×10⁷. Jamlanuvchi yigindi fp32 aniq ifodalay oladigan oxirgi butun sondan etti tartibga oshib ketadi.

Bu aniq nimani anglatadi? 10¹⁴ atrofida ikkita ifodalanadigan fp32 raqami orasidagi tafovut — oxirgi ozindagi bir birlik, ULP — taxminan 2²³ ≈ 8×10⁶. Shunday qilib, S2 10¹⁴ diapazonida bolgach, u faqat ±8 million aniqlikda malum. U saqlaydigan har bir qiymat ~8×10⁶ ning eng yaqin karralisiga yaxlitlangan.

Endi WMA tiklashi bu bilan nima qilishini kuzating. Bitta oynaning ogirlashtirilgan yigindisini olish uchun siz ikkita qoshni S2 qiymatini ayirasiz (ustiga S1 tuzatishlari). Bu ikki S2 qiymati har biri ~10¹⁴, har biri ±8×10⁶ yaxlitlash shovqinini olib yuradi. Ularning haqiqiy ayirmasi — sizga aslida kerak bolgan oynali miqdor — normallashuvdan keyin narxning ozi tartibidagi WMA'ga mos keladi, ~3×10⁴. Demak arifmetika shunday:

(1014±8×106)fp32(1014±8×106)fp32=sizga 104 da bir necha qismgacha kerak bolgan miqdorhaqiqiy javob\underbrace{(10^{14} \pm 8\times10^6)}_{\text{fp32}} - \underbrace{(10^{14} \pm 8\times10^6)}_{\text{fp32}} = \underbrace{\text{sizga } 10^4 \text{ da bir necha qismgacha kerak bolgan miqdor}}_{\text{haqiqiy javob}}

Bu — halokatli qisqarish eng sof korinishida: har bir operandning yaxlitlash xatosi (±8×10⁶) siz tiklamoqchi bolgan javobdan kattaroq. Signal u chiqarilayotgan raqamlarning shovqin polidan kichikroq. Bu bir necha raqamni yoqotish emas — siz ularning hammasini yoqotasiz va qaytadigan narsa cumsum'ning jamlangan yaxlitlashi bilan hukmronlik qilinadi.

Olchangan oqibat, GPU_NOTES.md dan: shu tarzda 150k bar boyicha ~30,000 narxda hisoblangan WMA uchun fp64'ga nisbatan maksimal nisbiy xato ~211 ga yetadi. 211 foiz emas — 211×. Hisoblangan harakatlanuvchi ortacha haqiqiysidan ikki tartibga uzoq bolishi mumkin. Va uni xatodan kora tuzoq qiladigan qism shu: u oxirigacha ishlaydi va chekli, ishonarli raqamlar qaytaradi. Cheksizlikka toshish yoq, NaN yoq, istisno yoq. 211× xato harakatlanuvchi ortacha baribir harakatlanuvchi ortachaga oxshaydi — u silliq, u chekli, qisqarish yumshoq bolgan barlarda taxminan togri chegarada — shuning uchun u ishonchli malumotnomaga togridan-togri taqqoslash bolmagan har qanday esli-hushlilik tekshiruvidan otib ketadi. Siz tola backtest, tola kapital egri chizigi, tola "optimal" parametrlar toplamini olasiz, hammasi badiiy toqima bolgan indikatorga qurilgan.

Yechim koproq aniqlik emas — bu boshqacha yigindi

~200 ta bir masshtabdagi hadning qisqa siljuvchi oynasi konvolyutsiya bilan togridan-togri jamlanadi — har bir qisman yigindi narx masshtabiga yaqin qoladi, fp32 ning aniq diapazonida

Xatoni korgach, refleks koproq aniqlik uchun qol chozishdir — fp64'da jamlash yoki kompensatsiyalangan (Kahan) yigindidan foydalanish. Metal'da birinchisi shunchaki mavjud emas. Ammo sizga ikkalasi ham kerak emas, chunki muammo hech qachon bitlar soni bolmagan. Muammo formulirovka edi. Prefiks-yigindi hiylasi 10¹⁴ masshtabidagi oraliqlarni ishlab chiqaradi va keyin ularni qaytadan ayiradi; u yaratadigan kattaliklar javobning emas, algoritmning artefaktidir. Ularni hech qachon yaratmaydigan formulirovkani tanlang va fp32 yaxshi ishlaydi.

Bu formulirovka — tarifning ozi: togridan-togri oynali konvolyutsiya. Ikkita global jamlangan yigindi ornida, p uzunlikdagi chiziqli yadroni qator boyicha suring va uni joyida jamlang. Har bir chiqish kopi bilan p ≈ 200 ta hadning yigindisi va har bir had weight × price, bu erda ogirliklar yigindisi 1 ga normallashtirilgan — shuning uchun har bir had price / p tartibida, har bir qisman yigindi narx masshtabi atrofida (~3×10⁴) qoladi va hech qanday oraliq hech qachon fp32 shiftining olti tartibiga yaqinlashmaydi. Qisqartiradigan hech narsa yoq, chunki hech narsa hech qachon shishmagan.

MLX'da bu bitta primitiv — mx.conv1d — bu aynan GPU'lar tez bajarish uchun qurilgan narsa:

def _mx_wma_valid(x, period):
    w = mx.arange(1, period + 1, dtype=mx.float32) / (period * (period + 1) / 2.0)
    return mx.conv1d(x.reshape(1, -1, 1), w.reshape(1, period, 1), padding=0).reshape(-1)

Bir xil WMA, prefiks-yigindi versiyasiga va CPU'ning fp64 vec_wma/nb_wma siga matematik jihatdan bir xil. Ammo endi olchangan fp64'ga nisbatan maksimal nisbiy xato 8.2×10⁻⁷ — aynan ~1.2×10⁻⁷ fp32 shovqin polida, etti muhim raqam kelishuvi. Qogozda sekinroq korinuvchi formulirovka (O(n) ornida O(n·p)) yagona togri boladigani va — chunki bu GPU barlar va oynalar boyicha bir vaqtda parallellashtiradigan zich konvolyutsiya — u shuningdek chaqmoq tezlikda tez. Biz 211 nisbiy xatodan 8×10⁻⁷ ga qanday jamlash orqali otdik, necha bitda jamlash orqali emas.

Buni shu tarzda qilishdan kelib chiqadigan ikkita amaliy qayd. Birinchi, MLX conv1d orqali numpy kabi NaN'larni tarqatmaydi, shuning uchun isinish hududini (birinchi p−1 bar, bu erda oynali ortacha aniqlanmagan) GPU'da NaN bilan belgilab bolmaydi. Bizga bu kerak emas: har bir qatorning haqiqiy boshlanishi analitik malum, yaroqsiz prefikslar hech qachon oqilmaydigan nollar bilan toldiriladi va NaN toldirish keyinchalik CPU'da tiklanadi — vektorlashtirilgan va numba versiyalari bilan bit-baba-bit bir xil haqiqiylik niqobi. Ikkinchi, butun saralash bitta cand_close qatorini boishadi va kombinatsiyalar boyicha oynalarni koplab qayta ishlatadi, shuning uchun koplab chiqish kanallariga ega bitta partiyali conv1d saralashga kerak bolgan har bir noyob WMA'ni bitta GPU chaqiruvida hisoblaydi, bitta mx.eval() bilan materiallashtiriladi.

Unga tushmaganingizni isbotlash: savdolar soni boyicha paritet

Mana oxirgi bolim keltirib chiqarishi kerak bolgan nokulay savol: agar 211× xato WMA baribir WMA'ga oxshasa, 8×10⁻⁷ versiyasi haqiqatan togri, faqat nozikroq tarzda notogri emasligini qanday bilasiz? Uni koz bilan chamalab bolmaydi. Sizga quvurning quyi oqim, diskret qismi ochib beradigan invariant kerak — va backtest sizga mukammalini beradi: savdolar.

Zinapoyadagi boshqa GPU metodlari (M0–M4) tolaligicha fp64'da ishlaydi, shuning uchun biz ularni qatiy ekvivalentlik assertiga tutamiz — bir xil savdolar soni, PnL atol=1e-6 gacha mos keladi. fp32 GPU metodi (M5) buni tuzilishi boyicha ota olmaydi va uni sigdirish uchun hamma uchun assertni jimgina bosashtirish aynan bu turkum qarshi kurashish uchun mavjud bolgan xolislik turi bolardi. Shuning uchun M5 ozining miqdoriy paritet hisobotini oladi, report_equiv_fp32, ozining chiqarilgan savdolarini fp64 malumotnomasiga taqqoslaydi.

Har qanday qoldiq kelishmovchilikning mexanizmini aniq nomlash arzigulik, chunki bu qisqarish halokati emas — bu kutgan oddiy, minglik fp32 yaxlitlashidir. Strategiya ikkita Hull ortachasining kesishuvida ishga tushadi, h ga qarshi h3. ~30,000 narxdagi indikatorda ~1×10⁻⁶ nisbiy xato ~0.03 mutlaq tebranishdir. Barlarning katta kopchiligida ikki egri chiziq bundan uzoqroq va kesishuv aniq. Ammo chegaraviy barda — bu erda h − h3 ning ozi noldan 0.03 ichida — bu tebranish taqqoslash belgisini tognatishi, bir kesishuvni bitta bar bilan siljitishi, bitta savdoni qoshishi yoki olib tashlashi mumkin.

Shuning uchun "farq qiladigan kombinatsiyalar ulushi" foydasiz salomatlik metrigi va bizning birinchi paritet tekshiruvimiz undan foydalanib ozini uyaltirdi. 150k barda har bir kombinatsiyada minglab kesishuvlar bor, shuning uchun kamida bitta chegaraviy bar deyarli har bir kombinatsiyada paydo boladi — 80 tadan 37 kombinatsiya "farq qildi", bu tashvishli tuyuladi va hech narsani anglatmaydi. Ahamiyatga ega bolgan metrik — qanchalik:

  • Barcha 80 kombinatsiya boyicha PnL delta: maks |Δ| = 1.843 foiz punkti, maks nisbiy = 1.25×10⁻²; halokat chegarasi 5 p.p.
  • Kombinatsiya boyicha savdolar sonining siljishi: maks |Δn| = minglab savdodan 4 savdo, maks nisbiy = 2.5×10⁻³; halokat chegarasi 1%.
  • Yigindida: 479,016 savdodan 90 tasi siljigan — 0.019%.

Deyarli yarim milliondan toqson savdo, har biri narx belgisidan kichikroq yaxlitlash tebranishi bilan turtkilangan chegaraviy kesishuv, hech biri halokat chegaralariga yaqin emas. Bu — togri bolgan fp32 metodining imzosi — kichik, chegaralangan, tushuntiriladigan kelishmovchilik — va bu 211 nisbiy xatodan mutlaqo boshqa jonzot. Chegaralar buzilgan formulirovkani "xop, bu shunchaki fp32-da" niqobi ostida tutish uchun mavjud; haqiqiy deltalar ulardan bir tartibga pastda keladi. Savdolar soni — kapital egri chizigi bolishdan bosh tortgan orakuldir.

Foyda va GPU qaerda yordam berishni toxtatadi

Togrilik ornatilgach, tezlik aytishga arzigulik — va keyin halol shartlangan, chunki GPU'ning ustunligi quvur boyicha bir tekis emas.

Yakka WMA konvolyutsiyalarida — butun metod tezlashtirish uchun mavjud bolgan amal — fp32 conv1d partiyasi shu 8.2×10⁻⁷ nisbiy xatoda bir oqimli numba'dan 55.9× tezroq ishlaydi. Bu — toza, olma-olmaga GPU-qarshi-CPU raqami: bir xil matematika, kompilyatsiyalangan CPU kodining bir oqimi Metal GPU'ga qarshi.

Ammo saralash faqat konvolyutsiyalar emas. HMA/HMA3 matritsalari GPU'da hisoblangach, savdolar hali ham chiqarilishi kerak — har bir kombinatsiyaning kesishuvlari boyicha O(n) yurish — va buni biz CPU'da fp64'da qilamiz, boshqa metodlarning aynan savdo semantikasini qayta ishlatib, ularni GPU'da qaytadan amalga oshirish ornida. Uchdan uchgacha timed() raqami hamma narsani oz ichiga oladi: yadro isinishi chiqarib tashlangan (numba kompilyatsiyasini chiqarib tashlash bilan simmetrik), lekin GPU→CPU otkazish va CPU savdo chiqarish kiritilgan. 150k bar × 80 kombinatsiya, 3 tadan eng yaxshisi, M2 Max'da:

Metod Devor vaqti Bazaga nisbatan tezlanish komb/s
M0 pandas + Python loop* 287.08s 1.0× 0.3
M1 vektorlashtirilgan numpy 3.14s 91.5× 25.5
M2 numba (serial) 2.02s 142.3× 39.7
M3 multiprocess + vektorlashtirilgan 0.50s 570.2× 158.9
M4 multiprocess + numba (12 yadro) 0.33s 882.5× 245.9
M5 MLX GPU (fp32) 0.10s 2796.0× 779.2

*M0 bir tekis 5-kombinatsiyali namunadan ekstrapolyatsiya qilingan.

Tola dvigatelli M5 779 komb/s qiladi — pandas bazasidan 2,796×, serial numba'dan 19.6× va numba (M4) ishlatadigan butun 12 yadroli CPU basseynidan 3.2×. Bitta GPU mashinadagi har bir CPU yadrosini uch baravar utadi.

Endi halol shart: uchdan uchgacha GPU ustunligi (M2'dan 19.6×) faqat konvolyutsiya ustunligidan (numba'dan 55.9×) kichikroq ekanligini payqang. Bu tafovut — jadval boyicha keladigan Amdal qonuni. GPU konvolyutsiyalarni shunchalik tola yoq qiladiki, ular tor joy bolishni toxtatadi; qolgan narsa — GPU umuman tezlatmagan O(n) CPU savdo chiqarishi — endi M5'ning devor vaqtida hukmronlik qiladi. Bu bu turkumdagi tezlik-zinapoyasi va IPC-soligi maqolalari doim tushib keladigan bir xil saboq: malum nuqtadan keyin golib "tez qismni tezroq qilish" emas, u orkestrlash — malumot qaerda yashaydi, qaysi bosqich endi serial, qurilma va host orasida kochirish uchun nima tolayapsiz. Savdo chiqarishni maxsus Metal yadrosiga surkovchi faraziy M6'ni quvish faqat qisqarayotgan CPU tilimini qaytarib olar edi, shuning uchun biz uni qurmadik.

Umumiy saboq: jimgina sonli axlat — sukut boyicha holat

Hull ortachalari va MLX'dan bir qadam orqaga chekining, chunki tuzoq bu bitta indikatordan ancha uzoqqa umumlashadi.

GPU backtestingning vasvasali taklifi — "bitta katta matritsa": har bir parametr kombinatsiyasini tenzorga toplang, butun saralashni bir necha zich massiv amali sifatida ishga tushiring, apparat uni yesin. Bu taklif haqiqiy — yuqoridagi tezlanishlar haqiqiy. Ammo u ostingizdagi sonli rejimni jimgina ozgartiradi va ozgarish kodda korinmaydi. CPU'da sizning sukutlaringiz sizni himoya qildi: fp64, NaN tarqalishi, payqamasdan 10¹⁴ gacha ishlay oladigan cumsum. Bir xil massiv ifodasini Metal'ga kochiring va siz qatiy 1.6×10⁷ butun son shifti bilan fp32'dasiz va aynan bir xil kod qatori — cumsum(j * price) — aniqdan axlatga otadi. Sintaksisda hech narsa sizni ogohlantirmaydi. Kompilyator xursand. Chiqish chekli va ishonarli. fp32 baland ovozda buzilmaydi; u odob bilan, raqamlar bilan buzoladi.

Sizni haqiqatan himoya qiladigan uchta odat arzon:

  1. Oraliqlaringiz qaerda yashashini biling, faqat kirish va chiqishlaringizni emas. Kirishlar (narxlar ~10⁴) va chiqishlar (WMA ~10⁴) ikkalasi ham fp32 ning aniq diapazonida bemalol edi. Falokat tolaligicha yashirin oraliqda edi (S2 ~10¹⁴), uni na API na turlar korinadigan qilgan. Har qanday fp32 reduksiyasiga ishonishdan oldin, eng katta qisman yigindi qanchaga etishini soang — va agar u ~10⁷ dan otsa, formulirovkani ozgartiring.
  2. Kattaliklarni chegaralangan saqlaydigan formulirovkalarni afzal koring. Prefiks-yigindilar ornida togridan-togri konvolyutsiya; global skanlar ornida lokal oynalar; jamlashdan keyin emas, oldin markazlash/ayirish. Katta-keyin-qisqartir — anti-pattern. Togri algoritm kopincha qogozda asimptotik jihatdan yomonroq korinadigani, lekin hech qachon qisqartirishi kerak bolgan miqdorni ishlab chiqarmaydigani.
  3. fp64 orakuli boyicha diskret invariant orqali tasdiqlang. Egri chiziqlarni taqqoslamang; kvantlangan va quyi oqim narsani taqqoslang — savdolar soni, kesishuvlar soni, pozitsiya-ozgarish hodisalari. Diskret invariant jimgina 211× xatoni qichqiruvchi assert nosozligiga aylantiradi va maqbul 8×10⁻⁷ xatoni kichik, chegaralangan, tushuntiriladigan deltaga aylantiradi. Bu oldinga qarash siljishi uchun bir-bar siljish testi bilan bir xil intizom: korinmas nosozlikni korinadiganiga aylantiradigan arzon diagnostika.

Bularning hech biri ekzotik sonli tahlil emas. Bu — sekin, ishonchli backtest uni tasdiqlamaguncha tez backtestga ishonmaslikning oddiy gigienasi — tilning aniqlik jimgina on ikki tartibga tushib ketganini ogohlantirishni toxtatadigan yagona joyga kengaytirilgan.

Xulosalar

Bir qatorli burilish: cumsum(j·price) 211× xatoda ochirilgan, mx.conv1d yonida 8×10⁻⁷ da — bir xil WMA, boshqa yigindi

  1. Apple'ning GPU'sida float64 yoq — backtestingizdagi har bir GPU raqami fp32. Butun sonlar faqat ~1.6×10⁷ gacha aniq va aniqlik ~1.2×10⁻⁷. Bayroq yoq, zaxira yol yoq. Backtestning katta qismi bundan omon qoladi; aniq bitta amal odatda qolmaydi.
  2. Prefiks-yigindi WMA — tuzoq. cumsum(j · price) ~10¹⁴ ga kotariladi, fp32 ning aniq shiftidan etti tartib narida va oynani tiklash sizni yaxlitlash xatosi (±8×10⁶) allaqachon javobni kichraytirgan ikkita shunday raqamni ayirishga majbur qiladi. Olchangan maks nisbiy xato: 211×. U hech qachon qulamaydi — u ishonarli axlat qaytaradi.
  3. Yechim — boshqa yigindi, koproq bit emas. Togridan-togri oynali konvolyutsiya (mx.conv1d) har bir qisman yigindini narx masshtabiga yaqin saqlaydi, shuning uchun fp32 etti halol raqam ushlab turadi: 8.2×10⁻⁷ nisbiy xato va bir oqimli numba'dan 55.9× tezroq. Metal'da fp64'ni sotib ololmaysiz va sizga kerak ham emas.
  4. Diskret invariant bilan tekshiring, hech qachon egri chiziq bilan emas. Savdolar soni pariteti uni ushladi: fp32 conv1d fp64 bilan 479,016 savdodan 90 tasida (0.019%) kelishmadi, hammasi chegaraviy kesishuvlar, hammasi halokat chegaralaridan ancha past — togri metodning imzosi, 211× xatodan aniq farqli. "Farq qiladigan kombinatsiyalar ulushi" — chalgituvchi metrik; qanchalik ekanligini olchang.
  5. Tola saralash 779 komb/s qiladi — pandas bazasidan 2,796×, butun 12 yadroli CPU basseynidan 3.2× — lekin uchdan uchgacha golib (serial numba'dan 19.6×) faqat konvolyutsiya golibidan (55.9×) kichikroq, chunki CPU savdo chiqarishi endi tor joy. Malum nuqtadan keyin tezlik — orkestrlash, arifmetika emas.

GPU kochirish 2,796× tezroq edi va, ozining birinchi ishlaydigan versiyasida, tolaligicha notogri — va bu ikki fakt bir-biri bilan bogliq emas edi. Tezlik haqiqiy edi. Axlat — fp32 ushlab turolmagan va hech qanday xato xabari eslatib otmaydigan yashirin 10¹⁴ oraliq edi. Agar backtest keskin tezlashsa va raqamlar hali ham yaxshi korinsa, bu tasdiqlash emas. Metal'da "yaxshi korinadi" — aynan 211 nisbiy xato korinishidir.

Bu — bu turkum tirmashib chiqayotgan zinapoyaning GPU pogonasi: backtest-dvigateli tezlik zinapoyasi, koplab jarayonli ishlovning IPC soligi, oqishlarning oldinga qarash taksonomiyasi va "yaxshi" nima ekanligini hal qiladigan maqsad-funksiyasi dizayni. Agar notogri raqamni hisoblashda tez bolsa, tezlik befoyda.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Bozordan bir qadam oldinda bo'ling

Sun'iy intellekt savdo tahlillari, bozor tahlili va platforma yangiliklari uchun bizning xabarnomaga obuna bo'ling.

Biz sizning maxfiyligingizni hurmat qilamiz. Istalgan vaqtda obunadan chiqishingiz mumkin.