Encoding Candlestick Patterns (Part 2): Modeling Price Action as an Ordered Sequence
Introduction
A price pattern is fundamentally an ordered sequence of individual candlesticks. Since each candlestick can be classified into a finite set of distinct types—using the encoding scheme introduced in Part 1—the problem of enumerating every possible pattern becomes a combinatorial one. The mathematical concept that governs such ordered arrangements is the permutation. Permutations allow us to systematically generate and count all possible sequences that can be formed from a given set of candlestick types, without repetition (or with repetition, depending on the encoding rules).
In the previous part of this series, we assigned specific alphabetic labels to different candlestick types. For bullish candles, we used the uppercase set "AHEGD", where each letter stands for a predefined candlestick pattern (e.g., A = bullish marubozu, H = bullish pin bar, G = bullish spinning top, etc.). For bearish candles, we used the lowercase set "ahegd" (e.g., a = bearish marubozu, h = bearish pin bar, g = bearish spinning top, etc.). This alphabet encoding transforms raw price action into a compact symbolic representation.
We then built a function called CandleType() that analyzes each candlestick's open, high, low, and close prices, and returns the corresponding alphabetic code. After processing a sequence of candlesticks, we stored the resulting ordered string of letters in two separate function: bullishPattern and bearishPattern, which are used for detecting specific trading signals.
Objective
In this installment, our goal is to develop a permutation calculator in MQL5. This tool will compute the total expected number of unique patterns that can be generated from a given input set, such as "AHEGD" (bullish set) or "ahegd" (bearish set). For example, if we take a subset of three letters from the five available, how many distinct ordered sequences (permutations) can we form? The calculator will answer such questions, accounting for whether repetition is allowed (e.g., the same candlestick type can appear consecutively) or not allowed (each type used at most once).
Additionally, we will construct a function that generates all ordered sequences (permutations) for a given set of candlestick types. This function will produce an array of strings, each representing a valid pattern sequence. This exhaustive list enables data-driven analysis. Traders can compare real-time sequences with all theoretical patterns and identify those most associated with specific outcomes (eg, continuation or reversal).
The Concept of Permutation
In mathematics, a permutation is any arrangement of a collection of items in a particular order. When we treat each encoded candlestick type as an element drawn from a predefined alphabet, the construction of candlestick patterns translates into forming strings of these elements. A permutation-based approach systematically generates and tests every ordered sequence, rather than focusing on a limited set of traditional patterns. This exhaustive perspective ensures that no statistically meaningful combination is overlooked simply because it lacks a conventional name.
- Counting Patterns: Permutations Without Repetition
In its strictest form, the number of ways to arrange r distinct elements selected from a set of n distinct elements, where each element is used at most once and the order matters, is given by the well-known formula for permutations without repetition:

Where:
- n = total number of distinct candlestick types in the encoding alphabet.
- r = the length of the pattern (the number of candles being arranged), with 0≤r≤n.
- ! (factorial) = the product of all positive integers up to that number (e.g., 5!=5×4×3×2×1=120).
- P(n, r) = the total number of unique ordered arrangements possible under the no-repetition constraint.
For example, if our alphabet contains 7 distinct candlestick codes and we wish to examine all 2-candle patterns consisting of different candle types, there would be P(7,2)=7!/5!=7×6=42 such arrangements. This formula is foundational because it captures the essence of ordered selection without replacement—a scenario that can apply when a trader specifically wants to study patterns in which no two consecutive candles belong to the same encoded category.
To visualize this concept, consider a bullish set defined as AGH. The number of ways to arrange all three elements is calculated as P(n=3,r=3)=6. Figure 1 illustrates all possible permutations.

Figure 1: All possible permutations for a 3-bullish set
Each permutation represents a different price action narrative. By enumerating all permutations, you can back-test every possible ordered sequence of a given set of candlestick types, removing human bias and identifying which sequences statistically lead to profitable trades.
- The More General Case: Permutations With Repetition
In real-market analysis, however, the same candlestick type frequently appears multiple times within a pattern (for instance, two consecutive bearish marubozu candles). Therefore, the more commonly applicable counting principle is that of permutations with repetition. When repetition is allowed, the number of possible patterns of length r drawn from a set of n elements is simply:

