Files
QuanTAlib/python/tests/__pycache__/test_pandas_ta_parity.cpython-313-pytest-9.0.2.pyc
T

231 lines
29 KiB
Plaintext
Raw Normal View History

2026-02-28 14:14:35 -08:00
ó
ËI£iùGãó²SrSSKJr SSKrSSKJs Jr SSK r
SSK r SSK
rSSKrSSKJrJrJrJrJrJrJrJrJrJrJrJrJrJrJr Sr Sr!Sr"Sr#\ S S
S S 4S!S
jjr$\$"\!5r%\ RL"\%SS9r'\"\#SS.S"Sjjr("SS5r)"SS5r*"SS5r+"SS5r,"SS5r-"SS5r."SS 5r/g)#uÚtest_pandas_ta_parity.py — Validate quantalib vs pandas-ta using the same
methodology as our C# ValidationHelper:
1. Generate a LONG GBM series (5000 bars, seeded) so recursive indicators converge.
2. Compare only the LAST 100 values (DefaultVerificationCount = 100).
3. Skip lookback/warmup bars before comparison window.
4. Tolerance: 1e-7 default, looser for known algorithmic differences (SPEC §9.3).
This mirrors lib/feeds/gbm/ValidationHelper.cs exactly.
é)Ú annotationsN)ÚsmaÚemaÚdemaÚtemaÚwmaÚhmaÚtrimaÚalmaÚrsiÚrocÚmomÚstddevÚvarianceÚzscoreÚbbandsé*é'édç•Ö&è .>gY@gš™™™™™©?gš™™™™™É?gAAp?cóÆ[RRU5nURUS-
5nUSUS---
U-nU[R"U5-U-n X‰-n
[R
"U[R S9n X+S'[R"X«SSS9 U SS===[R"U5-
sss&[R"U SSU SSS9 X+S'U $)zsGenerate GBM close prices matching C# GBM feed logic.
S(t+1) = S(t) * exp((mu - sigma^2/2)*dt + sigma*sqrt(dt)*Z)
égà?é)ÚdtyperN)Úout)
ÚnpÚrandomÚ default_rngÚstandard_normalÚsqrtÚemptyÚfloat64ÚcumsumÚlogÚexp) ÚseedÚ start_priceÚmuÚsigmaÚdtÚrngÚdriftÚ diffusionÚ log_returnsÚpricess Ú9c:\github\quantalib\python\tests\test_pandas_ta_parity.pyÚ
_generate_gbmr3#ô
)‰)×
Ñ
 Ó
