Analysis of Donchian Channel Strategy in the Research Environment
Tags: donchian, fmzquant, trading, analysis
Published: 2024-05-13 08:57:30

### Strategy introduction
Among many trading strategies, the Donchian Channel strategy should be one of the most classic breakthrough strategies. It was famous as early as 1970. At that time, a foreign company conducted simulation testing and research on the mainstream program trading strategies. The results showed that the Donchian Channel strategy was the most successful one in all strategy tests.

Later, the most famous "turtle" trader training in trading history took place in the United States, which was a great success. At that time, the trading methods of the "Turtles" were confidential, but after more than ten years, the "Turtle Trading Rules" was published to the public, people found that the "Turtles" used the improved version of the Donchian Channel strategy.

Breakthrough trading strategy is suitable for trading varieties with relatively smooth trend. The most common breakthrough trading method is to use the relative position relationship between price, support and resistance to judge the specific trading point. The Donchian Channel strategy in this article is also based on this principle.

### Donchian Channel strategy rules
The Donchian Channel is a trend indicator, and its appearance and signal are somewhat similar to those of the Bollinger Band indicator. However, the price channel of Donchian is constructed according to the highest price and lowest price within a certain period. For example, the maximum value of the highest price of the latest 50 K-lines is calculated to form the upper track; Calculate the minimum value of the lowest price of the latest 50 K-lines to form the lower track.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbzf0g5c5akd6xu49sey.png)

As shown in the above figure, this indicator is composed of three curves with different colors. By default, the highest and lowest prices within 20 periods are used to show the volatility of market prices. When the channel is narrow, it means that the market volatility is small. Conversely, when the channel is wide, it means that the market volatility is large.

If the price rises above the upper track, it is a buying signal; On the contrary, if the price falls below the lower track, it is a selling signal. Since the upper and lower tracks are calculated by the highest and lowest prices, generally, prices rarely rise and fall below the upper and lower channel lines at the same time. In most cases, the price moves along the upper or lower tracks unilaterally, or between the upper and lower tracks.

### Strategy logic
There are many ways to use the Donchian Channel, which can be used alone or combined with other indicators. In this lesson, we will use the simplest method. That is, when the price breaks through the upper track from bottom to top, that is, above the pressure line, we believe that the strength of many parties is growing, a wave of rising market has been formed, and the buy open position signal has been generated; When the price falls below the lower track from top to bottom, that is, below the support line, we believe that the short position side is strengthening, a wave of downward trend has been formed, and the sell opening position signal has been generated.

If the price falls back to the middle track of the Donchian Channel after buying to open a position, we think that the multi party force is weakening, or the short position party force is strengthening, and the signal of selling and closing the position is generated; If the price rises back to the middle track of the Donchian channel after the opening of the selling position, we think that the short position side is weakening, or the multi party forces are strengthening, and the buy closing position signal is generated.

**Buying and selling conditions**
- Long opening position: if there is no position and the closing price is higher than the upper track.
- Short opening position: if there is no position and the closing price is less than the lower track.
- Long closing position: if you hold a long position and the closing price is less than the middle track.
- Short closing position: If you hold a short position and the closing price is greater than the middle track.

### Strategy code implementation
Next, we will understand this strategy one by one in the research environment of the FMZ Quant platform:

Enter the research environment of the FMZ Quant platform, as shown below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1cmlkstsab6i169hzcm.png)

Donchian Channel strategy in Python version.ipynb
In [1]:
```
from fmz import *
task = VCtx('''backtest
start: 2019-08-01 09:00:00
end: 2019-10-10 15:00:00
period: 5m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
''')
# Create a Backtesting Environment
# The example format of the backtest information in red above can be obtained by clicking "Save settings" on the strategy edting page of the FMZ Quant platform.
```
In [2]:
```
# First, we need to get the position information, and we define a mp() function to do this.

def mp():
    positions = exchange.GetPosition() # Get position array
    if len(positions) == 0: # If the length of the position array is 0
        return 0 # Prove a short position, return 0
    for i in range(len(positions)): # Iterate through the positions array
        if (positions[i]['Type'] == PD_LONG) or (positions[i]['Type'] == PD_LONG_YD):
            return 1 # If there are long position orders, return 1
        elif (positions[i]['Type'] == PD_SHORT) or (positions[i]['Type'] == PD_SHORT_YD):
            return -1 # If there are short position orders, return -1
        
    print(positions)
    
mp() # Next, we execute this function to get the position information, and we can see that the result is 0, which means that the current position is short.
```
Out[2]:0

In [3]:
```
# Let's start testing this strategy using the current main rebar contract as an example.

exchange.SetContractType("rb888") # Set the variety code, the main contract is the contract code followed by the number 888.
```
Out[3]:
{'CombinationType': 0,
 'CreateDate': 0,
 'DeliveryMonth': 9,
 'DeliveryYear': 0,
 'EndDelivDate': 0,
 'ExchangeID': 'SHFE',
 'ExchangeInstID': 'rb888',
 'ExpireDate': 0,
 'InstLifePhase': 49,
 'InstrumentID': 'rb888',
 'InstrumentName': 'rb continuity',
 'IsTrading': 1,
 'LongMarginRatio': 0.06,
 'MaxLimitOrderVolume': 500,
 'MaxMarginSideAlgorithm': 49,
 'MaxMarketOrderVolume': 30,
 'MinLimitOrderVolume': 1,
 'MinMarketOrderVolume': 1,
 'OpenDate': 0,
 'OptionsType': 48,
 'PositionDateType': 49,
 'PositionType': 50,
 'PriceTick': 1,
 'ProductClass': 49,
 'ProductID': 'rb',
 'ShortMarginRatio': 0.06,
 'StartDelivDate': 0,
 'StrikePrice': 0,
 'UnderlyingInstrID': 'rb',
 'UnderlyingMultiple': 1,
 'VolumeMultiple': 10}