Using the same 7-letter alphabet, the number of all 2-candle patterns (allowing repetition) becomes 7^2=49, and for 3-candle patterns it explodes to 7^3=343. This exponential growth reflects the remarkably large combinatorial space that traditional, intuition-based pattern recognition ignores. By generating the full set of n^r sequences and testing each against historical data, a trader can uncover high-probability setups that have never been documented in classical technical analysis literature.
To facilitate a visual understanding of this concept, consider the bullish set AGH. The number of ways to arrange three members, allowing for repetition, is calculated as 3^3=27 (where n=3,r=3). Figure 2 illustrates six specific permutations out of the total twenty-seven possible arrangements. 
Figure 2: Permutation with repetition allowed
It is important to note that bullish candlesticks were utilized for the sake of simplicity. The number of possible permutations is extensive and contingent upon the specific composition of the element set.
For permutations without repetition, the number of selected elements r cannot exceed the total number of elements n (r≤n). Conversely, for permutations with repetition, "r may be any positive integer ( r>0 )."
Table 1 provides a summary of the permutation counts, assuming a set size of n=5.
| r | Without repetition : n!/(n-r)! | Repetition allowed : n^r |
|---|---|---|
| 1 | 5 | 5 |
| 2 | 20 | 25 |
| 3 | 60 | 125 |
| 4 | 120 | 625 |
| 5 | 120 | 3125 |
| 6 | undefined (r > n) | 15625 |
Table 1 shows permutation counts for different pattern lengths (r) with n = 5. As symbol count and sequence length increase, the number of arrangements grows rapidly. This makes the manual review impractical.
Demonstration: Permutations Calculation
We developed a permutation calculator, a script that accepts user input to compute the total number of possible arrangements for a given set size (n) and pattern size (r). The program implements functions for both permutations with repetition and permutations without repetition, since each follows different computational approach. A factorial function was also included to support calculations for permutations without repetition.
To improve efficiency, the PermNoRep function uses the simplified permutation formula: P(n,r)=n×(n−1)×(n−2)×⋯×(n−r+1).
This method multiplies r consecutive integers in descending order from n, avoiding unnecessary factorial calculations.
For example: 
Thus, the number of possible arrangements of 2 elements selected from 7 elements is 42.
Before demonstrating how the permCalculator works, let us first examine the structure and logic of the code.
This section of the code computes the number of possible arrangements without repetition. The function accepts two inputs: the total number of elements (n) and the number of patterns (r). If r<0 or r>n, the function returns 0 because such arrangements are not possible. Otherwise, it calculates and returns the permutation result using the permutation formula without repetition.
//+------------------------------------------------------------------+ //| Permutations without repetition: n!/(n-r)! | //+------------------------------------------------------------------+ long PermNoRep(int n, int r) { if(r < 0 || r > n) return 0; long res = 1; for(int i = 0; i < r; i++) res *= (n - i); return res; }
Similarly, the PermWithRep function accepts two input arguments: the total number of elements (n) and the number of patterns (r). If r<0, the function returns 0 because a negative pattern size is invalid. Otherwise, the function computes and returns the total number of possible arrangements using the permutation formula with repetition.
//+------------------------------------------------------------------+ //| Permutations with repetition: n^r | //+------------------------------------------------------------------+ long PermWithRep(int n, int r) { if(r < 0) return 0; /* if (r == 0) return 1; long res = 1; for (int i = 0; i < r; i++) res *= n; return res; */ return (long)MathPow(n, r); }
The factorial function accepts the total number of elements (n) as input and returns the factorial result. If n≤1, the function returns 1, since the factorial of 0 and 1 is defined as 1. Otherwise, the function computes the product of all positive integers from 1 to n.
//+------------------------------------------------------------------+ //| Factorial function: n! | //+------------------------------------------------------------------+ long Factorial(int n) { if(n <= 1) return 1; long res = 1; for(int i = 2; i <= n; i++) res *= i; return res; }
The code allows users to provide input values for (n) and (r), which are then passed into the appropriate function to compute and return the permutation results.
//--- input parameters input int N=5; // Total number of elements input int R=2; // Number of selections
The final results are displayed using the MessageBox function, which presents the computed values for both permutations with repetition and permutations without repetition. The StringFormat function is used to format and organize the output message for better readability in MQL5.
MessageBox(StringFormat( "Total Permutation for: ( n=%d, r=%d )\r\n\tWithout repetition: %d\r\n\tWith repetition : %d", N, R, PermNoRep(N, R), PermWithRep(N, R) ), "Result");
Figure 3 below shows how the script file works.

