TechGeneralIndicators.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. //
  5. // Purpose: This class is used for calculations of
  6. // general technical analyses indicators.
  7. //
  8. using System;
  9. using System.Globalization;
  10. namespace FastReport.DataVisualization.Charting.Formulas
  11. {
  12. /// <summary>
  13. /// This class is used for calculations of general
  14. /// technical analyses indicators.
  15. /// </summary>
  16. internal class GeneralTechnicalIndicators : PriceIndicators
  17. {
  18. #region Properties
  19. /// <summary>
  20. /// Formula Module name
  21. /// </summary>
  22. override public string Name { get { return SR.FormulaNameGeneralTechnicalIndicators; } }
  23. #endregion
  24. #region Formulas
  25. /// <summary>
  26. /// Standard Deviation is a statistical measure of volatility.
  27. /// Standard Deviation is typically used as a component of
  28. /// other indicators, rather than as a stand-alone indicator.
  29. /// For example, Bollinger Bands are calculated by adding
  30. /// a security's Standard Deviation to a moving average.
  31. /// High Standard Deviation values occur when the data item
  32. /// being analyzed (e.g., prices or an indicator) is changing
  33. /// dramatically. Similarly, low Standard Deviation values
  34. /// occur when prices are stable.
  35. /// ---------------------------------------------------------
  36. /// Input:
  37. /// - 1 Y value.
  38. /// Output:
  39. /// - 1 Y value Standard Deviation
  40. /// Parameters:
  41. /// - Periods for standard deviation ( used for moving average )
  42. /// Extra Parameters:
  43. /// -
  44. /// </summary>
  45. /// <param name="inputValues">Arrays of doubles - Input values</param>
  46. /// <param name="outputValues">Arrays of doubles - Output values</param>
  47. /// <param name="parameterList">Array of strings - Parameters</param>
  48. /// <param name="extraParameterList">Array of strings - Extra parameters</param>
  49. private void StandardDeviation(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
  50. {
  51. int length = inputValues.Length;
  52. // Period for standard deviation ( used for moving average )
  53. int period;
  54. try
  55. {period = int.Parse( parameterList[0], System.Globalization.CultureInfo.InvariantCulture );}
  56. catch( Exception e )
  57. {
  58. if (e.Message == SR.ExceptionObjectReferenceIsNull)
  59. throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing);
  60. else
  61. throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing + e.Message);
  62. }
  63. if( period <= 0 )
  64. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  65. // Starting average from the first data point or after period.
  66. bool startFromFirst = bool.Parse( extraParameterList[0] );
  67. // There is no enough series
  68. if( length != 2 )
  69. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  70. // Different number of x and y values
  71. if( inputValues[0].Length != inputValues[1].Length )
  72. throw new ArgumentException(SR.ExceptionPriceIndicatorsSameXYNumber);
  73. // Not enough values for moving average in Standard deviation.
  74. if( inputValues[0].Length < period )
  75. throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
  76. outputValues = new double [2][];
  77. StandardDeviation( inputValues[1], out outputValues[1], period, startFromFirst );
  78. // Set X values
  79. outputValues[0] = new double [outputValues[1].Length];
  80. for( int index = 0; index < outputValues[1].Length; index++ )
  81. {
  82. if( startFromFirst )
  83. outputValues[0][index] = inputValues[0][index];
  84. else
  85. outputValues[0][index] = inputValues[0][index+period-1];
  86. }
  87. }
  88. /// <summary>
  89. /// The Average True Range ("ATR") is a measure of volatility. It was introduced
  90. /// by Welles Wilder in his book, New Concepts in Technical Trading Systems, and
  91. /// has since been used as a component of many indicators and trading systems. Wilder
  92. /// has found that high ATR values often occur at market bottoms following a "panic"
  93. /// sell-off. Low Average True Range values are often found during extended sideways
  94. /// periods, such as those found at tops and after consolidation periods. The Average
  95. /// True Range can be interpreted using the same techniques that are used with
  96. /// the other volatility indicators.
  97. /// ---------------------------------------------------------
  98. /// Input:
  99. /// - 3 Y values ( High, Low, Close ).
  100. /// Output:
  101. /// - 1 Y value AverageTrueRange
  102. /// Parameters:
  103. /// - Periods (Default 14) = is used to configure the number of periods to calculate the ATR
  104. /// </summary>
  105. /// <param name="inputValues">Arrays of doubles - Input values</param>
  106. /// <param name="outputValues">Arrays of doubles - Output values</param>
  107. /// <param name="parameterList">Array of strings - Parameters</param>
  108. private void AverageTrueRange(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  109. {
  110. // There is no enough input series
  111. if( inputValues.Length != 4 )
  112. throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
  113. // Different number of x and y values
  114. CheckNumOfValues( inputValues, 3 );
  115. // Period
  116. int period;
  117. if (parameterList.Length < 1 ||
  118. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  119. {
  120. period = 14;
  121. }
  122. if( period <= 0 )
  123. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  124. // The distance from today's high to today's low
  125. double distanceOne;
  126. // The distance from yesterday's close to today's high
  127. double distanceTwo;
  128. // The distance from yesterday's close to today's low
  129. double distanceTree;
  130. double [] trueRange = new double [inputValues[0].Length - 1];
  131. // True Range
  132. for( int index = 1; index < inputValues[0].Length; index++ )
  133. {
  134. // The distance from today's high to today's low
  135. distanceOne = Math.Abs( inputValues[1][index] - inputValues[2][index] );
  136. // The distance from yesterday's close to today's high
  137. distanceTwo = Math.Abs( inputValues[3][index-1] - inputValues[1][index] );
  138. // The distance from yesterday's close to today's low
  139. distanceTree = Math.Abs( inputValues[3][index-1] - inputValues[2][index] );
  140. // True Range
  141. trueRange[index-1] = Math.Max( Math.Max( distanceOne, distanceTwo ), distanceTree );
  142. }
  143. outputValues = new double [2][];
  144. outputValues[0] = new double [inputValues[0].Length-period];
  145. // Moving average of true range
  146. MovingAverage( trueRange, out outputValues[1], period, false );
  147. // Set X values
  148. for( int index = period; index < inputValues[0].Length; index++ )
  149. {
  150. outputValues[0][index-period] = inputValues[0][index];
  151. }
  152. }
  153. /// <summary>
  154. /// The Ease of Movement indicator shows the relationship between volume and price
  155. /// change. This indicator shows how much volume is required to move prices. The Ease
  156. /// of Movement indicator was developed Richard W. Arms, Jr., the creator of Equivolume.
  157. /// High Ease of Movement values occur when prices are moving upward on lightStyle volume.
  158. /// Low Ease of Movement values occur when prices are moving downward on lightStyle volume.
  159. /// If prices are not moving, or if heavy volume is required to move prices, then
  160. /// indicator will also be near zero.
  161. /// ---------------------------------------------------------
  162. /// Input:
  163. /// - 3 Y values ( High, Low, Volume ).
  164. /// Output:
  165. /// - 1 Y value Ease Of Movement
  166. /// </summary>
  167. /// <param name="inputValues">Arrays of doubles - Input values</param>
  168. /// <param name="outputValues">Arrays of doubles - Output values</param>
  169. private void EaseOfMovement(double [][] inputValues, out double [][] outputValues)
  170. {
  171. // There is no enough input series
  172. if( inputValues.Length != 4 )
  173. throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
  174. // Different number of x and y values
  175. CheckNumOfValues( inputValues, 3 );
  176. double MidPointMove;
  177. double BoxRattio;
  178. outputValues = new double [2][];
  179. outputValues[0] = new double [inputValues[0].Length - 1];
  180. outputValues[1] = new double [inputValues[0].Length - 1];
  181. // Ease Of Movement
  182. for( int index = 1; index < inputValues[0].Length; index++ )
  183. {
  184. // Set X values
  185. outputValues[0][index - 1] = inputValues[0][index];
  186. // Calculate the Mid-point Move for each day:
  187. MidPointMove = ( inputValues[1][index] + inputValues[2][index] ) / 2 - ( inputValues[1][index - 1] + inputValues[2][index - 1] ) / 2;
  188. // The Box Ratio determines the ratio between height and width of the Equivolume box:
  189. BoxRattio = ( inputValues[3][index] ) / (( inputValues[1][index] - inputValues[2][index] ) );
  190. // Ease of Movement is then calculated as:
  191. outputValues[1][index - 1] = MidPointMove / BoxRattio;
  192. }
  193. }
  194. /// <summary>
  195. /// The Mass Index was designed to identify trend reversals by measuring the narrowing
  196. /// and widening of the range between the high and low prices. As this range widens, the
  197. /// Mass Index increases; as the range narrows the Mass Index decreases.
  198. /// The Mass Index was developed by Donald Dorsey. According to Mr. Dorsey, the most
  199. /// significant pattern to watch for is a "reversal bulge." A reversal bulge occurs when
  200. /// a 25-period Mass Index rises above 27.0 and subsequently falls below 26.5. A reversal
  201. /// in price is then likely. The overall price trend (i.e., trending or trading range)
  202. /// is unimportant.
  203. /// ---------------------------------------------------------
  204. /// Input:
  205. /// - 2 Y values ( High, Low ).
  206. /// Output:
  207. /// - 1 Y value Mass Index
  208. /// Parameters:
  209. /// - Period = is used to calculate the accumulation, By default this property is set to 25.
  210. /// - AveragePeriod = is used to calculate Simple Moving Avg, By default this property is set to 9.
  211. /// </summary>
  212. /// <param name="inputValues">Arrays of doubles - Input values</param>
  213. /// <param name="outputValues">Arrays of doubles - Output values</param>
  214. /// <param name="parameterList">Array of strings - Parameters</param>
  215. private void MassIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  216. {
  217. // There is no enough input series
  218. if( inputValues.Length != 3 )
  219. throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
  220. // Different number of x and y values
  221. CheckNumOfValues( inputValues, 2 );
  222. // Period
  223. int period;
  224. if (parameterList.Length < 1 ||
  225. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  226. {
  227. period = 25;
  228. }
  229. if( period <= 0 )
  230. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  231. // Average Period
  232. int averagePeriod;
  233. if (parameterList.Length < 2 ||
  234. !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out averagePeriod))
  235. {
  236. averagePeriod = 9;
  237. }
  238. if( period <= 0 )
  239. throw new InvalidOperationException(SR.ExceptionPeriodAverageParameterIsNegative);
  240. double [] highLow = new double [inputValues[0].Length];
  241. double [] average;
  242. double [] secondAverage;
  243. for( int index = 0; index < inputValues[0].Length; index++ )
  244. {
  245. highLow[index] = inputValues[1][index] - inputValues[2][index];
  246. }
  247. // Find exponential moving average
  248. ExponentialMovingAverage( highLow, out average, averagePeriod, false );
  249. // Find exponential moving average of exponential moving average
  250. ExponentialMovingAverage( average, out secondAverage, averagePeriod, false );
  251. outputValues = new double [2][];
  252. outputValues[0] = new double [secondAverage.Length - period + 1];
  253. outputValues[1] = new double [secondAverage.Length - period + 1];
  254. // Mass Index
  255. int outIndex = 0;
  256. double sum = 0;
  257. for( int index = 2 * averagePeriod - 3 + period; index < inputValues[0].Length; index++ )
  258. {
  259. // Set X values
  260. outputValues[0][outIndex] = inputValues[0][index];
  261. sum = 0;
  262. for( int indexSum = index - period + 1; indexSum <= index; indexSum++ )
  263. {
  264. sum += average[indexSum - averagePeriod + 1] / secondAverage[indexSum - 2 * averagePeriod + 2];
  265. }
  266. // Set Y values
  267. outputValues[1][outIndex] = sum;
  268. outIndex++;
  269. }
  270. }
  271. /// <summary>
  272. /// The Performance indicator displays a security's price performance as
  273. /// a percentage. This is sometimes called a "normalized" chart. The
  274. /// Performance indicator displays the percentage that the security
  275. /// has increased since the first period displayed. For example, if
  276. /// the Performance indicator is 10, it means that the security's
  277. /// price has increased 10% since the first period displayed on the
  278. /// left side of the chart. Similarly, a value of -10% means that
  279. /// the security's price has fallen by 10% since the first period
  280. /// displayed.
  281. /// ---------------------------------------------------------
  282. /// Input:
  283. /// - 1 Y value ( Close ).
  284. /// Output:
  285. /// - 1 Y value Performance
  286. /// </summary>
  287. /// <param name="inputValues">Arrays of doubles - Input values</param>
  288. /// <param name="outputValues">Arrays of doubles - Output values</param>
  289. private void Performance(double [][] inputValues, out double [][] outputValues)
  290. {
  291. // There is no enough input series
  292. if( inputValues.Length != 2 )
  293. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  294. // Different number of x and y values
  295. CheckNumOfValues( inputValues, 1 );
  296. outputValues = new double [2][];
  297. outputValues[0] = new double [inputValues[0].Length];
  298. outputValues[1] = new double [inputValues[0].Length];
  299. // Performance indicator
  300. for( int index = 0; index < inputValues[0].Length; index++ )
  301. {
  302. // Set X values
  303. outputValues[0][index] = inputValues[0][index];
  304. // Set Y values
  305. outputValues[1][index] = ( inputValues[1][index] - inputValues[1][0] ) / inputValues[1][0] * 100;
  306. }
  307. }
  308. /// <summary>
  309. /// Rate of Change is used to monitor momentum by making direct comparisons between current
  310. /// and past prices on a continual basis. The results can be used to determine the strength
  311. /// of price trends. Note: This study is the same as the Momentum except that Momentum uses
  312. /// subtraction in its calculations while Rate of Change uses division. The resulting lines
  313. /// of these two studies operated over the same data will look exactly the same - only the
  314. /// scale values will differ. The Price Rate-of-Change indicator displays the
  315. /// difference between the current price and the price x-time periods ago. The difference
  316. /// can be displayed in either points or as a percentage. The Momentum indicator displays
  317. /// the same information, but expresses it as a ratio. When the Rate-of-Change displays
  318. /// the price change in points, it subtracts the price x-time periods ago from today’s price.
  319. /// When the Rate-of-Change displays the price change as a percentage, it divides
  320. /// the price change by price x-time period’s ago.
  321. /// ---------------------------------------------------------
  322. /// Input:
  323. /// - 1 Y value ( Close ).
  324. /// Output:
  325. /// - 1 Y value Rate of Change
  326. /// Parameters:
  327. /// - Periods = is used to configure the number of periods to calculate the rate of Change. By default the Periods property is set to 10.
  328. /// </summary>
  329. /// <param name="inputValues">Arrays of doubles - Input values</param>
  330. /// <param name="outputValues">Arrays of doubles - Output values</param>
  331. /// <param name="parameterList">Array of strings - Parameters</param>
  332. private void RateOfChange(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  333. {
  334. // There is no enough input series
  335. if( inputValues.Length != 2 )
  336. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  337. // Different number of x and y values
  338. CheckNumOfValues( inputValues, 1 );
  339. // Period
  340. int period;
  341. if (parameterList.Length < 1 ||
  342. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  343. {
  344. period = 10;
  345. }
  346. if( period <= 0 )
  347. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  348. outputValues = new double [2][];
  349. outputValues[0] = new double [inputValues[0].Length - period];
  350. outputValues[1] = new double [inputValues[0].Length - period];
  351. // Rate Of Change
  352. for( int index = period; index < inputValues[0].Length; index++ )
  353. {
  354. // Set X values
  355. outputValues[0][index - period] = inputValues[0][index];
  356. // Set Y values
  357. outputValues[1][index - period] = ( inputValues[1][index] - inputValues[1][index - period] ) / inputValues[1][index - period] * 100;
  358. }
  359. }
  360. /// <summary>
  361. /// This indicator was developed by Welles Wilder Jr. Relative Strength is often
  362. /// used to identify price tops and bottoms by keying on specific levels
  363. /// (usually "30" and "70") on the RSI chart which is scaled from from 0-100.
  364. /// The study is also useful to detect the following:
  365. /// - Movement which might not be as readily apparent on the bar chart
  366. /// - Failure swings above 70 or below 30 which can warn of coming reversals
  367. /// - Support and resistance levels
  368. /// - Divergence between the RSI and price which is often a useful reversal indicator
  369. /// ---------------------------------------------------------
  370. /// Input:
  371. /// - 1 Y value ( Close ).
  372. /// Output:
  373. /// - 1 Y value RelativeStrengthIndex
  374. /// Parameters:
  375. /// - Periods = is used to configure the number of periods to calculate the RSI indicator. By default the Periods property is set to 10.
  376. /// </summary>
  377. /// <param name="inputValues">Arrays of doubles - Input values</param>
  378. /// <param name="outputValues">Arrays of doubles - Output values</param>
  379. /// <param name="parameterList">Array of strings - Parameters</param>
  380. private void RelativeStrengthIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  381. {
  382. // There is no enough input series
  383. if( inputValues.Length != 2 )
  384. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  385. // Different number of x and y values
  386. CheckNumOfValues( inputValues, 1 );
  387. // Period
  388. int period;
  389. if (parameterList.Length < 1 ||
  390. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  391. {
  392. period = 10;
  393. }
  394. if( period <= 0 )
  395. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  396. double [] upward = new double[inputValues[0].Length-1];
  397. double [] downward = new double[inputValues[0].Length-1];
  398. for( int index = 1; index < inputValues[0].Length; index++ )
  399. {
  400. // Upward - price is going up
  401. if( inputValues[1][index - 1] < inputValues[1][index] )
  402. {
  403. upward[index-1] = inputValues[1][index] - inputValues[1][index - 1];
  404. downward[index-1] = 0.0;
  405. }
  406. // Downward - price is going down
  407. if( inputValues[1][index - 1] > inputValues[1][index] )
  408. {
  409. upward[index-1] = 0.0;
  410. downward[index-1] = inputValues[1][index - 1] - inputValues[1][index];
  411. }
  412. }
  413. double [] averageUpward = new double[inputValues[0].Length];
  414. double [] averageDownward = new double[inputValues[0].Length];
  415. ExponentialMovingAverage(downward, out averageDownward, period, false );
  416. ExponentialMovingAverage(upward, out averageUpward, period, false );
  417. outputValues = new double [2][];
  418. outputValues[0] = new double [averageDownward.Length];
  419. outputValues[1] = new double [averageDownward.Length];
  420. // Find RSI
  421. for( int index = 0; index < averageDownward.Length; index++ )
  422. {
  423. // Set X values
  424. outputValues[0][index] = inputValues[0][index + period];
  425. // Calculate the Relative Strength Index (RSI):
  426. outputValues[1][index] = 100 - 100 / ( 1 + averageUpward[index] / averageDownward[index] );
  427. }
  428. }
  429. /// <summary>
  430. /// TripleExponentialMovingAverage is a momentum indicator that displays the percent rate-of-change of a triple
  431. /// exponentially smoothed moving average of the security's closing price. It is designed
  432. /// to keep you in trends equal to or shorter than the number of periods you specify.
  433. /// The TripleExponentialMovingAverage indicator oscillates around a zero line. Its triple exponential smoothing is
  434. /// designed to filter out "insignificant" cycles (i.e., those that are shorter than
  435. /// the number of periods you specify). Trades should be placed when the indicator changes
  436. /// direction (i.e., buy when it turns up and sell when it turns down). You may want to
  437. /// plot a 9-period moving average of the TripleExponentialMovingAverage to create a "signal" line (similar to the
  438. /// MovingAverageConvergenceDivergence indicator, and then buy when the TripleExponentialMovingAverage rises above its signal, and sell when it
  439. /// falls below its signal. Divergences between the security and the TripleExponentialMovingAverage can also help
  440. /// identify turning points.
  441. /// ---------------------------------------------------------
  442. /// Input:
  443. /// - 1 Y values ( Close ).
  444. /// Output:
  445. /// - 1 Y value ( TripleExponentialMovingAverage ).
  446. /// Parameters:
  447. /// - Period = is used to calculate the Exponential Moving Avg, By default this property is set to 12.
  448. /// </summary>
  449. /// <param name="inputValues">Arrays of doubles - Input values</param>
  450. /// <param name="outputValues">Arrays of doubles - Output values</param>
  451. /// <param name="parameterList">Array of strings - Parameters</param>
  452. private void Trix(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  453. {
  454. // There is no enough input series
  455. if( inputValues.Length != 2 )
  456. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  457. // Different number of x and y values
  458. CheckNumOfValues( inputValues, 1 );
  459. // Period
  460. int period;
  461. if (parameterList.Length < 1 ||
  462. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  463. {
  464. period = 12;
  465. }
  466. if( period <= 0 )
  467. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  468. double [] exp1; // Exponential Moving average of input values
  469. double [] exp2; // Exponential Moving average of exp1
  470. double [] exp3; // Exponential Moving average of exp2
  471. // Find exponential moving average
  472. ExponentialMovingAverage( inputValues[1], out exp1, period, false );
  473. // Find exponential moving average
  474. ExponentialMovingAverage( exp1, out exp2, period, false );
  475. // Find exponential moving average
  476. ExponentialMovingAverage( exp2, out exp3, period, false );
  477. outputValues = new double [2][];
  478. outputValues[0] = new double [inputValues[0].Length - period * 3 + 2];
  479. outputValues[1] = new double [inputValues[0].Length - period * 3 + 2];
  480. // Calculate TripleExponentialMovingAverage
  481. int outIndex = 0;
  482. for( int index = period * 3 - 2; index < inputValues[0].Length; index++ )
  483. {
  484. // set X value
  485. outputValues[0][outIndex] = inputValues[0][index];
  486. // set Y value
  487. outputValues[1][outIndex] = ( exp3[outIndex+1] - exp3[outIndex] ) / exp3[outIndex];
  488. outIndex++;
  489. }
  490. }
  491. /// <summary>
  492. /// The MovingAverageConvergenceDivergence is used to determine overbought or oversold conditions in the market. Written
  493. /// for stocks and stock indices, MovingAverageConvergenceDivergence can be used for commodities as well. The MovingAverageConvergenceDivergence line
  494. /// is the difference between the long and short exponential moving averages of the chosen
  495. /// item. The signal line is an exponential moving average of the MovingAverageConvergenceDivergence line. Signals are
  496. /// generated by the relationship of the two lines. As with RSI and Stochastics,
  497. /// divergences between the MovingAverageConvergenceDivergence and prices may indicate an upcoming trend reversal. The MovingAverageConvergenceDivergence
  498. /// is a trend following momentum indicator that shows the relationship between two
  499. /// moving averages of prices. The MovingAverageConvergenceDivergence is the difference between a 26-day and 12-day
  500. /// exponential moving average. A 9-day exponential moving average, called the "signal"
  501. /// (or "trigger") line is plotted on top of the MovingAverageConvergenceDivergence to show buy/sell opportunities. The
  502. /// MovingAverageConvergenceDivergence is calculated by subtracting the value of a 26-day exponential moving average
  503. /// from a 12-day exponential moving average. A 9-day dotted exponential moving average of
  504. /// the MovingAverageConvergenceDivergence (the "signal" line) is then plotted on top of the MovingAverageConvergenceDivergence.
  505. /// ---------------------------------------------------------
  506. /// Input:
  507. /// - 1 Y value ( Close ).
  508. /// Output:
  509. /// - 1 Y value ( MovingAverageConvergenceDivergence ).
  510. /// Parameters:
  511. /// - ShortPeriod = is used to configure the short Exponential Moving Average, By default this property is set to 12.
  512. /// - LongPeriod = is used to configure the Int64 Exponential Moving Average, By default this property is set to 26.
  513. /// </summary>
  514. /// <param name="inputValues">Arrays of doubles - Input values</param>
  515. /// <param name="outputValues">Arrays of doubles - Output values</param>
  516. /// <param name="parameterList">Array of strings - Parameters</param>
  517. private void Macd(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  518. {
  519. // There is no enough input series
  520. if( inputValues.Length != 2 )
  521. throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
  522. // Different number of x and y values
  523. CheckNumOfValues( inputValues, 1 );
  524. // Short Period
  525. int shortPeriod;
  526. if (parameterList.Length < 1 ||
  527. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out shortPeriod))
  528. {
  529. shortPeriod = 12;
  530. }
  531. if( shortPeriod <= 0 )
  532. throw new InvalidOperationException(SR.ExceptionPeriodShortParameterIsNegative);
  533. // Int64 Period
  534. int longPeriod;
  535. if (parameterList.Length < 2 ||
  536. !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out longPeriod))
  537. {
  538. longPeriod = 26;
  539. }
  540. if( longPeriod <= 0 )
  541. throw new InvalidOperationException(SR.ExceptionPeriodLongParameterIsNegative);
  542. if( longPeriod <= shortPeriod )
  543. throw new InvalidOperationException(SR.ExceptionIndicatorsLongPeriodLessThenShortPeriod);
  544. double [] longAverage; // Int64 Average
  545. double [] shortAverage; // Short Average
  546. // Find Int64 exponential moving average
  547. ExponentialMovingAverage( inputValues[1], out longAverage, longPeriod, false );
  548. // Find Short exponential moving average
  549. ExponentialMovingAverage( inputValues[1], out shortAverage, shortPeriod, false );
  550. outputValues = new double [2][];
  551. outputValues[0] = new double [inputValues[0].Length - longPeriod + 1];
  552. outputValues[1] = new double [inputValues[0].Length - longPeriod + 1];
  553. // Calculate MovingAverageConvergenceDivergence
  554. int outIndex = 0;
  555. for( int index = longPeriod - 1; index < inputValues[0].Length; index++ )
  556. {
  557. // set X value
  558. outputValues[0][outIndex] = inputValues[0][index];
  559. // set Y value
  560. outputValues[1][outIndex] = shortAverage[ outIndex + longPeriod - shortPeriod ] - longAverage[outIndex];
  561. outIndex++;
  562. }
  563. }
  564. /// <summary>
  565. /// The CCI is a timing system that is best applied to commodity contracts which
  566. /// have cyclical or seasonal tendencies. CCI does not determine the length of
  567. /// cycles - it is designed to detect when such cycles begin and end through
  568. /// the use of a statistical analysis which incorporates a moving average and a divisor
  569. /// reflecting both the possible and actual trading ranges. Although developed primarily
  570. /// for commodities, the CCI could conceivably be used to analyze stocks as well. The
  571. /// Commodity Channel Index ("CCI") measures the variation of a security’s price from
  572. /// its statistical mean. High values show that prices are unusually high compared to
  573. /// average prices whereas low values indicate that prices are unusually low.
  574. /// 1. Calculate today's Typical Price (TP) = (H+L+C)/3 where H = high; L = low, and C = close.
  575. /// 2. Calculate today's 20-day Simple Moving Average of the Typical Price (SMATP).
  576. /// 3. Calculate today's Mean Deviation. First, calculate the absolute value of the difference
  577. /// between today's SMATP and the typical price for each of the past 20 days.
  578. /// Add all of these absolute values together and divide by 20 to find the Mean Deviation.
  579. /// 4. The final step is to apply the Typical Price (TP), the Simple Moving Average of the
  580. /// Typical Price (SMATP), the Mean Deviation and a Constant (.015).
  581. /// ---------------------------------------------------------
  582. /// Input:
  583. /// - 3 Y values ( Hi, Low, Close ).
  584. /// Output:
  585. /// - 1 Y value ( CCI ).
  586. /// Parameters:
  587. /// - Periods = is used to configure the number of periods to calculate the CCI. By default the Periods property is set to 10.
  588. /// </summary>
  589. /// <param name="inputValues">Arrays of doubles - Input values</param>
  590. /// <param name="outputValues">Arrays of doubles - Output values</param>
  591. /// <param name="parameterList">Array of strings - Parameters</param>
  592. private void CommodityChannelIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
  593. {
  594. // There is no enough input series
  595. if( inputValues.Length != 4 )
  596. throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
  597. // Different number of x and y values
  598. CheckNumOfValues( inputValues, 3 );
  599. // Period
  600. int period;
  601. if (parameterList.Length < 1 ||
  602. !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
  603. {
  604. period = 10;
  605. }
  606. if( period <= 0 )
  607. throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
  608. // Typical Price
  609. double [] typicalPrice = new double[inputValues[0].Length];
  610. // Typical Price loop
  611. for( int index = 0; index < inputValues[0].Length; index++ )
  612. {
  613. typicalPrice[index] = ( inputValues[1][index] + inputValues[2][index] + inputValues[3][index] ) / 3.0;
  614. }
  615. // Moving Average
  616. double [] movingAverage;
  617. // Simple Moving Average of the Typical Price
  618. MovingAverage( typicalPrice, out movingAverage, period, false );
  619. // Calculate today's Mean Deviation. First, calculate the absolute value
  620. // of the difference between today's SMATP and the typical price for each
  621. // of the past 20 days. Add all of these absolute values together and
  622. // divide by 20 to find the Mean Deviation.
  623. // Mean Deviation
  624. double [] meanDeviation = new double[movingAverage.Length];
  625. double sum =0;
  626. for( int index = 0; index < movingAverage.Length; index++ )
  627. {
  628. sum = 0;
  629. for( int indexSum = index; indexSum < index + period; indexSum++ )
  630. {
  631. sum += Math.Abs( movingAverage[index] - typicalPrice[indexSum] );
  632. }
  633. meanDeviation[index] = sum / period;
  634. }
  635. outputValues = new double [2][];
  636. outputValues[0] = new double [meanDeviation.Length];
  637. outputValues[1] = new double [meanDeviation.Length];
  638. for( int index = 0; index < meanDeviation.Length; index++ )
  639. {
  640. // Set X values
  641. outputValues[0][index] = inputValues[0][index + period - 1];
  642. // Set Y values
  643. outputValues[1][index] = ( typicalPrice[index + period - 1] - movingAverage[index] ) / ( 0.015 * meanDeviation[index] );
  644. }
  645. }
  646. #endregion
  647. #region Methods
  648. /// <summary>
  649. /// Default constructor
  650. /// </summary>
  651. public GeneralTechnicalIndicators()
  652. {
  653. }
  654. /// <summary>
  655. /// The first method in the module, which converts a formula
  656. /// name to the corresponding private method.
  657. /// </summary>
  658. /// <param name="formulaName">String which represent a formula name</param>
  659. /// <param name="inputValues">Arrays of doubles - Input values</param>
  660. /// <param name="outputValues">Arrays of doubles - Output values</param>
  661. /// <param name="parameterList">Array of strings - Formula parameters</param>
  662. /// <param name="extraParameterList">Array of strings - Extra Formula parameters from DataManipulator object</param>
  663. /// <param name="outLabels">Array of strings - Used for Labels. Description for output results.</param>
  664. override public void Formula( string formulaName, double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList, out string [][] outLabels )
  665. {
  666. string name;
  667. outputValues = null;
  668. name = formulaName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
  669. // Not used for these formulas.
  670. outLabels = null;
  671. try
  672. {
  673. switch( name )
  674. {
  675. case "STANDARDDEVIATION":
  676. StandardDeviation( inputValues, out outputValues, parameterList, extraParameterList );
  677. break;
  678. case "AVERAGETRUERANGE":
  679. AverageTrueRange( inputValues, out outputValues, parameterList );
  680. break;
  681. case "EASEOFMOVEMENT":
  682. EaseOfMovement( inputValues, out outputValues );
  683. break;
  684. case "MASSINDEX":
  685. MassIndex( inputValues, out outputValues, parameterList );
  686. break;
  687. case "PERFORMANCE":
  688. Performance( inputValues, out outputValues );
  689. break;
  690. case "RATEOFCHANGE":
  691. RateOfChange( inputValues, out outputValues, parameterList );
  692. break;
  693. case "RELATIVESTRENGTHINDEX":
  694. RelativeStrengthIndex( inputValues, out outputValues, parameterList );
  695. break;
  696. case "TRIPLEEXPONENTIALMOVINGAVERAGE":
  697. Trix( inputValues, out outputValues, parameterList );
  698. break;
  699. case "MOVINGAVERAGECONVERGENCEDIVERGENCE":
  700. Macd( inputValues, out outputValues, parameterList );
  701. break;
  702. case "COMMODITYCHANNELINDEX":
  703. CommodityChannelIndex( inputValues, out outputValues, parameterList );
  704. break;
  705. default:
  706. outputValues = null;
  707. break;
  708. }
  709. }
  710. catch( IndexOutOfRangeException )
  711. {
  712. throw new InvalidOperationException( SR.ExceptionFormulaInvalidPeriod( name ) );
  713. }
  714. catch( OverflowException )
  715. {
  716. throw new InvalidOperationException( SR.ExceptionFormulaNotEnoughDataPoints( name ) );
  717. }
  718. }
  719. #endregion
  720. }
  721. }