Bridging 19th‑century mathematics and 21st‑century trading methods
A client sent me what looked like a simple indicator written in TradingView’s Pine Script—though I didn’t realize it was Pine at first—and asked if I could port it to EasyLanguage (or PowerLanguage for MultiCharts). If you Google “Gaussian Channel Donovan Wall TradingView,” you’ll find the original code. Pine Script isn’t exactly newcomer-friendly; it’s fine once you get the feel for it, but I’m spoiled by EasyLanguage, which —at least to my eye—reads almost like plain English. (Others may beg to differ!) Below is a brief Pine snippet; to this humble EL devotee, it’s more hieroglyphics than prose.
I could see right away that the code was doing some kind of coefficient “lookup,” so I ran it through ChatGPT to get a quick explanation. The model suggested it was building weights from Pascal’s Triangle. A bit later the client sent me the original TradingView post, which confirmed the script was using John Ehlers’s Gaussian filter to build a channel—similar in spirit to Keltner or Bollinger bands.
Once Ehlers’s name popped up, the next stop was his resource-rich site (mesasoftware.com/TechnicalArticles) for the theory behind the filter. I also searched for a ready-made EasyLanguage version but came up empty. With ChatGPT’s help I decided to roll my own; after all, knocking out support code like this is exactly what these AI tools are for.
What do Carl Friedrich Gauss, Blaise Pascal, and the markets have in common.
You’ve probably bumped into the bell curve in school—maybe in a stats class, maybe when teachers “graded on a curve.” Mathematicians call it by a few interchangeable names:
- Normal distribution (stats class)
- Gaussian curve (named after Carl Friedrich Gauss)
- Binomial curve (because it pops out of Pascal’s Triangle)
No matter the label, it’s the same smooth hump that says, “most values cluster in the middle, very few at the extremes.” Gauss formalized the formula, Pascal’s Triangle supplies the ready‑made integer weights, and traders borrow both ideas to build filters that tame noisy price charts.
Big picture: Gauss gives us the shape of the curve, Pascal gives us the exact numbers to approximate it, and that combo lets us create a market indicator that reacts quickly and stays smooth.
How does this help build an indicator?
The word channel is in the name of the indicator, so it was highly likely we are dealing with a smoothed price with an upper and lower band a certain distance from the smoothed price. If you feed this into Chat GPT and ask for it in EasyLanguage, it will create an indicator using a bunch of arrays. See Chat GPT isn’t 100% knowledgeable of EasyLanguage like it is with python. It didn’t understand the concept of EasyLanguage’s serialized variables. You know where you can refer to a prior value of a variable – myValue[1] or myValue[2]. Chat tries to replicate this with the usage of arrays which gets you into a bunch of trouble right off the bat. Let’s discuss this a little later.
The Mechanics of smoothing price with Pascal’s Triangle, or Gaussian Kernal or Binomial Coefficients.
Stack those rows, keep going, and you build Pascal’s Triangle—each number is the sum of the two numbers just above it.
Look at the 7th row of Pascal’s Triangle:
1 6 15 20 15 6 1
Normalize those numbers (divide by their sum), and you obtain a discrete approximation of a Gaussian kernel. Big Deal, right? You don’t need to know the math behind this, just know that each row in Pascal’s triangle is symmetric. Each row starts are one and ends at one. You can use these coefficients to weight each value across a period of time. Do you mean all this math stuff is akin to a weighted moving average.
Idea | Weighted Moving Average | Binomial / Gaussian weights | Why they feel similar |
---|---|---|---|
What it does | Averages recent prices, but gives newer bars bigger weights (e.g., 1-2-3-4). | Averages recent prices using the numbers from Pascal’s Triangle (e.g., 1-4-6-4-1). | Both are just weighted sums of past prices. |
Shape of the weights | Forms a triangle – rises steadily to the newest bar, then drops to zero beyond the window. | Forms a bell – climbs to the centre, then falls off symmetrically. | Triangles and bells are both peaked shapes: the middle matters most, the edges least. |
Normalizing step | Divide by the sum of the weights (e.g., 1+2+3+4 = 10) so they add to 1. | Same: divide by 1+4+6+4+1 = 16 so they add to 1. | After normalizing, each is just a fancy way to say “take a percentage of each bar and add them up.” |
Smoothing power | Good at knocking out single-bar noise, but the straight sides of the triangle let more mid-frequency wiggles through. | Slightly better at suppressing both very fast and mid-speed wiggles, so the line looks cleaner. | Both cut random jitter while trying not to lag too far behind real turns. |
Math connection | A single pass of linear weights. | What you get if you apply a two-point moving average over and over again (each pass builds the next Pascal row). | Re-applying a simple WMA repeatedly evolves into the binomial weights – that’s the family link. |
Which comes first the indicator or the function that feeds the indicator?
If you are working with code and especially with ChatGPT or any other LLM you need a medium where you can quickly program and observe results. The indicator analysis module will give you instant results. and this is where you should start. However, if you look at the TradingView code of the Gaussian Channel you will notice that the smoothing function is called twice, once for the close and once for the true range on each bar. In other words, you are using the same code twice and incorporating this without functions would be redundant. In my first attempt, I created the smoothing function and named it Binomial, and the channels were a magnitude of 10 below the current price. So, all the price bars were scrunched at the very top of the chart. At first if you don’t succeed, try and try and try and try again.
At first ChatGPT kept insisting on arrays because it didn’t realize EasyLanguage can reference earlier bars just by tagging a variable with [n]
. EasyLanguage conveniently hides that bookkeeping, but you have to tell the model so it stops reinventing circular buffers. Once I explained that a local variable—say filt
—already remembers its prior values (filt[1]
, filt[2]
, etc.), the conversation moved forward.
The next hurdle was clarifying that Donovan’s script feeds raw data (Close and TrueRange) into every stage, not the output of the previous stage. ChatGPT was trying to build a true cascade—each pole using the prior pole’s result—whereas Donovan calculates each pole completely independently. After I pointed that out, the model rewrote the logic correctly and even walked me through the difference:
-
Cascaded filter → Pole 2 uses Pole 1’s output, Pole 3 uses Pole 2’s, and so on.
-
Independent poles → Every pole starts over with the raw Close and Range.
That explanation finally squared the circle and let me produce an EasyLanguage version that matches the original TradingView indicator.
“Cascade” = one stage feeding the next
Think of a cascade as a relay race:
-
Stage 1 (“Pole 1”) takes the raw price, smooths it a little, and hands the baton to …
-
Stage 2 (“Pole 2”), which smooths the output of stage 1 a bit more, then passes to …
-
Stage 3, and so on.
After 4-, 6-, or 9-hand-offs the combined shape of all those little smooths matches the full Gaussian bell.
The indicator lets you pick anywhere from two to nine poles to do the heavy lifting on the data-smoothing. And no, we’re not talking about the North and South Poles—or the kind you cast a fishing line from.
So, what is a pole?
In filter speak, a pole is one little “memory stage” inside the math that reaches back to yesterday’s value (or last bar’s value) before deciding today’s output. Stack more poles and you stack more of those memory stages:
-
1 pole → basically a quick-and-dirty exponential average.
-
4 poles → four mini-averages chained together; much smoother, a hair more lag.
-
9 poles → nine stages deep; super-silky curve, but you’ll feel the delay.
Think of each pole as a coffee filter. One filter catches the big grounds, two filters catch the sludge, and by the time you’ve got nine stacked up, you’re practically drinking distilled water. Same beans in, different smoothness out.
You can dial in two extra tweaks:
- Lag compensation – Tell the code to look one step ahead by swapping in a one-bar forecast of price for the raw price. That little nudge pulls the channel forward so it doesn’t trail the market.
- Extra smoothing – Want the line even silkier? Flip the switch and the function just averages the most-recent two filter values. It’s a tiny moving average—jitter drops a notch, lag creeps up by only half a bar.
For illustrative purposes this is how Pole 6 is calculated. I also show a mapping scheme to store Pascal’s triangle into arrays. I put all this code inside a function with the name BinomialFilterN.
There is redundant code here, but I included it to make it readable for most of my EasyLanguage/PowerLanguage programmers. The math is very simple when you break it down. If we choose Pole #6 all we do is:
beta_ = (1 – Cosine(360 / per)) / (Power(1.414, 2 / numPoles) – 1);
alpha = -beta_ + SquareRoot(beta_ * beta_ + 2 * beta);
- 1 x alpha^6 x close
- plus 6 x beta^1 x prior f6[1]
- minus 15 x beta^2 x f6[2]
- plus 20 x beta^3 x f6[3]
- minus 15 x beta^4 x f6[4]
- plus 6 x beta^5 x f6[5]
- minus 1 x beta^6 x f6[6]
EasyLanguage’s trig calls expect degrees, while most other languages want radians. That’s why the code feeds Cosine(360 / per)
—the 360
converts the cycle length into degrees before taking the cosine.
I also lift the constant √2 (1.414…
) by squaring it with Power(1.414, 2)
and use the same Power
routine for roots—for example, the cube root of x
is simply Power(x, 1 / 3)
.
I placed BinomialFilterN
inside a second routine called GaussianChannelFunc
—a classic wrapper.
Why bother with the extra layer?
Reason | What the wrapper does before/after calling BinomialFilterN |
---|---|
Housekeeping | • Converts the user-friendly period (per ) into the α required by the core filter.• Optional one-bar “look-ahead” to cancel lag.• Runs the filter twice (price and TrueRange). |
Packaging | • Builds upper, centre, and lower bands from the two filtered series.• Returns all three numbers through one array argument. |
Extensibility | Tomorrow you can tweak the channel logic—different volatility measure, ATR multiplier, extra smoothing—without touching the filter math. The heavy-duty code stays in BinomialFilterN ; the wrapper simply preps inputs and formats outputs. |
Think of it as a coffee machine:
BinomialFilterN
is the brewing unit—hot water + grounds in, espresso out, and it never changes.GaussianChannelFunc
is the barista: grinds the beans, measures the water, adds milk and foam, then hands you the finished latte. If you want vanilla syrup tomorrow, you ask the barista; you don’t redesign the boiler.
By splitting the work this way, each piece stays focused, easier to test, and simple to extend later.
The wrapper has to hand back three numbers—upper band, centre line, and lower band—yet an EasyLanguage function can formally return only one. The standard workaround is to pass the additional outputs by reference:
That works, but the call quickly turns into a mile-long argument list.
Instead, I bundle those three outputs into a tiny array and pass the array’s address once:
This wasn’t that impressive, but what if your function needed to return five values?
Now onto the indicator and the strategy
From the outside this looks like a quick coding job—but getting here was a series of detours. I let ChatGPT drive and only nudged when it went off-track. Here are the dead-ends we hit before the indicator finally behaved:
- Pine-script blind spot
- ChatGPT didn’t recognise TradingView syntax, so its first translation attempts were gibberish.
- “Mystery math” instead of binomial weights
- After I mentioned Ehlers and Gaussian smoothing, the model invented a dynamic weighting scheme rather than using the fixed Pascal-triangle numbers the original script relies on.
- Arrays everywhere
- It kept insisting on circular buffers because it didn’t realise EasyLanguage variables already remember their own history via
[1]
,[2]
, etc.
- It kept insisting on circular buffers because it didn’t realise EasyLanguage variables already remember their own history via
- Wrong memory reference
- Even after the array issue was fixed, the code updated each pole with raw price / range instead of the pole’s own prior output.
- Unwanted filter cascade
- ChatGPT then tried a true “cascade” (pole 2 fed by pole 1, pole 3 by pole 2). Donovan’s version calculates every pole independently—so we had to unwind that and start over.
- Sign-flip confusion
- It forgot the plus/minus pattern that keeps the Gaussian zero-lagged, producing a line that trailed price by several bars.
Each course-correction tightened the spec until the model finally spit out the straight, hard-coded-coefficients version you see now.
After all that was it worth the time and analysis?
- A stop version where you buy at and sell short at the upper and lower levels worked best. Liquidating at the midlevel on a stop was also incorporated.
- Using a large profit objective and a relatively small stop loss seemed to work best.
- Intermediate period length and utilizing 8 poles produced the best results.
ELD for TradeStation and Multicharts
Text files of functions, indicator and strategies
Head to Head with Bollinger Bands
Test results across 22 commodities for the past 25 years.
Gaussian Channel: Optimizing the period and ATR multiplier with 8 poles:
Simple Bollinger Band: optimizing moving average length and number of standard deviations
Conclusion (fight-card style)
Decision on the first bout:
The Rolling heavy-hitter—Bollinger Bands—lands the cleaner power shots and takes the scorecards in our 22-commodity test.But don’t call it a knockout just yet.
The Recursive counter-puncher—the Gaussian Channel—fights with an extra weapon: pole count. Adjusting those poles changes how tightly the centre line hugs price, and we’ve only sparred with one setting.Next round:
Tune the poles, test different time-frames, and pit the fighters on equities and FX. The smarter, jabbing Gaussian might steal the rematch once its footwork is dialed in.