Figure 3: PermCalculator demonstration
With the help of the permCalculator, users can estimate the number of possible sets and patterns that can be generated from a given selection. This provides insight into the range of possible arrangements to explore. As demonstrated, increasing the size of the set and the number of patterns causes the number of possible arrangements to grow rapidly, creating a larger search space for identifying effective patterns across currencies, commodities, stocks, and indices.
Constructing Mql5 Script for Permutation Generation
In Part 1 of this series, we used preprocessed permutation lists for signal detection. As illustrated in Figures 1 and 2, several possible candlestick combinations were generated from the bullish set. However, an important question remains: how can these permutations be generated directly in MQL5 for signal detection and statistical analysis?
While the permCalculator estimates the total number of possible arrangements, it does not generate the actual permutation lists required for analysis. To address this limitation, we developed an additional MQL5 script dedicated to generating all possible combinations automatically.
In this section, we implement permutation generation in MQL5 using iterative and recursive algorithms. By the end of this part, you will have a practical utility for enumerating candlestick patterns, enabling your trading system to perform quantitative, pattern-based analysis across currencies, commodities, stocks, and indices.
In our discussion, we established that permutation analysis can be performed using either the strict permutation approach without repetition or the more general approach that allows repetition. Both methods are useful depending on the type of market analysis being conducted. In this section, we examine the pseudo code and MQL5 implementation for each approach.
- Permutation Without Repetition
When permutation without repetition is selected, each element can appear only once in a generated arrangement. This approach is suitable when duplicate candlestick patterns are not allowed within the same sequence. Figure 4 presents the pseudo code used to implement this logic.
K Permutation Pseudo Code

Figure 4: Pseudo code for permutation without repetition
When called, GenerateKPermutation initializes the following:
- n: length of the input string
- results: array to store all valid permutations
- current: temporary array for the ongoing arrangement
- used: boolean array to track which elements have been processed
It then invokes a recursive backtrackfunction. The used array ensures each element is considered only once per permutation path. Whenever the length of current reaches the desired pattern size (k), the current arrangement is appended to the results, and the function returns. After all elements have been processed, GenerateKPermutation outputs the complete set of permutations.
Below, we present the MQL5 implementation for permutation generation without repetition.
We developed a recursive function named PermuteBacktrack with seven parameters. The parameters pos, current, elements, n, and k are passed by value, while used and results are passed by reference to allow updates during recursion. A base condition, pos == k, is defined to terminate the recursive process once the required permutation length is reached.
The function iterates through all possible elements in the set while ensuring that no element is repeated within the same permutation path. During each iteration, a character is extracted from the set and appended to the current sequence. The updated sequence is stored in the variable next, which is then passed recursively into the backtracking function until the base condition is satisfied. This process systematically generates all valid permutations without repetition.
//+------------------------------------------------------------------+ //| permutation without repetition (recursive backtracking) | //+------------------------------------------------------------------+ void PermuteBacktrack(int pos, string current, bool &used[], string &results[], string elements, int n, int k) { //--- Base condition if(pos == k) { int size = ArraySize(results); ArrayResize(results, size + 1); results[size] = current; return; } //--- Try every unused element for(int i = 0; i < n; i++) { if(!used[i]) { used[i] = true; //-- append character string next = current + CharToString(StringGetCharacter(elements, i)); //-- recursive call PermuteBacktrack(pos + 1, next, used, results, elements, n, k); //-- backtrack used[i] = false; } } }
The GetKPermutations function accepts three input arguments: elements, k, and results. The function first determines the total number of elements, denoted as n, from the provided set.
The implementation is designed such that when k <= 0, the value of k is automatically set to the full length of the element set. Conversely, if k > n, the function returns an empty result and displays an "undefined" alert since such permutations are not possible.
Before generating permutations, the function clears any previous results and initializes the required parameters. It then invokes the PermuteBacktrack function, starting with pos = 0 and an empty current string to recursively build all valid permutations without repetition.
//+------------------------------------------------------------------+ //| Construct list of permutations without repetition | //+------------------------------------------------------------------+ void GetKPermutations(string elements, int k, string &results[]) { int n = StringLen(elements); //-- default to full length if(k <= 0) k = n; //-- invalid case if(k > n) { ArrayResize(results, 0); Alert("Undefined! k>n"); return; } ArrayResize(results, 0); // clear previous results bool used[]; ArrayResize(used, n); for(int i = 0; i < n; i++) used[i] = false; PermuteBacktrack(0, "", used, results, elements, n, k); }
- Permutation With Repetition
When a trader requires all possible arrangements allowing repetition (i.e., each element can appear multiple times), the PermWithRepetition function is used. Figure 5 presents the corresponding pseudocode.
Repetition Permutation Pseudo Code

