
私の親友のランは長い間この指標を観察していて、元旦前に私にそれを勧め、定量的な形に変換できるかどうか議論しました。 残念ながら、私は先延ばし癖があり、今まで彼の願いを叶える手助けをしてきませんでした。実際、アルゴリズムに対する私の理解は最近大きく進歩しました。 いつか松の翻訳機を書こうと思っています。すべてを Python にすることができます。 。 さて、これ以上前置きせずに、この伝説的なスーパートレンドラインを紹介しましょう。 。
CMC Markets の新世代インテリジェント取引システム - Supertrend
このシステムを紹介する記事はこちらです。

CMC Markets の新世代インテリジェント取引システムでは、テクニカル指標で「スーパートレンドライン」を選択して呼び出します。 図のように、上昇信号と下降信号の「色と太さ」を好みに応じて調整できます。 では、スーパートレンド指標とは何でしょうか?スーパートレンドインジケーターの計算式を理解する前に、ATR を理解する必要があります。スーパートレンドは ATR 値を使用してインジケーターの値を計算するためです。
主なアルゴリズムは下の図でも紹介されています。

ざっと見てみると、主な説明はHL2(Kラインの平均価格)にATRのn倍を乗じたチャネルです。トレンドを突破する。
しかし、この記事はかなり短いです。詳細なアルゴリズムはありません。そこで、最高のコミュニティであるTradingviewを思いつきました。
驚くことではありません。確かに、そこにあります。

グラフから判断すると、傾向とかなり一致しています。しかし残念ながら、それは単なる警告信号です。
コードはそれほど長くはないので、翻訳して試してみましょう。 ! (っ•̀ω•́)っ✎⁾⁾!
完全な Pine コードは上記のとおりです。 。
ここではFMZで新しい戦略を作成し、SuperTradeと名付けます。

次に、2つのパラメータFactorとPdを設定します。

