data_util.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import 'dart:math';
  2. import '../entity/index.dart';
  3. class DataUtil {
  4. static calculate(List<KLineEntity> dataList,
  5. [List<int> maDayList = const [5, 10, 20], int n = 20, k = 2]) {
  6. /// calculate main state
  7. calcMA(dataList, maDayList);
  8. calcBOLL(dataList, n, k);
  9. calcSAR(dataList);
  10. /// calculate secondary state
  11. calcVolumeMA(dataList);
  12. calcKDJ(dataList);
  13. calcMACD(dataList);
  14. calcRSI(dataList);
  15. calcWR(dataList);
  16. calcCCI(dataList);
  17. }
  18. static calcMA(List<KLineEntity> dataList, List<int> maDayList) {
  19. List<double> ma = List<double>.filled(maDayList.length, 0);
  20. if (dataList.isNotEmpty) {
  21. for (int i = 0; i < dataList.length; i++) {
  22. KLineEntity entity = dataList[i];
  23. final closePrice = entity.close;
  24. entity.maValueList = List<double>.filled(maDayList.length, 0);
  25. for (int j = 0; j < maDayList.length; j++) {
  26. ma[j] += closePrice;
  27. if (i == maDayList[j] - 1) {
  28. entity.maValueList?[j] = ma[j] / maDayList[j];
  29. } else if (i >= maDayList[j]) {
  30. ma[j] -= dataList[i - maDayList[j]].close;
  31. entity.maValueList?[j] = ma[j] / maDayList[j];
  32. }
  33. }
  34. }
  35. }
  36. }
  37. static void calcSAR(List<KLineEntity> dataList) {
  38. const List<double> params = [2, 2, 20]; //calcParams default
  39. final startAf = params[0] / 100;
  40. final step = params[1] / 100;
  41. final maxAf = params[2] / 100;
  42. // Acceleration factor
  43. double af = startAf;
  44. // Extreme point
  45. double ep = -100;
  46. // Determine trend direction — false: downtrend
  47. bool isIncreasing = false;
  48. double sar = 0;
  49. for (int i = 0; i < dataList.length; ++i) {
  50. // the previous period SAR
  51. final preSar = sar;
  52. final high = dataList[i].high;
  53. final low = dataList[i].low;
  54. if (isIncreasing) {
  55. // Uptrend
  56. if (ep == -100 || ep < high) {
  57. // Reinitialize parameters
  58. ep = high;
  59. af = min(af + step, maxAf);
  60. }
  61. sar = preSar + af * (ep - preSar);
  62. final lowMin = min(dataList[max(1, i) - 1].low, low);
  63. if (sar > dataList[i].low) {
  64. sar = ep;
  65. // Reinitialize parameters
  66. af = startAf;
  67. ep = -100;
  68. isIncreasing = !isIncreasing;
  69. } else if (sar > lowMin) {
  70. sar = lowMin;
  71. }
  72. } else {
  73. if (ep == -100 || ep > low) {
  74. // Reinitialize parameters
  75. ep = low;
  76. af = min(af + step, maxAf);
  77. }
  78. sar = preSar + af * (ep - preSar);
  79. final highMax = max(dataList[max(1, i) - 1].high, high);
  80. if (sar < dataList[i].high) {
  81. sar = ep;
  82. // Reinitialize parameters
  83. af = 0;
  84. ep = -100;
  85. isIncreasing = !isIncreasing;
  86. } else if (sar < highMax) {
  87. sar = highMax;
  88. }
  89. }
  90. dataList[i].sar = sar;
  91. }
  92. }
  93. static void calcBOLL(List<KLineEntity> dataList, int n, int k) {
  94. _calcBOLLMA(n, dataList);
  95. for (int i = 0; i < dataList.length; i++) {
  96. KLineEntity entity = dataList[i];
  97. if (i >= n) {
  98. double md = 0;
  99. for (int j = i - n + 1; j <= i; j++) {
  100. double c = dataList[j].close;
  101. double m = entity.BOLLMA!;
  102. double value = c - m;
  103. md += value * value;
  104. }
  105. md = md / (n - 1);
  106. md = sqrt(md);
  107. entity.mb = entity.BOLLMA!;
  108. entity.up = entity.mb! + k * md;
  109. entity.dn = entity.mb! - k * md;
  110. }
  111. }
  112. }
  113. static void _calcBOLLMA(int day, List<KLineEntity> dataList) {
  114. double ma = 0;
  115. for (int i = 0; i < dataList.length; i++) {
  116. KLineEntity entity = dataList[i];
  117. ma += entity.close;
  118. if (i == day - 1) {
  119. entity.BOLLMA = ma / day;
  120. } else if (i >= day) {
  121. ma -= dataList[i - day].close;
  122. entity.BOLLMA = ma / day;
  123. } else {
  124. entity.BOLLMA = null;
  125. }
  126. }
  127. }
  128. static void calcMACD(List<KLineEntity> dataList) {
  129. double ema12 = 0;
  130. double ema26 = 0;
  131. double dif = 0;
  132. double dea = 0;
  133. double macd = 0;
  134. for (int i = 0; i < dataList.length; i++) {
  135. KLineEntity entity = dataList[i];
  136. final closePrice = entity.close;
  137. if (i == 0) {
  138. ema12 = closePrice;
  139. ema26 = closePrice;
  140. } else {
  141. // EMA(12) = 前一日EMA(12) X 11/13 + 今日收盘价 X 2/13
  142. ema12 = ema12 * 11 / 13 + closePrice * 2 / 13;
  143. // EMA(26) = 前一日EMA(26) X 25/27 + 今日收盘价 X 2/27
  144. ema26 = ema26 * 25 / 27 + closePrice * 2 / 27;
  145. }
  146. // DIF = EMA(12) - EMA(26) 。
  147. // 今日DEA = (前一日DEA X 8/10 + 今日DIF X 2/10)
  148. // 用(DIF-DEA)*2即为MACD柱状图。
  149. dif = ema12 - ema26;
  150. dea = dea * 8 / 10 + dif * 2 / 10;
  151. macd = (dif - dea) * 2;
  152. entity.dif = dif;
  153. entity.dea = dea;
  154. entity.macd = macd;
  155. }
  156. }
  157. static void calcVolumeMA(List<KLineEntity> dataList) {
  158. double volumeMa5 = 0;
  159. double volumeMa10 = 0;
  160. for (int i = 0; i < dataList.length; i++) {
  161. KLineEntity entry = dataList[i];
  162. volumeMa5 += entry.vol;
  163. volumeMa10 += entry.vol;
  164. if (i == 4) {
  165. entry.MA5Volume = (volumeMa5 / 5);
  166. } else if (i > 4) {
  167. volumeMa5 -= dataList[i - 5].vol;
  168. entry.MA5Volume = volumeMa5 / 5;
  169. } else {
  170. entry.MA5Volume = 0;
  171. }
  172. if (i == 9) {
  173. entry.MA10Volume = volumeMa10 / 10;
  174. } else if (i > 9) {
  175. volumeMa10 -= dataList[i - 10].vol;
  176. entry.MA10Volume = volumeMa10 / 10;
  177. } else {
  178. entry.MA10Volume = 0;
  179. }
  180. }
  181. }
  182. static void calcRSI(List<KLineEntity> dataList) {
  183. double? rsi;
  184. double rsiABSEma = 0;
  185. double rsiMaxEma = 0;
  186. for (int i = 0; i < dataList.length; i++) {
  187. KLineEntity entity = dataList[i];
  188. final double closePrice = entity.close;
  189. if (i == 0) {
  190. rsi = 0;
  191. rsiABSEma = 0;
  192. rsiMaxEma = 0;
  193. } else {
  194. double rMax = max(0, closePrice - dataList[i - 1].close.toDouble());
  195. double rAbs = (closePrice - dataList[i - 1].close.toDouble()).abs();
  196. rsiMaxEma = (rMax + (14 - 1) * rsiMaxEma) / 14;
  197. rsiABSEma = (rAbs + (14 - 1) * rsiABSEma) / 14;
  198. rsi = (rsiMaxEma / rsiABSEma) * 100;
  199. }
  200. if (i < 13) rsi = null;
  201. if (rsi != null && rsi.isNaN) rsi = null;
  202. entity.rsi = rsi;
  203. }
  204. }
  205. static void calcKDJ(List<KLineEntity> dataList) {
  206. var preK = 50.0;
  207. var preD = 50.0;
  208. final tmp = dataList.first;
  209. tmp.k = preK;
  210. tmp.d = preD;
  211. tmp.j = 50.0;
  212. for (int i = 1; i < dataList.length; i++) {
  213. final entity = dataList[i];
  214. final n = max(0, i - 8);
  215. var low = entity.low;
  216. var high = entity.high;
  217. for (int j = n; j < i; j++) {
  218. final t = dataList[j];
  219. if (t.low < low) {
  220. low = t.low;
  221. }
  222. if (t.high > high) {
  223. high = t.high;
  224. }
  225. }
  226. final cur = entity.close;
  227. var rsv = (cur - low) * 100.0 / (high - low);
  228. rsv = rsv.isNaN ? 0 : rsv;
  229. final k = (2 * preK + rsv) / 3.0;
  230. final d = (2 * preD + k) / 3.0;
  231. final j = 3 * k - 2 * d;
  232. preK = k;
  233. preD = d;
  234. entity.k = k;
  235. entity.d = d;
  236. entity.j = j;
  237. }
  238. }
  239. static void calcWR(List<KLineEntity> dataList) {
  240. double r;
  241. for (int i = 0; i < dataList.length; i++) {
  242. KLineEntity entity = dataList[i];
  243. int startIndex = i - 14;
  244. if (startIndex < 0) {
  245. startIndex = 0;
  246. }
  247. double max14 = double.minPositive;
  248. double min14 = double.maxFinite;
  249. for (int index = startIndex; index <= i; index++) {
  250. max14 = max(max14, dataList[index].high);
  251. min14 = min(min14, dataList[index].low);
  252. }
  253. if (i < 13) {
  254. entity.r = -10;
  255. } else {
  256. r = -100 * (max14 - dataList[i].close) / (max14 - min14);
  257. if (r.isNaN) {
  258. entity.r = null;
  259. } else {
  260. entity.r = r;
  261. }
  262. }
  263. }
  264. }
  265. static void calcCCI(List<KLineEntity> dataList) {
  266. final size = dataList.length;
  267. final count = 14;
  268. for (int i = 0; i < size; i++) {
  269. final kline = dataList[i];
  270. final tp = (kline.high + kline.low + kline.close) / 3;
  271. final start = max(0, i - count + 1);
  272. var amount = 0.0;
  273. var len = 0;
  274. for (int n = start; n <= i; n++) {
  275. amount += (dataList[n].high + dataList[n].low + dataList[n].close) / 3;
  276. len++;
  277. }
  278. final ma = amount / len;
  279. amount = 0.0;
  280. for (int n = start; n <= i; n++) {
  281. amount +=
  282. (ma - (dataList[n].high + dataList[n].low + dataList[n].close) / 3)
  283. .abs();
  284. }
  285. final md = amount / len;
  286. kline.cci = ((tp - ma) / 0.015 / md);
  287. if (kline.cci!.isNaN) {
  288. kline.cci = 0.0;
  289. }
  290. }
  291. }
  292. }