Figure 5: Pseudo code for permutation with repetition
Upon calling PermWithRepetition, the function initializes:
- n = length of the source string
- results array – stores all generated permutations
- current array – holds the ongoing arrangement
A recursive backtrack function is then invoked. The recursion continues until the length of current equals the desired pattern size (r). At that point, the current arrangement is appended to the results and the function returns. Once all possible combinations have been explored (i.e., every element in the set has been tested at each position), the function outputs the complete results array.
Below, we present the MQL5 implementation for permutation generation with repetition.We developed a recursive function named BacktrackRepetition, which accepts six arguments. The parameters pos, current, elements, n, and r are passed by value, while results is passed by reference to store the generated permutations. A base condition, pos == r, is defined to terminate the recursion once the required pattern length has been reached.
The function iterates through all possible elements in the set, allowing elements to be reused in subsequent iterations. During each iteration, a character is extracted from the set and appended to the current sequence. The updated sequence is stored in the variable next, which is recursively passed back into the function until the base condition is satisfied. This process systematically generates all possible permutations with repetition.
//+------------------------------------------------------------------+ //| Recursive backtracking for permutations with repetition | //+------------------------------------------------------------------+ void BacktrackRepetition(int pos, string current, string &results[], string elements, int n, int r) { if(pos == r) { //--- base condition int size = ArraySize(results); ArrayResize(results, size + 1); results[size] = current; return; } //--- try every element for(int i = 0; i < n; i++) { //-- append character string next = current + CharToString(StringGetCharacter(elements, i)); //-- recursive call BacktrackRepetition(pos + 1, next, results, elements, n, r); } }
The PermuteWithRepetition function accepts three input arguments: elements, r, and results. First, the function determines the total number of elements, denoted as n, from the provided set.
The implementation is designed such that when r <= 0, the value of r is automatically set to the full length of the element set. If n = 0, the function returns an empty result since no permutations can be generated from an empty set.
Before generating permutations, the function clears any previous results and initializes the necessary parameters. It then calls the BacktrackRepetition function, starting with pos = 0 and an empty current string to recursively generate all possible permutations with repetition.
//+------------------------------------------------------------------+ //| Construct list of permutations with r repetition | //+------------------------------------------------------------------+ void PermuteWithRepetition(string elements, int r, string &results[]) { int n = StringLen(elements); ArrayResize(results, 0); // clear output if(n == 0) return; if(r <= 0) r=n ; // default to length of string BacktrackRepetition(0, "", results, elements, n, r); }
Storing Generated Permutation
Once the GetKPermutations or PermuteWithRepetition function is executed, the generated results are stored in arrays for further use. To make these permutations easily accessible for other algorithms and trading models, we developed an additional function that exports the results into a text (.txt) file.
The SavePermutationsToFile function accepts three arguments: Cperm1, Cperm2, and filename, where Cperm1 and Cperm2 are passed by reference and the filename is initialized as "patterns.txt."
The function opens the file and verifies that the handle is valid. Once confirmed, it writes the generated permutation data from both arrays in a structured format and other required information into the text file. After the writing process is completed the file is closed, and a MessageBox notification displaying "Results saved to file" is shown to confirm successful execution. The structure of the function is presented below.
//+------------------------------------------------------------------+ //| Write permutation results to TXT file | //+------------------------------------------------------------------+ void SavePermutationsToFile(string &Cperms1[], string &Cperms2[], string filename="patterns.txt") { //--- open/create file int handle = FileOpen(filename, FILE_TXT | FILE_WRITE, '\t'); //--- check if file opened successfully if(handle == INVALID_HANDLE) { Print("Failed to open file: ", filename); return; } //--- header FileWrite(handle, "No.", "|", "BullishPattern", "|", "BearishPattern"); FileWrite(handle, "----------------------------------------------------"); //--- write rows int size = MathMin(ArraySize(Cperms1), ArraySize(Cperms2)); for(int i = 0; i < size; i++) { FileWrite(handle, i+1, "|", Cperms1[i], "|", Cperms2[i]); } //--- close file FileClose(handle); //-- output message MessageBox(StringFormat("Results saved to file: %s", filename), "Prompt"); }
PermGenerator Workflow
When the code is executed from the Scripts section of the MQL5 platform navigation panel, the algorithm first validates whether the bullish and bearish candlestick sets have equal lengths. If the lengths do not match, a message stating "Set must be equal length!" is displayed and the program terminates.
After passing the validation stage, the algorithm determines which permutation method to generate—either permutation without repetition or permutation with repetition. The selected permutation process is then executed, and the generated results are stored in a text file for easy access and integration into other trading or analytical projects.
The overall workflow and implementation structure are presented below.
- enum permType — Defines the two permutation generation modes: single (without repetition) and repeat (with repetition).
- allowPermType — Enables the user to choose the permutation mode through the MQL5 input dialog. The default value, 0, selects the single permutation mode.
- p — Specifies the permutation length or selection size (p), representing the number of elements in each generated pattern. The default value is 2.
- bullCandles = "AHEGD" — Represents the set of bullish candlestick pattern identifiers, where each character corresponds to a unique bullish pattern type.
- bearCandles = "ahegd" — Represents the corresponding bearish candlestick pattern identifiers. Each bearish character maps directly to its bullish counterpart, and the algorithm ensures that both strings have equal lengths before processing.
- perms1 & perms2 — A dynamic array used to store the generated bullish and bearish permutations respectively.
enum permType{single, repeat}; input permType allowPermType = 0; input int p = 2; // number of selections string bullCandles = "AHEGD"; // bullish candles string bearCandles = "ahegd"; // bearish candles string perms1[]; // store bullish patterns string perms2[]; // store bearish patterns
The bullCandles and bearCandles sets, the pattern size (p), and the arrays designated for storing permutations are passed into the GetKPermutations or PermuteWithRepetition functions. Based on the permutation mode selected by the user, the algorithm executes the corresponding generation process. The generated bullish and bearish permutations are then stored in arrays and exported to a text file, making them readily available for use in signal detection, statistical analysis, and other trading algorithms.
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- check for matching length of bull and bear string if(StringLen(bullCandles) != StringLen(bearCandles)) { MessageBox("Set must be equal length!", "Note"); return; } if(allowPermType==0) { GetKPermutations(bullCandles, p, perms1); GetKPermutations(bearCandles, p, perms2); } else { PermuteWithRepetition(bullCandles, p, perms1); PermuteWithRepetition(bearCandles, p, perms2); } //-- save to txt file SavePermutationsToFile(perms1, perms2); }
Demonstration of PermGenerator
This demonstration illustrates how to execute the script file and locate the generated text file within the MQL5 platform after processing is completed. Once the message "Results saved to file" appears, it confirms that the program executed successfully and the permutation data has been stored in the designated text file, ready for use in other applications or trading algorithms.

