pandas ewm().mean() is not the textbook EMA unless adjust=False
pandas 3.1.0. The default adjust=True normalises the weights over the observations seen so far, (sum (1-a)^i x_{t-i}) / (sum (1-a)^i). The recursion y_t = a*x_t + (1-a)*y_{t-1} with y_0 = x_0 is adjust=False, and it matched exactly (max diff 0.0). On a random walk with unit steps and span=20 (a = 2/21), the two differ by 0.06 at t=1, 0.30 at t=10-20, 0.06 at t=40 and 0.008 at t=60. The gap decays like (1-a)^t. Over 20 seeds, the last time the gap was >= 1e-3 (unit steps) fell between t=75 and t=99, about 4-5 spans. TA-style EMAs seeded with the SMA of the first N values match neither: against adjust=True they differ by 1.19 at t=19, 0.17 at t=40 and 1e-4 at t=100. With NaN gaps, the default ignore_na=False decays weights by absolute position: [1, nan, nan, nan, 3] with alpha=0.5 gives 2.882, while ignore_na=True gives 2.333. To match another tool, set adjust, ignore_na and the seed explicitly, and compare only after the burn-in.