%€CØ ×јA ™EÓ"€AØ
#˜ 
Ñ
"  (€EØœŸš  Ñ# aÑ'€IØÑ#€KÜ
XŠXaœrŸz™zÑ
*€F؈1‡IIˆk a b˜
ˆ1ˆ2ƒJ”"—&&˜Ó%ƒJ܇FFˆ6!"ˆ:˜6 ! "˜ˆ1 €MóÚclose©ÚnameÚ)Ú verify_countÚ toleranceÚlabelc ó’
[U[R5(aUR5OUn[ U5n[ U5ovU:Hoˆ(G[
R "SU4SXg45S[R"5;d[
R"[5(a[
R"[5OSS[R"5;d[
R"U5(a[
R"U5OS[
R"U5S[R"5;d[
R"[5(a[
R"[5OSS[R"5;d[
R"U5(a[
R"U5OS[
R"U5S.-n [
R"US[ U5S[ U535S -S
U 0-n
[[
R"U
55eS =n=p‡[ U5n [S X²-
5n X S n
X\S n[R "U
5[R "U5-n[R"nU"U5nS oxU:„nU(Gd>[
R "S
U4SX‡45S[R"5;d[
R"[5(a[
R"[5OS[
R"U5S[R"5;d[
R"U5(a[
R"U5OS[
R"U5[
R"U5S.-n [
R"USU35S -S
U 0-n
[[
R"U
55eS =n=n=nnnn[%[R"[R&"UU-
555nUU:*nU(Gd
[
R "SU4SUU45S[R"5;d[
R"U5(a[
R"U5OSS[R"5;d[
R"U5(a[
R"U5OSS.-n[
R"USUSSUSS[ U5SUS3
5S-S U0-n[[
R"U55eS ng )!zÈCompare only the last `verify_count` values where both are finite.
This matches the C# pattern:
int start = Math.Max(0, count - skip);
for (int i = start; i < count; i++) { ... compare ... }
©ú==)zN%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
lenÚqtl_arrÚpta)Úpy0Úpy1Úpy3Úpy5Úpy6Úpy8z: length mismatch qtl=z vs pta=z
>assert %(py10)sÚpy10Nr)Ú>)zH%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.sum
}(%(py3)s)
} > %(py8)srÚfinite)rBÚpy2rDrErGz: no finite values in last ©ú<=)z%(py0)s <= %(py2)sÚmax_diffr:)rBrKz : max_diff=ú.2ez exceeds tolerance=z.0ez (compared z values in last Ú)z
>assert %(py4)sÚpy4)Ú
isinstanceÚpdÚSeriesÚto_numpyr?Ú
@pytest_arÚ_call_reprcompareÚ @py_builtinsÚlocalsÚ_should_repr_global_nameÚ _safereprÚ_format_assertmsgÚAssertionErrorÚ_format_explanationÚmaxrÚisfiniteÚsumÚfloatÚabs)r@Ú
pta_resultr9r:r;rAÚ @py_assert2Ú @py_assert7Ú @py_assert4Ú @py_format9Ú @py_format11ÚcountÚstartÚq_tailÚp_tailrJÚ @py_assert1Ú @py_assert6Úq_valsÚp_valsrNÚ @py_format3Ú @py_format5s r2Ú_verify_last_nrt@ô$.¨j¼"¿)¹)×#DÑ#Dˆ*×
Ñ
Ô
È*€CÜ ˆw‹<ðœ3˜s›8ð˜8Ñ ôïéôˆ<÷òïé÷õïéðŒ3÷öïéðŒ3÷óïé÷õïéðˆw÷öïéðˆw÷òïéðˆ<÷ñïé÷õïéðœ3÷öïéðœ3÷óïé÷õïéð˜s÷öïéð˜s÷òïéð˜8÷õïéðØ ˆ¨G«  ~°X¼cÀ#»h¸ZÐ÷öïé÷÷ô
L€EÜ  (€Eà
Vˆ_€FØ
ˆ[€Fô[Š[˜Ó
¤2§;¢;¨vÓ#6Ñ
6€FÜ
6‰6ÐR‰6&R˜R˜ ×R×R×Rˆ>×R×R×R×R×RŒ2×RŒ2×R×Rˆ6×R×R×R×R×R&×R×R×R&×R×Rˆ>×R×R˜A×R×R×R % Ð(CÀLÀ>ÐR×R×R×R×R×R×
‰^€FØ
‰^€Fä”R—V’VœBŸFšF 6¨F¡?Ó5€HØ  ÷õïéôˆ8òïé÷õïéðˆ8÷öïéðˆ8÷óïé÷õïéðöïéð÷ïéðØ ˆ'˜X c˜NÐ*=¸¸Mܘ“[MÐ!1°,°¸qð B÷÷öïé÷õr4cóº\rSrSrSrS
SjrS
SjrS
Sjr\RRSS9S
Sj5r \RRS S9S
S
j5r S r
g )Ú TestTrendFIRékuMFIR indicators: SMA, WMA, HMA — deterministic convolution, tight tolerance.c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)é
éé2©ÚlengthrzSMA(rP©r:r;)rÚCLOSEÚtaÚSERIESrt©Úselfr~ÚqtlrAs r2Útest_smaÚTestTrendFIR.test_smanó@Û"ˆ”e +ˆ—&¨Ñ/ˆ ˜3¨tØ#'¨ x¨qÐ!1ô
#r4c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)rzéér}rzWMA(rPr)rr€rrrts r2Útest_wmaÚTestTrendFIR.test_wmaurˆr4c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)N)é rr{r}ç:Œ0âŽyE>zHMA(rPr)r r€rrrts r2Útest_hmaÚTestTrendFIR.test_hma|ó@Û!ˆ”e +ˆ—&¨Ñ/ˆ ˜3¨tØ#'¨ x¨qÐ!1ô
"r4u•TRIMA kernel differs: quantalib uses symmetric triangular convolution, pandas-ta delegates to TA-Lib which uses cascaded SMA (SPEC §9.3 known delta)©Úreasoncóh[[SS9n[R"[SS9n[ XSSS9 g)Nrr}rz TRIMA(14)r)r
r€rrrt©r„r…rAs r2Ú
test_trimaÚTestTrendFIR.test_trimaƒs,ô”E %ˆÜhŠh”v )ˆÜs¨4°{ÓCr4uZALMA sigma/offset defaults differ between quantalib and pandas-ta (SPEC §9.3 known delta)cóh[[SS9n[R"[SS9n[ XSSS9 g)Nrr}çíµ ÷ư>zALMA(14)r)r r€rrrtr˜s r2Ú test_almaÚTestTrendFIR.test_almas,ô”5 ÑÜgŠg”f RÑÜs¨4°zÓBr4©ÚreturnÚNone)Ú__name__Ú
__module__Ú __qualname__Ú__firstlineno__Ú__doc__r†rrÚpytestÚmarkÚxfailr™rÚ__static_attributes__rŸr4r2rvrvkstÙ ‡[ÑðRÐðSóDóSðDð
 ‡[ÑðRÐðSóCóSóCr4rvcó6\rSrSrSrSSjrSSjrSSjrSrg) Ú TestTrendIIRé—u IIR indicators: EMA, DEMA, TEMA — recursive convergence.
With 5000 bars the warmup difference is buried in the past.
The last 100 bars should match tightly.
c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)Nryr}çH¯¼šò×z>zEMA(rPr)rr€rrrts r2Útest_emaÚTestTrendIIR.test_emažrˆr4c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)Nryr}zDEMA(rPr)rr€rrrts r2Ú test_demaÚTestTrendIIR.test_dema¥ó@Û"ˆ”u ,ˆ—''œ&¨Ñ0ˆ ˜3¨tØ#(¨¨°Ð!2ô
#r4c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)NrŠr}zTEMA(rPr)rr€rrrts r2Ú test_temaÚTestTrendIIR.test_tema¬r4Nr ) r£r¸r4r2r­r­sñô 4r4r­cón\rSrSrSrS
Sjr\RRSS9S
Sj5r S
Sjr
Sr g ) Ú TestMomentumé¸z$Momentum: RSI (recursive), ROC, MOM.c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)zqRSI is recursive (Wilder smoothing). With 5000 bars, warmup
convergence difference is negligible in the last 100.)érér}zRSI(rPrN)r r€rrrts r2Útest_rsiÚTestMomentum.test_rsi»sBó"ˆ”e +ˆ—&¨Ñ/ˆ ˜3¨tØ#'¨ x¨qÐ!1ô
"r4u“ROC formula differs: quantalib uses absolute difference (close-prev), pandas-ta uses percentage ((close/prev - 1)*100). Known delta per SPEC §9.3.r•cóh[[SS9n[R"[SS9n[ XSSS9 g)z¶ROC: quantalib 'Roc' is Rate of Change (Absolute) = close - close[n].
pandas-ta 'roc' is Rate of Change (Percentage) = ((c/c[n])-1)*100.
These are fundamentally different indicators.rzr}zROC(10)rN)r
r€rrrtr˜s r2Útest_rocÚTestMomentum.test_rocÄs,ô”% Ñ#ˆÜfŠf”V 'ˆÜs¨4°yÓAr4c ó‚SH9n[[US9n[R"[US9n[ X#SSUS3S9 M; g)N)érzr{r}rzMOM(rPr)rr€rrrts r2Útest_momÚTestMomentum.test_momÐr”r4Nr ) r4r2¸s<Ù ‡[ÑððBó B÷3r4có6\rSrSrSrSSjrSSjrSSjrSrg) ÚTestStatisticséÜzÓSTDDEV, VARIANCE, ZSCORE.
Note: pandas-ta uses sample stddev (ddof=1), quantalib may use population.
With 5000 bars and period=20, the difference is ~5% for ddof effect.
We use relative tolerance where needed.
c óØSGn[[US9n[R"[US9nUR 5n[
U5n[SU[-
5nX&SnXFSn[R"U5[R"U5-n [R"U 5S:Xa[R"SUS35 Xyn
X‰n [[R"[R"X«-
555n U S::a gU
[R "XS-
- 5-n
[[R"[R"XÛ-
555nUS::a gU
[R "US-
U- 5-n[[R"[R"Xû-
555nUS::a g[R"SUSU S S
US S US 35 G g) Nryr}rzSTDDEV(ú): no finite in tailrrú
): direct=rOõ, pop→sample=õ, sample→pop=)rr€rÚstdevrrUr?r_Ú VERIFY_COUNTrr`raÚfailrbrcr ©r„r~r…rAÚpta_nprjrkrlrmrJÚq_fÚp_frNÚadjustedÚadj_diffÚ adjusted_invÚinv_diffs r2Ú test_stddevÚTestStatistics.test_stddeväÜ"ˆœ .ˆ—((œ6¨&Ñ1ˆCð—\‘\“^ˆFܘ“HˆEܘ˜5¤<Ñ0ˆEؘ[ˆ˜F^ˆFÜ—[[ Ó(¬2¯;ª;°vÓ+>Ñ>ˆvŠvf~ Ó ˜g f XÐ-AБ.ˆCØ‘.ˆCôœRŸVšV¤B§F¢F¨3©9Ó$5Ó7ˆHؘ4ÓÙðœRŸWšW V¸©zÑ%:Ó;ˆHÜœRŸVšV¤B§F¢F¨8©>Ó$:Ó<ˆ˜4ÓÙ𤧢¨&°1©*¸Ñ)>Ó!?Ñ?ˆœRŸVšV¤B§F¢F¨<Ñ+=Ó$>Ó@ˆ˜Ùä KŠKؘ&˜ ¨H°S¨>ð: Ø (¨˜~¨_¸XÀc¸L÷
òK#r4c ó†SGn[[US9n[R"[US9nUR 5n[ U5n[
SU[-
5nX&SnXFSn[R"U5[R"U5-n [R"U 5S:Xa[R"SUS35 Xyn
X‰n [[R "[R"X«-
555n U S::a gX¡US-
- -n
[[R "[R"XÛ-
555nUS::a gX¡S-
U- -n[[R "[R"Xû-
555nUS::a g[R"SUSU S S
US S US 35 G g) Nryr}rz VARIANCE(rÍrrrO)rr€rrrUr?r_rr`rarbrcs r2Ú
test_varianceÚTestStatistics.test_variances‡Ü"ˆœ5¨Ñ0ˆCÜ—+’+œf¨VÑ4ˆCØ—\‘\“^ˆFܘ“HˆEܘ˜5¤<Ñ0ˆEؘ[ˆ˜F^ˆFÜ—[[ Ó(¬2¯;ª;°vÓ+>Ñ>ˆvŠvf~ Ó ˜ xÐ/CБ.ˆCØ‘.ˆCôœRŸVšV¤B§F¢F¨3©9Ó$5Ó7ˆHؘ4ÓÙ𨰩
Ñ4ˆHÜœRŸVšV¤B§F¢F¨8©>Ó$:Ó<ˆ˜Ùà¨A¡:°Ñ"7Ñ8ˆœRŸVšV¤B§F¢F¨<Ñ+=Ó$>Ó@ˆ˜Ùä KŠKؘF˜8 :¨h°s¨^ð< Ø (¨˜~¨_¸XÀc¸L÷
òA#r4cóSGHyn[[US9n[R"[US9nUR 5n[ U5n[
SU[-
5nX&SnXFSn[R"U5[R"U5-n [R"U 5S:Xa[R"SUS35 Xyn
X‰n [R"XS-
- 5n [[R "[R"X«-
555n
[[R "[R"X«U --
555n[[R "[R"X¬- U -
555n[!XÞU5nSnUU:*nU(["R$"S U4S
UU45S [&R("5;d["R*"U5(a["R,"U5OS ["R,"U5S .-n["R."SUS
USSU
SSUSSUSS3 5S-SU0-n[1["R2"U55eS=nnGM| g)z¶ZSCORE = (x - mean) / stddev.
quantalib uses population stddev (ddof=0), pandas-ta uses sample (ddof=1).
The ratio is sqrt(n/(n-1)). We verify after applying the correction factor.
ryr}rNzZSCORE(rÍrrL)z%(py0)s <= %(py3)sÚbest)rBrDz
): best_diff=rOz (direct=z , corrected=z, inv=rPz
>assert %(py5)srE)rr€rrrUr?r_rr`rar rbrcÚminrVrWrXrYrZr[r\r]r^)r„r~r…rArjrkrlrmrJr×Ú
ddof_ratioÚ diff_directÚdiff_correctedÚdiff_invrârernÚ @py_format4Ú @py_format6s r2Ú test_zscoreÚTestStatistics.test_zscore5s%ô #ˆœ vÑ.ˆCÜ—)’)œF¨6Ñ2ˆCØ—\‘\“^ˆFܘ“HˆEܘ˜5¤<Ñ0ˆEؘ[ˆ˜F^ˆFÜ—[[ Ó(¬2¯;ª;°vÓ+>Ñ>ˆvŠvf~ Ó ˜g f XÐ-AБ.ˆCØ‘.ˆCôŸš °A©:Ñ!6Ó7ˆJô ¤§¢¤r§v¢v¨c©iÓ'8Ó 9Ó:ˆ"¤2§6¢6¬"¯&ª&°¸ZÑ7GÑ1GÓ*HÓ#IÓJˆœRŸVšV¤B§F¢F¨3Ñ+;¸cÑ+AÓ$BÓDˆ{°HÓ=ˆð
4˜4
ô
ïéô 
4˜
ò
÷
õ
ïéð 

ö
÷

ò
÷
˜
õ
ïéð 
ؘ&˜ ¨t°C¨jð9Ø& +¨<¸ÀsÐ7KðLØ n 
÷
ö
ïé÷ 
÷
ñ
ò;#r4Nr ) r£r4r2Üsñô)ôV$÷L'r4cóD\rSrSrSrS S
SjjrS SjrS SjrS SjrSr g) ÚTestMultiOutputiczEMulti-output indicators: BBands returns (upper, middle, lower) tuple.có|[[XS9n[R"[XSS9n[ U[
5(aUSUSUSpvnO^[
US5(a+URS:XaUSS2S4USS2S4USS2S4pvnO"[R"S[U535 [UR5nUV s/sHo™RS 5(dMU PM n
n UV s/sHo™RS
5(dMU PM n n UV s/sHo™RS 5(dMU PM n n /oÚoê(a
U oë(aU nU(GdS S
S[R"5;d[ R""U
5(a[ R$"U
5OS0-oýR'U5 U
(SSS[R"5;d[ R""U 5(a[ R$"U 5OS0-nU
R'U5 U (abSSS[R"5;d[ R""U 5(a[ R$"U 5OS0-nU
R'U5 [ R("U
S50-n[ R*"SU35S-SU0-n[-[ R."U55eS=píXJSR15nXKSR15nXLSR15nWWW4UUU44$s sn fs sn fs sn f)z0Get both quantalib and pandas-ta BBands results.)r~Ústdr)r~ÚddofrrÚndimNzUnexpected bbands return type: ÚBBUÚBBMÚBBLz%(py2)srKÚbbuz%(py4)srQÚbbmz%(py6)srFÚbblzBBands columns not found: z
>assert %(py9)sÚpy9)rr€rrrRÚtupleÚhasattrrñÚtypeÚlistÚcolumnsÚ
startswithrXrYrVrZr[ÚappendÚ_format_boolopr\r]r^rU)r„r~r…rAÚ qtl_upperÚqtl_midÚ qtl_lowerÚcolsÚcrõrnÚ @py_assert0rrrsÚ @py_format7Ú @py_format8Ú @py_format10Ú pta_upperÚpta_midÚ pta_lowers r2Ú _get_bbandsÚTestMultiOutput._get_bbandsfs-ä”U äiŠiœ v¸QÑô cœ5× !Ø,/°©F°C¸±F¸¹F  ˆI  Ü
S˜&×
! c§h¡h°!£mØ,/²°1°©I°sº1¸a¸4±yÀ#ÂaÈÀdÁ)  ˆI  ä KŠKÐ9¼$¸s»)¸Ð
C—K‘KÓ ˆÙÓ6š$Q§,¡,¨u×"5q™$ˆÐÓ6š$Q§,¡,¨u×"5q™$ˆÐÓ6š$Q§,¡,¨u×"5q™$ˆÐGsG•s˜s×G×G×G×G×G×Gˆs×Gˆs×G×GŽs×G×G×G×G×Gs×G×G×Gs×G×G×Gs×G×G˜s×G˜s×G×G×G×G×GÐ&@ÀÀÐ$G×G×GטA™‘K×*ˆ ؘ!f+×(ˆØ˜A™K×*ˆ à˜7 IиGÀYÐ0OÐOùò7ùÚ6ùÚ6s$ÃL/Ã!L/Ã-L4Ä
L4ÄL9Ä3L9cóLUR5uupnupn[X#SSS9 g)z(Middle band = SMA, should match exactly.rz
BBands middlerr