Figure 6: PermGenerator demo 1

Figure 7: PermGenerator demo 2

Figure 8: PermGenerator demo 3
It is advisable to use the permCalculator before running the PermGenerator to estimate the total number of possible arrangements. This helps users evaluate the size of the search space in advance, as the number of generated permutations can grow exponentially and become computationally expensive for large sets and pattern sizes.
| Files | Purpose |
|---|---|
| PermCalculator.mq5 | Computes number of permutations for a given set and pattern length |
| PermGenerator.mq5 | Generates list of permutations for a given set and pattern length |
Conclusion
In this article, we approached price action as a series of ordered sequences that can be mathematically modeled using permutations. To support this framework, we developed both a permutation calculator and a permutation generator in MQL5. The permutation calculator estimates the total number of possible candlestick arrangements, while the permutation generator creates the complete set of encoded pattern combinations for further analysis.
By representing candlestick types as alphabetic characters, we transformed complex price patterns into structured symbolic sequences that can be efficiently processed, permuted, and tested computationally. This encoding approach allows traders to systematically evaluate which candlestick arrangements perform best across different financial markets, including currencies, commodities, stocks, and indices.
More importantly, this work establishes the foundation for data-driven pattern discovery in algorithmic trading. Instead of relying solely on predefined chart patterns, traders can generate and analyze exhaustive combinations to uncover hidden market behaviors and statistically significant trading opportunities.
In the next part of this series, we will further explore the application of encoded candlestick patterns and demonstrate how these generated permutations can be integrated into signal detection and automated trading strategies. Until then, happy trading.
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Modular Indicator Architecture in MQL5 (Part 1): Stop Copy-Pasting and Start Writing Scalable, Reusable Code
Beyond GARCH (Part IV): Partition Analysis in MQL5
MQL5 Bootstrap (I): Reusable Functions for Working with Positions and Orders
Building Volatility Models in MQL5 (Part III): Implementing the SLSQP Algorithm for Model Estimation
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use