**Next, we get the K-line array, because according to the strategic logic, we need the market to run for a period of time and then make logical judgments, so that our strategic logic can better adapt to the market. Here we will take 50 K-lines as the starting requirement temporarily. The K-line information of FMZ Quant is stored in the form of an array, which contains the highest price, lowest price, opening price, closing price, trading quantity and other information. For the contents of this part, please refer to the official API document of the FMZ Quant platform: https://www.fmz.com/api**

In [4]:
```
# Next we define a variable to store the K-line array.

records = exchange.GetRecords() # Get the K-line array
```
In [5]:
```
# According to the strategy logic description, we use the closing price as the price to open a position, so we need to calculate the closing price of the latest K-line.

close = records[len(records) - 1].Close # Get the latest K-line closing price
close
```
Out[5]:
3846.0

**Then, we need to calculate the maximum value of the highest price and the minimum value of the lowest price in the 50 K-lines by using the closing price as the standard.**

In [6]:
```
upper = TA.Highest(records, 50, 'High') # Get the maximum value of the 50-period maximum price
upper
```
Out[6]:
3903.0

In [7]:
```
lower = TA.Lowest(records, 50, 'Low') # Get the minimum value of the 50-period minimum price
lower
```
Out[7]:
3856.0

**Next, we need to calculate the average value of the upper and lower tracks of this channel.**

In [8]:
```
middle = (upper + lower) / 2 # Calculate the average value of the upper and lower tracks.
middle
```
Out[8]:
3879.5

**Above, we have completed all the calculations required for this strategy. Next, we will start to judge the opening conditions logically and carry out the actual opening position operation according to the results of the logical judgment. It should be noted here that we need to use the domestic commodity futures template of the FMZ Quant platform. Since the current research environment cannot support this template, we will write it temporarily, but the operation will report an error, in the FMZ Quant platform strategy writing page for actual coding, import this template without any problems, the template address is: https://www.fmz.com/strategy/24288. When you code on the FMZ Quant platform strategy editing page, you need to copy this template to your own strategy library first, and then check it off when backtesting.**

In [ ]:
```
obj = ext.NewPositionManager() # When using the FMZ Quant trading class library, errors will be reported at runtime, which can be ignored. Now it is the research environment,
                               # This problem does not occur during the actual coding process, and the following is the same without further comment.
```
**The next step is to determine the logic of the strategy and to open and close positions according to the logic.**

In [ ]:
```
if positions > 0 and close < middle: # If you hold a long position order and the closing price falls below the middle track
            obj.CoverAll() # Close all positions
        if positions < 0 and close > middle: # If you hold a short position order and the closing price rises above the middle track
            obj.CoverAll() # Close all positions
        if positions == 0: # If it's a short position
            if close > upper: # If the closing price rises above the upper track
                obj.OpenLong("rb888", 1) # Buy opening positions
            elif close < lower: # If the closing price falls below the lower track
                obj.OpenShort("rb888", 1) # Sell opening positions
```
In [ ]:
```
# Complete strategy code:
def mp():
    positions = exchange.GetPosition() # Get the position array
    if len(positions) == 0: # If the length of the position array is 0
        return 0 # It proved a short position, return 0
    for i in range(len(positions)): # Iterate through the positions array
        if (positions[i]['Type'] == PD_LONG) or (positions[i]['Type'] == PD_LONG_YD):
            return 1 # If there are long position orders, return 1
        elif (positions[i]['Type'] == PD_SHORT) or (positions[i]['Type'] == PD_SHORT_YD):
            return -1 # If there are short position orders, return -1

def main(): # Main function
    exchange.SetContractType("rb888") # Set the variety code, the main contract is the contract code followed by the number 888
    while True: # Enter the loop
        records = exchange.GetRecords() # Get the K-line array
        if len(records) < 50: continue # If there are less than 50 K-lines, skip the loop
        close = records[len(records) - 1].Close # Get the latest K-line closing price
        positions = mp() # Get position information function
        upper = TA.Highest(records, 50, 'High') # Get the maximum value of the 50-period maximum price
        lower = TA.Lowest(records, 50, 'Low') # Get the minimum value of the 50-period minimum price
        middle = (upper + lower) / 2 # Calculate the average value of the upper and lower tracks
        obj = ext.NewPositionManager() # Use the Trading Library
        if positions > 0 and close < middle: # If you hold a long position order and the closing price falls below the middle track
            obj.CoverAll() # Close all positions
        if positions < 0 and close > middle: # If you hold a short position order and the closing price rises above the middle track
            obj.CoverAll() # Close all positions
        if positions == 0: # If it's a short position
            if close > upper: # If the closing price rises above the upper track
                obj.OpenLong("rb888", 1) # Buy opening positions
            elif close < lower: # If the closing price falls below the lower track
                obj.OpenShort("rb888", 1) # Sell opening positions
```

From: https://blog.mathquant.com/2022/12/26/analysis-of-donchian-channel-strategy-in-the-research-environment.html