Lean  $LEAN_TAG$
HangingMan.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
18 
20 {
21  /// <summary>
22  /// Hanging Man candlestick pattern indicator
23  /// </summary>
24  /// <remarks>
25  /// Must have:
26  /// - small real body
27  /// - long lower shadow
28  /// - no, or very short, upper shadow
29  /// - body above or near the highs of the previous candle
30  /// The meaning of "short", "long" and "near the highs" is specified with SetCandleSettings;
31  /// The returned value is negative (-1): hanging man is always bearish;
32  /// The user should consider that a hanging man must appear in an uptrend, while this function does not consider it
33  /// </remarks>
35  {
36  private readonly int _bodyShortAveragePeriod;
37  private readonly int _shadowLongAveragePeriod;
38  private readonly int _shadowVeryShortAveragePeriod;
39  private readonly int _nearAveragePeriod;
40 
41  private decimal _bodyShortPeriodTotal;
42  private decimal _shadowLongPeriodTotal;
43  private decimal _shadowVeryShortPeriodTotal;
44  private decimal _nearPeriodTotal;
45 
46  /// <summary>
47  /// Initializes a new instance of the <see cref="HangingMan"/> class using the specified name.
48  /// </summary>
49  /// <param name="name">The name of this indicator</param>
50  public HangingMan(string name)
51  : base(name, Math.Max(Math.Max(Math.Max(CandleSettings.Get(CandleSettingType.BodyShort).AveragePeriod, CandleSettings.Get(CandleSettingType.ShadowLong).AveragePeriod),
52  CandleSettings.Get(CandleSettingType.ShadowVeryShort).AveragePeriod), CandleSettings.Get(CandleSettingType.Near).AveragePeriod) + 1 + 1)
53  {
54  _bodyShortAveragePeriod = CandleSettings.Get(CandleSettingType.BodyLong).AveragePeriod;
55  _shadowLongAveragePeriod = CandleSettings.Get(CandleSettingType.ShadowLong).AveragePeriod;
56  _shadowVeryShortAveragePeriod = CandleSettings.Get(CandleSettingType.ShadowVeryShort).AveragePeriod;
57  _nearAveragePeriod = CandleSettings.Get(CandleSettingType.Near).AveragePeriod;
58  }
59 
60  /// <summary>
61  /// Initializes a new instance of the <see cref="HangingMan"/> class.
62  /// </summary>
63  public HangingMan()
64  : this("HANGINGMAN")
65  {
66  }
67 
68  /// <summary>
69  /// Gets a flag indicating when this indicator is ready and fully initialized
70  /// </summary>
71  public override bool IsReady
72  {
73  get { return Samples >= Period; }
74  }
75 
76  /// <summary>
77  /// Computes the next value of this indicator from the given state
78  /// </summary>
79  /// <param name="window">The window of data held in this indicator</param>
80  /// <param name="input">The input given to the indicator</param>
81  /// <returns>A new value for this indicator</returns>
82  protected override decimal ComputeNextValue(IReadOnlyWindow<IBaseDataBar> window, IBaseDataBar input)
83  {
84  if (!IsReady)
85  {
86  if (Samples >= Period - _bodyShortAveragePeriod)
87  {
88  _bodyShortPeriodTotal += GetCandleRange(CandleSettingType.BodyShort, input);
89  }
90 
91  if (Samples >= Period - _shadowLongAveragePeriod)
92  {
93  _shadowLongPeriodTotal += GetCandleRange(CandleSettingType.ShadowLong, input);
94  }
95 
96  if (Samples >= Period - _shadowVeryShortAveragePeriod)
97  {
98  _shadowVeryShortPeriodTotal += GetCandleRange(CandleSettingType.ShadowVeryShort, input);
99  }
100 
101  if (Samples >= Period - _nearAveragePeriod - 1 && Samples < Period - 1)
102  {
103  _nearPeriodTotal += GetCandleRange(CandleSettingType.Near, input);
104  }
105 
106  return 0m;
107  }
108 
109  decimal value;
110  if (
111  // small rb
112  GetRealBody(input) < GetCandleAverage(CandleSettingType.BodyShort, _bodyShortPeriodTotal, input) &&
113  // long lower shadow
114  GetLowerShadow(input) > GetCandleAverage(CandleSettingType.ShadowLong, _shadowLongPeriodTotal, input) &&
115  // very short upper shadow
116  GetUpperShadow(input) < GetCandleAverage(CandleSettingType.ShadowVeryShort, _shadowVeryShortPeriodTotal, input) &&
117  // rb near the prior candle's highs
118  Math.Min(input.Close, input.Open) >= window[1].High - GetCandleAverage(CandleSettingType.Near, _nearPeriodTotal, window[1])
119  )
120  value = -1m;
121  else
122  value = 0m;
123 
124  // add the current range and subtract the first range: this is done after the pattern recognition
125  // when avgPeriod is not 0, that means "compare with the previous candles" (it excludes the current candle)
126 
127  _bodyShortPeriodTotal += GetCandleRange(CandleSettingType.BodyShort, input) -
128  GetCandleRange(CandleSettingType.BodyShort, window[_bodyShortAveragePeriod]);
129 
130  _shadowLongPeriodTotal += GetCandleRange(CandleSettingType.ShadowLong, input) -
131  GetCandleRange(CandleSettingType.ShadowLong, window[_shadowLongAveragePeriod]);
132 
133  _shadowVeryShortPeriodTotal += GetCandleRange(CandleSettingType.ShadowVeryShort, input) -
134  GetCandleRange(CandleSettingType.ShadowVeryShort, window[_shadowVeryShortAveragePeriod]);
135 
136  _nearPeriodTotal += GetCandleRange(CandleSettingType.Near, window[1]) -
137  GetCandleRange(CandleSettingType.Near, window[_nearAveragePeriod + 1]);
138 
139  return value;
140  }
141 
142  /// <summary>
143  /// Resets this indicator to its initial state
144  /// </summary>
145  public override void Reset()
146  {
147  _bodyShortPeriodTotal = 0m;
148  _shadowLongPeriodTotal = 0m;
149  _shadowVeryShortPeriodTotal = 0m;
150  _nearPeriodTotal = 0m;
151  base.Reset();
152  }
153  }
154 }