タートルズのトレンドフォロー

トレンドフォロー戦略の投資集団タートルズの知見を活かした売買判定プログラム


```TradeClass.cls
Option Base 0
Private Const Multiplier = 1.005
Private Const SpecialMultiplier = 1.03
Private Const MinusMultiplier = 0.995
Private Const MinusSpecialMultiplier = 0.97
Private Const REFERENCEDATE = 1
Private Const OPENPRICE = 2
Private Const HIGHPRICE = 3
Private Const LOWPRICE = 4
Private Const CLOSEPRICE = 5
Private Const VOLUME = 6
Private Const LONGPOSITION = "LONG"
Private Const SHORTPOSITION = "SHORT"


Public Function GetPrice(ByRef DOHLCV_Last As Variant, _
                         ByVal EntryDate As Date, _
                         Optional PriceType = 5) As Double

'引数 DOHLCV_Lastで渡された時系列データがセル範囲か配列のどちらかを判定する。
If TypeName(DOHLCV_Last) = "Range" Then
    tmparray = Range(DOHLCV_Last.Address).Value
ElseIf TypeName(DOHLCV_Last) = "Variant()" Then
    tmparray = DOHLCV_Last
End If

For i = LBound(tmparray, 1) To UBound(tmparray, 1)
    If EntryDate = tmparray(i, REFERENCEDATE) Then
        GetPrice = tmparray(i, PriceType)
        Exit Function
    End If
Next

End Function


Function Counting(ByRef DOHLCV_Last As Variant, ByRef DOHLCV_Previous As Variant) As Long
'#####################################################################################################################################################
'関数  :   Counting
'機能  :   以下によってカウンティング結果を算出する。(値が大きい場合上昇トレンド、値が小さい場合は下落トレンド)
'          また、以下について、大きくない程度の変化率(0.5%)に留まる場合は、カウントは「0」とする。
'          陽線 +1 ⇔ 陰線 -1
'          終値が前日比プラス +1 ⇔ 終値が前日比マイナス -1
'          高値が切り上がっている +1 ⇔ 高値が切り上がっていない -1
'          安値が切り上がっている +1 ⇔ 安値が切り上がっていない -1
'          [特別カウント]
'          大陽線は更に+1 ⇔ 大陰線は更に-1 (ローソク足の実体が大きい ⇒ 当日の終値 - 当日の始値が 3%上昇もしくは下落)
'          窓開け上方+1  ⇔ 窓開け下方-1
'
'参考  :   https://www.youtube.com/watch?v=hl-lc9RO4Ac
'返り値:   カウンティング結果を返す
'#####################################################################################################################################################'
 
Dim CountingOutcome As Long
 
CountingOutcome = 0

 '陽線 +1 ⇔ 陰線 -1
 '[特別カウント] 大陽線は更に+1 ⇔ 大陰線は更に-1
 If DOHLCV_Last(OPENPRICE).Value * Multiplier < DOHLCV_Last(CLOSEPRICE).Value Then
     If DOHLCV_Last(OPENPRICE).Value * SpecialMultiplier < DOHLCV_Last(CLOSEPRICE).Value Then
         CountingOutcome = CountingOutcome + 2
     Else
         CountingOutcome = CountingOutcome + 1
     End If
 ElseIf DOHLCV_Last(OPENPRICE).Value * MinusMultiplier > DOHLCV_Last(CLOSEPRICE).Value Then
     If DOHLCV_Last(OPENPRICE).Value * MinusSpecialMultiplier > DOHLCV_Last(CLOSEPRICE).Value Then
         CountingOutcome = CountingOutcome - 2
     Else
         CountingOutcome = CountingOutcome - 1
     End If
 End If
'Debug.Print ("OPENPRICE: " & DOHLCV_Last(OPENPRICE).Value * Multiplier)
'Debug.Print ("CLOSEPRICE: " & DOHLCV_Last(CLOSEPRICE).Value)
 
 '終値が前日比プラス +1 ⇔ 終値が前日比マイナス -1
 If DOHLCV_Previous(CLOSEPRICE).Value * Multiplier < DOHLCV_Last(CLOSEPRICE).Value Then
     CountingOutcome = CountingOutcome + 1
 ElseIf DOHLCV_Previous(CLOSEPRICE).Value * MinusMultiplier > DOHLCV_Last(CLOSEPRICE).Value Then
     CountingOutcome = CountingOutcome - 1
 End If
 

 '高値が切り上がっている +1 ⇔ 高値が切り上がっていない -1
 If DOHLCV_Previous(HIGHPRICE).Value * Multiplier < DOHLCV_Last(HIGHPRICE).Value Then
     CountingOutcome = CountingOutcome + 1
 ElseIf DOHLCV_Previous(HIGHPRICE).Value * MinusMultiplier > DOHLCV_Last(HIGHPRICE).Value Then
     CountingOutcome = CountingOutcome - 1
 End If
 
 
 '安値が切り上がっている +1 ⇔ 安値が切り上がっていない -1
 If DOHLCV_Previous(LOWPRICE).Value * Multiplier < DOHLCV_Last(LOWPRICE).Value Then
     CountingOutcome = CountingOutcome + 1
 ElseIf DOHLCV_Previous(LOWPRICE).Value * MinusMultiplier > DOHLCV_Last(LOWPRICE).Value Then
     CountingOutcome = CountingOutcome - 1
 End If
 
 
 '[特別カウント]窓開け上方+1  ⇔ 窓開け下方-1
 If DOHLCV_Previous(HIGHPRICE).Value * Multiplier < DOHLCV_Last(LOWPRICE).Value Then
     CountingOutcome = CountingOutcome + 1
 ElseIf DOHLCV_Last(LOWPRICE).Value * MinusMultiplier > DOHLCV_Previous(HIGHPRICE).Value Then
     CountingOutcome = CountingOutcome - 1
 End If
 
 Counting = CountingOutcome
    
End Function

Function CandlestickAnalysis(ByRef DOHLCV_Last As Variant, ByRef HistoricalVolume As Variant) As String
'#####################################################################################################################################################
'関数  :   CandlestickAnalysis
'機能  :   チャートのローソク足の形を判定する。
'引数  :   価格と出来高の時系列、基準日の出来高
'返り値:   文字列
'#####################################################################################################################################################'


Const STRONGSIGN = "Strong"
Const ULTRASIGN = "Ultra"
Const DOJI = "Doji"
Const BULLISHPINBAR = "BullishPinBar"
Const BEARISHPINBAR = "BearishPinBar"
Const BearishPinBarMultiplier = 1.02
Const BullishPinBarMultiplier = 0.98
Const VolumeCngRcgnz = 1.5
Const UltraVolumeCngRcgnz = 3
Dim CandlestickSigns As String
Dim VolumeAVG As Double

VolumeAVG = WorksheetFunction.Average(HistoricalVolume)


If DOHLCV_Last(CLOSEPRICE).Value < DOHLCV_Last(OPENPRICE).Value * Multiplier And _
   DOHLCV_Last(OPENPRICE).Value * MinusMultiplier < DOHLCV_Last(CLOSEPRICE).Value Then
   CandlestickSigns = DOJI

'トンボ(上昇に向かう)
   If DOHLCV_Last(HIGHPRICE).Value < DOHLCV_Last(OPENPRICE).Value * Multiplier And _
          DOHLCV_Last(LOWPRICE).Value < DOHLCV_Last(OPENPRICE).Value * BullishPinBarMultiplier Then
            CandlestickSigns = BULLISHPINBAR
            If VolumeAVG * UltraVolumeCngRcgnz < DOHLCV_Last(VOLUME).Value Then
                CandlestickSigns = ULTRASIGN & BULLISHPINBAR
            ElseIf VolumeAVG * VolumeCngRcgnz < DOHLCV_Last(VOLUME).Value Then
                CandlestickSigns = STRONGSIGN & BULLISHPINBAR
            End If
            
'トウバ(下落に向かう)
   ElseIf DOHLCV_Last(OPENPRICE).Value * BearishPinBarMultiplier < DOHLCV_Last(HIGHPRICE).Value And _
          DOHLCV_Last(OPENPRICE).Value * MinusMultiplier < DOHLCV_Last(LOWPRICE).Value Then
            CandlestickSigns = BEARISHPINBAR
            If VolumeAVG * UltraVolumeCngRcgnz < DOHLCV_Last(VOLUME).Value Then
                CandlestickSigns = ULTRASIGN & BEARISHPINBAR
            ElseIf VolumeAVG * VolumeCngRcgnz < DOHLCV_Last(VOLUME).Value Then
                CandlestickSigns = STRONGSIGN & BEARISHPINBAR
            End If
            
'寄付同時線
   ElseIf DOHLCV_Last(OPENPRICE).Value * BearishPinBarMultiplier < DOHLCV_Last(HIGHPRICE).Value And _
          DOHLCV_Last(OPENPRICE).Value * MinusBearishPinBarMultiplier < DOHLCV_Last(LOWPRICE).Value Then
            CandlestickSigns = STRONGSIGN & DOJI
   End If
   
   
End If

CandlestickAnalysis = CandlestickSigns
End Function



Public Function CalcATR(ByRef DOHLCV_Last As Variant) As Double
'#####################################################################################################################################################
'関数  :   CalcATR (Average True Range)
'機能  :   次の3つを計算し、最大値を返り値として返す。True RangeからAverage True Rangeを求める際の日数はワークシート関数の範囲指定時に決定する。
'          ① (当日高値 - 当日安値)の絶対値
'          ② (当日高値 - 前日終値)の絶対値
'          ③ (当日安値 - 前日終値)の絶対値
'簡略式;   max(当日高値, 前日終値) - min(当日安値, 前日終値)
'引数  :   当日高値、当日の安値、前日終値
'返り値:   機能の記載のとおり
'#####################################################################################################################################################'

Dim i As Long
Dim TR As Double
Dim tmparray As Variant

TR = 0
'セル範囲が渡された場合のATRを計算する
If TypeName(DOHLCV_Last) = "Range" Then
    tmparray = Range(DOHLCV_Last.Address).Value
'Rangeを配列に指定する際に「0」スタートだと指定したセル範囲を超えて配列を取得してしまった
'そのため、For文は「1」スタートとしている。
    For i = 1 To DOHLCV_Last.Rows.Count - 1
        With Application.WorksheetFunction
            TR = TR + .Max(DOHLCV_Last(i, HIGHPRICE), DOHLCV_Last(i + 1, CLOSEPRICE)) _
                        - .Min(DOHLCV_Last(i, LOWPRICE), DOHLCV_Last(i + 1, CLOSEPRICE))
        End With
    Next
    CalcATR = TR / (DOHLCV_Last.Rows.Count - 1)
    
'配列が渡された場合のATRを計算する
ElseIf TypeName(DOHLCV_Last) = "Variant()" Then
    tmparray = DOHLCV_Last
    For i = 0 To UBound(tmparray, 1) - 1
        With Application.WorksheetFunction
            TR = TR + .Max(tmparray(i, HIGHPRICE), tmparray(i + 1, CLOSEPRICE)) _
                        - .Min(tmparray(i, LOWPRICE), tmparray(i + 1, CLOSEPRICE))
        End With
    Next
    CalcATR = TR / (UBound(tmparray, 1) - 1)
End If

End Function



Public Function PositioningUnit(ByRef DOHLCV_Last As Variant, _
                                ByRef Budget As Long, _
                                ByVal RiskTolerance As Double, _
                                Optional UnitMultiplier = 100) As Double
'#####################################################################################################################################################
'関数  :   PositioningUnit (Average True Range)
'機能  :   ユーザーが指定したセル範囲の日数分の過去データのATRを元に、1日あたりの最大損失額に収まるようなポジション数を計算する。
'          投資ポジション = (全体の投資予算 × リスク許容度) / (ATR × 1単元あたりの投資数)
'          ※1単元あたりの必要資金が大きすぎて購入金額が足りない場合は、「0」が返る。
'引数  :   当日高値、当日の安値、前日終値
'返り値:   機能の記載のとおり
'#####################################################################################################################################################'

PositioningUnit = Application.WorksheetFunction.RoundDown((Budget * RiskTolerance) / (Me.CalcATR(DOHLCV_Last) * UnitMultiplier), 1)

End Function

Public Function MovingAverage(ByRef DOHLCV_Last As Variant, _
                              ByVal EntryDate As Date, _
                              ByVal Period As Long) As Double
'#####################################################################################################################################################
'関数  :   MovingAverage
'機能  :   単純移動平均を算出する。
'引数  :   基準日、移動平均を算出の参照日数
'返り値:   機能の記載のとおり
'#####################################################################################################################################################'
Dim tmparray() As Variant
Dim i As Long
Dim j As Long
Dim ElemSum As Double
Dim AveragePeriodEntry As Long
Dim AveragePeriodLast As Long

If Period <= 1 Then
    GoTo ExitFunc
End If

If TypeName(DOHLCV_Last) = "Range" Then
    tmparray = Range(DOHLCV_Last.Address).Value
ElseIf TypeName(DOHLCV_Last) = "Variant()" Then
    tmparray = DOHLCV_Last
End If

For i = LBound(tmparray, 1) To UBound(tmparray, 1)

    'EntryDateに一致する日付のデータセットが存在する場合に効果検証を行う。
    If EntryDate = tmparray(i, REFERENCEDATE) Then
        
        '変数設定・初期化
        ElemSum = 0
        AveragePeriodEntry = i
        AveragePeriodLast = i + Period - 1 '-1 Newest to Oldestのソート順であることが前提
        
        'EntryDateを基準にInvestment Holizonとして設定した期間中の最高値・最安値を取得
        For AverageElem = AveragePeriodEntry To AveragePeriodLast
            ElemSum = ElemSum + tmparray(AverageElem, CLOSEPRICE)
        Next
        
        MovingAverage = ElemSum / Period
        
        Exit Function
    End If
Next

ExitFunc:
    Exit Function
End Function

Public Function CalcEMA(ByRef DOHLCV_Last As Variant, _
                        ByVal EntryDate As Date, _
                        ByRef DaysForAvg As Long) As Double
'#####################################################################################################################################################
'関数  :   CalcEMA()
'機能  :   指数平滑化移動平均を計算する。
'引数  :   価格と出来高の時系列と初期の移動平均値を算出する際の日数を指定する。
'返り値:   価格と出来高の時系列の各レコードに対応する指数平滑化移動平均を返す。
'#####################################################################################################################################################'
Dim SmoothingConstant As Double
Dim AverageTermSum As Double
Dim EMA As Double
Dim tmparray() As Variant
Dim i As Long
Dim j As Long


'引数 DOHLCV_Lastで渡された時系列データがセル範囲か配列のどちらかを判定する。
If TypeName(DOHLCV_Last) = "Range" Then
    '計算対象の時系列よりも初期移動平均値を算出する期間のレコード数が少ない場合は計算しない
    tmparray = Range(DOHLCV_Last.Address).Value
ElseIf TypeName(DOHLCV_Last) = "Variant()" Then
    tmparray = DOHLCV_Last
End If

For i = LBound(tmparray, 1) To UBound(tmparray, 1)

    'EntryDateに一致する日付のデータセットが存在する場合に効果検証を行う。
    If EntryDate = tmparray(i, REFERENCEDATE) Then
        '平滑化定数を求める
        SmoothingConstant = 2 / (DaysForAvg + 1)
        
        '変数初期化
        AverageTermSum = 0

'        For j = UBound(tmparray, 1) To LBound(tmparray, 1) Step -1
        For j = UBound(tmparray, 1) To i Step -1
            'IfとElseIfは単純移動平均部分の算出
            If j > UBound(tmparray, 1) - DaysForAvg + 1 Then
                AverageTermSum = AverageTermSum + tmparray(j, CLOSEPRICE)
            ElseIf j = UBound(tmparray, 1) - DaysForAvg + 1 Then
                AverageTermSum = AverageTermSum + tmparray(j, CLOSEPRICE)
                EMA = AverageTermSum / DaysForAvg
            'Else以下は計算対象期間の2日目以降の計算を実行
            Else
'                EMA = EMA + SmoothingConstant * (tmparray(j, CLOSEPRICE) - tmparray(j + 1, CLOSEPRICE))
                EMA = EMA + (SmoothingConstant * (tmparray(j, CLOSEPRICE) - EMA))
            End If
        Next
        '最新レコードのEMA
        CalcEMA = EMA
        Exit Function
    End If
Next

End Function


Public Function CalcRSI(ByRef DOHLCV_Last As Variant) As Double
'#####################################################################################################################################################
'関数  :   CalcRSI(Relative Strength Index)
'機能  :   Relative Strength Indexを計算する。
'         サイコロジカルラインの計算に価格という要素を加えたイメージ
'          安定上昇 → 66% ,安定下落 → 33% (70%、30%はそれぞれ、買い勢力と売り勢力の利益確定決済が出やすい。
'          ただし、トレンドがある場合はトレンドが加速していく可能性があり、必ずしも決済売買のサインとはならない)
'引数  :   ヒストリカルの価格の時系列
'返り値:   Relative Strength Indexの算出結果をDouble型で返す。
'#####################################################################################################################################################'

Dim i As Long
Dim Today_Prev_GAP As Double
Dim UpperSide As Long
Dim LowerSide As Long
Dim UpperCount As Long
Dim LowerCount As Long
Dim tmparray As Variant
Dim InitialRSI As Double
Dim a As Double
Dim b As Double

'変数の初期化
a = 0
b = 0

tmparray = Range(DOHLCV_Last.Address).Value

'RSI計算起算日のRSIを算出
For i = UBound(tmparray, 1) To 2 Step -1
    Today_Prev_GAP = tmparray(i - 1, CLOSEPRICE) - tmparray(i, CLOSEPRICE)
    If Today_Prev_GAP > 0 Then
        UpperSide = UpperSide + Abs(Today_Prev_GAP)
        UpperCount = UpperCount + 1
    ElseIf Today_Prev_GAP <= 0 Then
        LowerSide = LowerSide + Abs(Today_Prev_GAP)
        LowerCount = LowerCount + 1
    End If
Next
a = UpperSide / UpperCount
b = LowerSide / LowerCount

CalcRSI = a / (a + b) * 100

End Function


Public Function CalcBollingerBand(ByRef HistorlcalPrice As Variant) As Double
'#####################################################################################################################################################
'関数  :   CalcCalcBollingerBand
'機能  :   CalcBollingerBandを計算する。
'引数  :   価格と出来高の時系列
'返り値:   Relative Strength Indexの算出結果をDouble型で返す。
'#####################################################################################################################################################'

End Function


'↓汎用性をもたせるためにヒストリカルデータが渡される際のデータ型が配列か、セル範囲かで挙動を変更したい。
Public Function EdgeRatio(ByRef DOHLCV_Last As Variant, _
                          ByVal LongShort As String, _
                          ByVal EntryDate As Date, _
                          ByVal FwdLookDays As Long) As Variant
'#####################################################################################################################################################
'関数  :   EdgeRatio
'機能  :
'引数  :   価格と出来高の時系列
'          移動平均を算出する基準日
'          短中長期の算出対象の日数 (基本的には短期に5日、中期に20日、長期に40日を使用することが推奨される。)
'返り値:   Relative Strength Indexの算出結果をDouble型で返す。
'#####################################################################################################################################################'

Dim ArrayForATR() As Variant
Dim tmparray() As Variant
Dim i As Long
Dim j As Long
Dim VerifyElem As Long
Dim VerifyPeriodEntry As Long
Dim VerifyPeriodLast As Long
Dim EntryPrice As Double
Dim MFE As Double 'Maximum Favorable Excursion
Dim MAE As Double 'Maximum Adverse Excursion
Dim AdjustedMFE As Double
Dim AdjustedMAE As Double
Dim HighestPrice As Double
Dim LowestPrice As Double
Dim SecondHighestPrice As Double
Dim SecondLowestPrice As Double
Dim PrivateATR As Double

'関数の引数に指定したセル範囲を配列に格納
tmparray = Range(DOHLCV_Last.Address).Value


'EntryDateにおけるClose価格を取得する。


'EntryDateを基準に未来方向に向かってFwdLookDaysの分配列をFor文で進める
'最大値と最小値を更新していく。


'EntryDateが配列の中に存在するかどうかをチェックする処理が必要
'時系列が時間について昇順・降順であるかに関わらず、動作するように配列の順番が時系列順になっていることを確認する実装が必要(?)
'クラス内にプライベートメソッドを用意して共通処理を作成する。
'→ これまで、配列に格納してきたものを全てそのメソッドに変更する

For i = UBound(tmparray, 1) To LBound(tmparray, 1) Step -1

    'EntryDateに一致する日付のデータセットが存在する場合に効果検証を行う。
    If EntryDate = tmparray(i, REFERENCEDATE) Then
'        Stop
        VerifyPeriodEntry = i
        VerifyPeriodLast = i - FwdLookDays ' Newest to Oldestのソート順であることが前提
        
        ReDim Preserve ArrayForATR(FwdLookDays, UBound(tmparray, 2))
        EntryPrice = tmparray(i, CLOSEPRICE)
        HighestPrice = tmparray(i, CLOSEPRICE) '変数初期化
        LowestPrice = tmparray(i, CLOSEPRICE) '変数初期化
        SecondLowestPrice = 1.79769313486231E+308 '変数初期化 Double型の最大値
        
'        Stop
        j = 0
        'EntryDateを基準にInvestment Holizonとして設定した期間中の最高値・最安値を取得
        For VerifyElem = VerifyPeriodEntry To VerifyPeriodLast Step -1
            If HighestPrice < tmparray(VerifyElem, CLOSEPRICE) Then
                HighestPrice = tmparray(VerifyElem, CLOSEPRICE)
            ElseIf LowestPrice > tmparray(VerifyElem, CLOSEPRICE) Then
                LowestPrice = tmparray(VerifyElem, CLOSEPRICE)
            ElseIf SecondHighestPrice < tmparray(VerifyElem, CLOSEPRICE) And tmparray(VerifyElem, CLOSEPRICE) < HighestPrice Then
                SecondHighestPrice = tmparray(VerifyElem, CLOSEPRICE)
            ElseIf LowestPrice < tmparray(VerifyElem, CLOSEPRICE) And tmparray(VerifyElem, CLOSEPRICE) < SecondLowestPrice Then
                SecondLowestPrice = tmparray(VerifyElem, CLOSEPRICE)
            End If
            ArrayForATR(j, REFERENCEDATE) = tmparray(VerifyElem, REFERENCEDATE)
            ArrayForATR(j, OPENPRICE) = tmparray(VerifyElem, OPENPRICE)
            ArrayForATR(j, HIGHPRICE) = tmparray(VerifyElem, HIGHPRICE)
            ArrayForATR(j, LOWPRICE) = tmparray(VerifyElem, LOWPRICE)
            ArrayForATR(j, CLOSEPRICE) = tmparray(VerifyElem, CLOSEPRICE)
            ArrayForATR(j, VOLUME) = tmparray(VerifyElem, VOLUME)
            j = j + 1
        Next
        
        
        If LongShort = LONGPOSITION Then
        
            If HighestPrice <> EntryPrice Then
                MFE = HighestPrice - EntryPrice
            ElseIf HighestPrice = EntryPrice Then '#DIV/0!を防ぐための分岐 (EntryPriceが最高値を想定)
                MFE = EntryPrice - SecondHighestPrice
            End If
            
            If LowestPrice <> EntryPrice Then
                MAE = EntryPrice - LowestPrice
            ElseIf LowestPrice = EntryPrice Then '#DIV/0!を防ぐための分岐 (EntryPriceが最安値を想定)
                MAE = SecondLowestPrice - EntryPrice
            End If
            
        ElseIf LongShort = SHORTPOSITION Then
        
            If LowestPrice <> EntryPrice Then
                MFE = EntryPrice - LowestPrice
            ElseIf LowestPrice = EntryPrice Then '#DIV/0!を防ぐための分岐 (EntryPriceが最安値を想定)
                MFE = SecondLowestPrice - EntryPrice
            End If
                
                
            If HighestPrice <> EntryPrice Then
                MAE = HighestPrice - EntryPrice
            ElseIf HighestPrice = EntryPrice Then '#DIV/0!を防ぐための分岐 (EntryPriceが最高値を想定)
                MAE = EntryPrice - SecondHighestPrice
            End If
                        
        End If
        
        
        'ATRで標準化することで、商品横断的にEdge Ratioを比較できるようにする。
        'ATRの計算を何重にも計算すると非効率であるため､変数に格納させる
        PrivateATR = Me.CalcATR(ArrayForATR)

        AdjustedMFE = MFE / PrivateATR
        'Debug.Print AdjustedMFE
        AdjustedMAE = MAE / PrivateATR
        'Debug.Print AdjustedMAE
        EdgeRatio = AdjustedMFE / AdjustedMAE
    End If
Next

End Function


Public Function MaximumDrawdown() As Double
    
End Function


Public Function MA_Analysis(ByRef DOHLCV_Last As Variant, _
                            ByVal EntryDate As Date, _
                            ByVal MAType As String, _
                            ByVal ShortTerm As Long, _
                            ByVal MidTerm As Long, _
                            ByVal LongTerm As Long) As String
                              
'#####################################################################################################################################################
'関数  :   MACirculation
'機能  :   短期・中期・長期の移動平均の結果の大小関係を確認して、上昇・下落トレンドが発生していることを確認する。
'          上昇・下落トレンドは検知するが、揉み合い相場は検知しないことがこの移動平均大循環分析の良い点
'引数  :   価格と出来高の時系列
'          移動平均を算出する基準日
'          短中長期の算出対象の日数 (基本的には短期に5日、中期に20日、長期に40日を使用することが推奨される。)
'返り値:   Relative Strength Indexの算出結果をDouble型で返す。
'#####################################################################################################################################################'

Const BULL = "Bullish Trend"
Const BEAR = "Bearish Trend"
Const CONFLICT = "Trend conflict"
Const PRAM_ERROR = "Invalid Parameters are specified"
Const SIMPLE_MA = "SMA"
Const EXP_MA = "EMA"
Dim ShortMA As Double
Dim MidMA As Double
Dim LongMA As Double
Dim TargetClose As Double
Dim tmparray As Variant

If MAType = SIMPLE_MA And 0 < ShortTerm And ShortTerm < MidTerm And ShortTerm < LongTerm And MidTerm < LongTerm Then
    tmparray = Range(DOHLCV_Last.Address).Value
    TargetClose = Me.GetPrice(tmparray, EntryDate, CLOSEPRICE)
    ShortMA = Me.MovingAverage(tmparray, EntryDate, ShortTerm)
    MidMA = Me.MovingAverage(tmparray, EntryDate, MidTerm)
    LongMA = Me.MovingAverage(tmparray, EntryDate, LongTerm)
    
ElseIf MAType = EXP_MA And 0 < ShortTerm And ShortTerm < MidTerm And ShortTerm < LongTerm And MidTerm < LongTerm Then
    tmparray = Range(DOHLCV_Last.Address).Value
    TargetClose = Me.GetPrice(tmparray, EntryDate, CLOSEPRICE)
    ShortMA = Me.CalcEMA(tmparray, EntryDate, ShortTerm)
    MidMA = Me.CalcEMA(tmparray, EntryDate, MidTerm)
    LongMA = Me.CalcEMA(tmparray, EntryDate, LongTerm)
Else
    MA_Analysis = PRAM_ERROR
    Exit Function
End If

If LongMA < MidMA And MidMA < ShortMA Then
    MA_Analysis = BULL
ElseIf ShortMA < MidMA And MidMA < LongMA Then
    MA_Analysis = BEAR
Else
    MA_Analysis = CONFLICT
End If
    
End Function



Public Function StrategyBacktest(ByRef DOHLCV_Last As Variant) As Double
    tmparray = Range(DOHLCV_Last.Address).Value
End Function
```