コードの操作をより簡素化し、理解しやすくするために、Pythonの高度なデータ拡張パッケージを使用する必要があります。pandas
昼食時に、私は孟孟先生に、FMZがこのライブラリをサポートしているかどうか尋ねました。午後に確認したところ、実際に動作しました。 孟孟先生は本当にすごいです。
1.パンダライブラリのtimeライブラリをインポートする必要があります 2. メイン機能(主にOKEXを実行)で四半期契約を設定する 3. 15 分ごとに 1 回テストするループ doTicker() を設定します。 15分周期でコードを実行する 次に、doTicker() にメイン戦略を記述します。
import pandas as pd
import time
def main():
exchange.SetContractType("quarter")
preTime = 0
Log(exchange.GetAccount())
while True:
records = exchange.GetRecords(PERIOD_M15)
if records and records[-2].Time > preTime:
preTime = records[-2].Time
doTicker(records[:-1])
Sleep(1000 *60)
4. KラインのOHCLVを取得する必要があるので、GetRecords()を使用します。 5. 取得したデータをpandasにインポートします。M15 = pd.DataFrame(records) 6. テーブルヘッダータグを変更する必要があります。 M15.列 =[‘time’,‘open’,‘high’,‘low’,‘close’,‘volume’,‘OpenInterest’] 実際には、「open」、「high」、「low」、「close」の最初の文字を小文字に変更するだけで、後で大文字と小文字を交互に使用せずにコードを記述しやすくなります。
def doTicker(records):
M15 = pd.DataFrame(records)
M15.columns = ['time','open','high','low','close','volume','OpenInterest']
7.データセットに列hl2を追加します。hl2=(high+low)/2
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
8. 次にATRを計算してみましょう ATRの計算には変数の長さのインポートが必要なので、その値はPdです。
次に、Mai 言語マニュアルを参照すると、ATR 真ボラティリティ平均のアルゴリズム手順は次のようになります。 TR : MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW)); ATR : RMA(TR,N)
TR 値は、次の 3 つの差の中で最大になります。 1. 現在の取引日の最高値と最低値の間の変動 HIGH-LOW 2. 前取引日の終値と当日の最高値の間の変動(REF(CLOSE,1)-HIGH) 3. 前取引日の終値と当日の最安値の変動(REF(CLOSE,1)-LOW) したがって、TR: MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
Pythonの計算では
M15['prev_close']=M15['close'].shift(1)
まず、前の行のcloseのデータを取得するためにprev_closeを設定します。つまり、closeを1グリッド右に移動して新しいパラメータを形成します。
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
次に、TRの3つの比較値の配列を記録するための中間変数を定義します。 (高値-安値)(高値-前日終値)(安値-前日終値)
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
データ セットに TR という新しい列を定義します。TR の値は中間変数の最大絶対値です。abs() 関数と max() 関数を使用します。
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()
最後に、ATR の値、ATR: RMA (TR, N) を計算する必要があります。RMA アルゴリズムは、実際には EMA アルゴリズムの固定値バリアントであることがわかります。 N はインポートした変数で、ATR のデフォルト パラメーターは 14 です。ここでは、長さの逆数である alpha をインポートします。
===
次にEWMアルゴリズムを使用してEMAを計算します。 ATRの計算プロセスは次のとおりです。
#ATR(PD)
length=Pd
M15['prev_close']=M15['close'].shift(1)
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()
9 UpとDnの計算を開始する
M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])
Up=hl2 -(Factor * atr) Dn=hl2 +(Factor * atr) 簡単じゃないですか?
以下はTVの15行目から21行目のコアコードセグメントです。
TrendUp=close[1]>TrendUp[1]? max(Up,TrendUp[1]) : Up
TrendDown=close[1]<TrendDown[1]? min(Dn,TrendDown[1]) : Dn
Trend = close > TrendDown[1] ? 1: close< TrendUp[1]? -1: nz(Trend[1],1)
Tsl = Trend==1? TrendUp: TrendDown
linecolor = Trend == 1 ? green : red
この段落の主な目的は、 強気相場の場合、(下線)TrendUp = max(Up,TrendUp[1]) 下降段階の場合、(上線)TrendDown=min(Dn,TrendDown[1]) つまり、トレンドでは、ATR 値はバンディット ボリンジャー戦略に似た手法を使用しています。 チャネルの反対側を狭め続ける
ここで、TrendUp と TrendDown の各計算は自己反復する必要があります。 つまり、各ステップは前のステップに基づいて計算する必要があります。 したがって、データセットをループする必要があります。
ここでは、まずデータ セットの新しいフィールド TrendUp、TrendDown、Trend、および linecolor を作成する必要があります。そして初期値を与える 次に、fillna(0)構文を使用して、以前に計算された結果のデータを0で埋めます。
M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)
forループを開始する ループ内での Python の三項演算の使用
for x in range(len(M15)):
トレンドアップの計算 TrendUp = MAX(Up,TrendUp[-1]) if close[-1]>TrendUp[-1] else Up 一般的な意味は、前回の終値 > 前回のトレンドアップの場合、それが真であれば、アップと前回のトレンドアップの最大値を取得し、それが真でない場合は、アップ値を取得して現在のトレンドアップに渡すことです。
M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
同様にTrendDownを計算する TrendDown=min(Dn,TrendDown[-1]) if close[-1]
M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
以下は制御方向を計算するためのフラグです。擬似コードを簡略化しました Trend= 1 if (close > TrendDown[-1]) else (x) x = -1 if (close< TrendUp[-1]) else Trend[-1]
意味は、終値が前回のトレンドダウンよりも大きい場合は1(強気)を取り、そうでない場合はxを取るということです。 終値が前回のトレンドアップより低い場合は、-1(ショート)を取得します。そうでない場合は、前回のトレンドを取得します(つまり、変更なしです)。 これを図式的に表現すると、上側のレールを突破するとフラグが強気になり、下側のレールを突破するとフラグが弱気になり、その他の時間は変化しないことを意味します。
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
TslとLinecolorを計算する Tsl= rendUp if (Trend==1) else TrendDown Tsl はグラフ上で SuperTrend の値を表すために使用されます。つまり、強気の場合はチャートの下側のトラックをマークし、弱気の場合はチャートの上側のトラックをマークします。 linecolor= ‘green’ if (Trend==1) else ‘red’ ラインカラーの意味は、強気の場合は緑の線をマークし、弱気の場合は空の色をマークします(主にTradingviewの表示に使用されます)
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red'
次の 23 行目から 30 行目は主にプロット用であり、ここでは詳しく説明しません。
最後に、シグナルコントロールの売買のための2行のコードがあります Tradingviewでは、フラグを反転した後にシグナルを出すことを意味します。 条件文を Python に変換します。 前回のトレンドフラグが-1から1に変わった場合、それは上値抵抗が破られたことを意味します。ロングポジションを開く 以前のトレンドフラグが 1 から -1 に変わった場合、下方サポートが破られたことを意味します。ショートポジションを開きます。
if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
Log('SuperTrend V.1 Alert Long',"Create Order Buy)
if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long',"Create Order Sell)
このセクションの完全なコードは次のとおりです。
M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)
for x in range(len(M15)):
M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red'
if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
Log('SuperTrend V.1 Alert Long',"Create Order Buy)
Log('Tsl=',Tsl)
if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long',"Create Order Sell)
Log('Tsl=',Tsl)


全体的なコード構造を調整しました。 そして、ロングとショート関連の注文指示を戦略に統合します。 完全なコードはこちら
'''backtest
start: 2019-05-01 00:00:00
end: 2020-04-21 00:00:00
period: 15m
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
'''
import pandas as pd
import time
def main():
exchange.SetContractType("quarter")
preTime = 0
Log(exchange.GetAccount())
while True:
records = exchange.GetRecords(PERIOD_M15)
if records and records[-2].Time > preTime:
preTime = records[-2].Time
doTicker(records[:-1])
Sleep(1000 *60)
def doTicker(records):
#Log('onTick',exchange.GetTicker())
M15 = pd.DataFrame(records)
#Factor=3
#Pd=7
M15.columns = ['time','open','high','low','close','volume','OpenInterest']
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
#ATR(PD)
length=Pd
M15['prev_close']=M15['close'].shift(1)
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()
M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])
M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)
for x in range(len(M15)):
M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'Long' if ( M15['Trend'].values[x]==1) else 'Short'
linecolor=M15['linecolor'].values[-2]
close=M15['close'].values[-2]
Tsl=M15['Tsl'].values[-2]
if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
Log('SuperTrend V.1 Alert Long','Create Order Buy')
Log('Tsl=',Tsl)
position = exchange.GetPosition()
if len(position) > 0:
Amount=position[0]["Amount"]
exchange.SetDirection("closesell")
exchange.Buy(_C(exchange.GetTicker).Sell*1.01, Amount);
exchange.SetDirection("buy")
exchange.Buy(_C(exchange.GetTicker).Sell*1.01, vol);
if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long','Create Order Sell')
Log('Tsl=',Tsl)
position = exchange.GetPosition()
if len(position) > 0:
Amount=position[0]["Amount"]
exchange.SetDirection("closebuy")
exchange.Sell(_C(exchange.GetTicker).Buy*0.99,Amount);
exchange.SetDirection("sell")
exchange.Sell(_C(exchange.GetTicker).Buy*0.99, vol*2);
公開戦略リンク: https://www.fmz.com/strategy/200625
バックテストのために過去 1 年間のデータを選択しました。 15 分周期の OKEX 四半期契約を使用します。 設定されたパラメータは、 Factor=3 Pd=45 vol=100 (1回の注文につき100枚のチケット) 年率換算の利回りは約33%です。 一般的に、リトレースメントはそれほど大きくありません。 その主な理由は、システムに大きな影響を与えた 312 クラッシュです。 312 がなければ、リターンはもっと良くなるでしょう。

SuperTrendは非常に優れた取引システムです
SuperTrendシステムの主な原則は、ATRチャネルブレイクアウト戦略(ケントチャネルに類似)を使用することです。 しかし、主な変更点は、バンディット・ボリンジャー・ナローイング戦略、または逆ドンチャン原理の使用にあります。 市場操作中、上部チャネルと下部チャネルは常に狭くなっています。 チャネル突破と旋回動作を実現するため。 (チャネルが突破されると、上部レールと下部レールは初期値に戻ります)
私はTrendUp TrendDnをTradingViewで別々にプロットしました
これにより、この戦略をよりよく理解できるようになります
一目でわかる

github には js バージョンもあります。 js はよく分かりませんが、if 文から判断すると、何か問題があるようです。 住所はhttps://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js
ついにオリジナル版を見つけました。 2013.05.29に公開されました 著者:Rajandran R C++ コードが Mt4 フォーラムに公開されましたhttps://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4 C++ の意味は大体理解しているので、機会があれば書き直すつもりです。
皆さんもそこから本質を学んでいただければと思います。 それは難しい。 ~!