Test_portfolioIndicatorCalculation.dos 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. login(`admin, `123456)
  2. loadPlugin("ODBC")
  3. clearCachedModules()
  4. use fundit::fundCalculator
  5. use fundit::dataPuller
  6. use fundit::returnCalculator
  7. use fundit::indicatorCalculator
  8. /* init values for test cases */
  9. end_day = 2024.08.31; //2024.06.28;
  10. isFromNav = false;
  11. portfolio_ids = '166002,166114';
  12. cal_method = 1;
  13. /* codes from cal_fund_indicators */
  14. very_old_date = 1990.01.01;
  15. start_month = very_old_date.month();
  16. portfolio_info = get_portfolio_info(portfolio_ids);
  17. if(portfolio_info.isVoid() || portfolio_info.size() == 0) { return null };
  18. portfolio_info.rename!('portfolio_id', 'entity_id');
  19. if(isFromNav == true) {
  20. // 从净值开始计算收益
  21. tb_raw_ret = SELECT * FROM cal_portfolio_nav(portfolio_ids, very_old_date, cal_method) WHERE price_date <= end_day;
  22. if(tb_raw_ret.isVoid() || tb_raw_ret.size() == 0) return null;
  23. // funky thing is you can't use "AS" for the grouping columns?
  24. tb_ret = SELECT portfolio_id, price_date.month(), price_date.last() AS price_date, (1+ret).prod()-1 AS ret, nav.last() AS nav
  25. FROM tb_raw_ret
  26. WHERE price_date <= end_day
  27. GROUP BY portfolio_id, price_date.month();
  28. tb_ret.rename!(['portfolio_id', 'month_price_date'], ['entity_id', 'end_date']);
  29. } else {
  30. // 从pf_portfolio_performance表里读月收益
  31. tb_ret = get_monthly_ret('PF', portfolio_ids, very_old_date, end_day, true);
  32. tb_ret.rename!(['portfolio_id'], ['entity_id']);
  33. v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
  34. tb_ret.replaceColumn!('end_date', v_end_date);
  35. }
  36. // 沪深300做基准,同SQL保持一致
  37. t_dates = table(start_month..end_day.month() AS end_date);
  38. primary_benchmark = SELECT ei.entity_id, dt.end_date, 'IN00000008' AS benchmark_id
  39. FROM portfolio_info ei JOIN t_dates dt
  40. WHERE dt.end_date >= ei.inception_date.month();
  41. // 取所有出现的基准月收益
  42. bmk_ret = get_benchmark_return(primary_benchmark, end_day);
  43. risk_free_rate = SELECT fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
  44. /* Test START */
  45. entity_info = portfolio_info;
  46. entity_ids = portfolio_ids.split(',')$INT;
  47. // trailing 2 year standard indicators
  48. rtn = cal_basic_performance(entity_info, tb_ret, '24');
  49. @testing: case = 'trailing 2y return'
  50. assert (select rtn.trailing_ret.round(5) as trailing_ret
  51. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).trailing_ret == [-0.450874,-0.561324,-0.023055].round(5);
  52. @testing: case = 'trailing 2y std_dev_a' // [FAIL] slightly off
  53. assert (select (rtn.std_dev * sqrt(12)).round(4) as std_dev_a
  54. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).std_dev_a == [0.324279,0.276651,0.095596].round(4);
  55. @testing: case = 'trailing 2y skewness' // [FAIL] slightly off
  56. assert (select rtn.skewness.round(3) as skewness
  57. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).skewness == [-0.249279,-0.525918,0.179583].round(3);
  58. @testing: case = 'trailing 2y kurtosis' // [FAIL] slightly off
  59. assert (select rtn.kurtosis.round(2) as kurtosis
  60. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kurtosis == [1.493033 ,3.775385 ,-0.910534].round(2);
  61. @testing: case = 'trailing 2y wrst_month' // [FAIL] slightly off
  62. assert (select rtn.wrst_month.round(3) as wrst_month
  63. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).wrst_month == [-0.275322,-0.275002,-0.045004].round(2);
  64. @testing: case = 'trailing 2y max drawdown' // values from swagger and disable max drawdown when there is no NAV
  65. assert (select rtn.drawdown.round(3) as drawdown
  66. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).drawdown == [0.577912,0.536835,0.090399].round(3);
  67. @testing: case = 'trailing 2y var'
  68. assert (select rtn.var.round(4) as var // [FAIL]
  69. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).var == [0.244860,0.239825,0.043650].round(4);
  70. @testing: case = 'trailing 2y cvar'
  71. assert (select rtn.cvar.round(4) as cvar // [FAIL]
  72. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).cvar == [0.275322,0.275002,0.045004].round(4);
  73. @testing: case = 'trailing 2y calmar'
  74. assert (select rtn.calmar.round(4) as calmar // [FAIL]
  75. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).calmar == [-0.410715,-0.570759,-0.140442].round(4);
  76. // year-to-day standard indicators
  77. rtn = cal_basic_performance(entity_info, tb_ret, 'ytd');
  78. @testing: case = 'ytd return'
  79. assert (select rtn.trailing_ret.round(5) as trailing_ret
  80. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).trailing_ret == [-0.161847,-0.313497,-0.026577].round(5);
  81. @testing: case = 'ytd std_dev_a'
  82. assert (select (rtn.std_dev * sqrt(12)).round(4) as std_dev_a
  83. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).std_dev_a == [NULL ,0.415779,NULL].round(4);
  84. @testing: case = 'skewness'
  85. assert (select rtn.skewness.round(2) as skewness
  86. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).skewness == [NULL ,-0.644514,NULL].round(2);
  87. @testing: case = 'ytd kurtosis'
  88. assert (select rtn.kurtosis.round(3) as kurtosis
  89. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kurtosis == [NULL ,2.637834,NULL ].round(3);
  90. @testing: case = 'ytd wrst_month' // [FAIL] Java's bug? 2024.03M is missing
  91. assert (select rtn.wrst_month.round(4) as wrst_month
  92. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).wrst_month == [NULL ,-0.275002,NULL].round(4);
  93. @testing: case = 'ytd max drawdown' // values from swagger
  94. assert (select rtn.drawdown.round(4) as drawdown
  95. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).drawdown == [0, 0.180933, 0.008318].round(4);
  96. @testing: case = 'ytd calmar' // [FAIL] Java's bug? why calculate calmar when drawdown is 0
  97. assert (select rtn.calmar.round(4) as calmar
  98. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).calmar == [-0.453785,-0.949412,-0.667054].round(4);
  99. // since-inception-date standard indicators
  100. rtn = cal_basic_performance(entity_info, tb_ret, 'incep');
  101. @testing: case = 'incep return' // data from SWAGGER, SQL is NULL
  102. assert (select rtn.trailing_ret.round(4) as trailing_ret
  103. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).trailing_ret == [0.077187,-0.117711,0.074025].round(4);
  104. @testing: case = 'incep std_dev_a'
  105. assert (select (rtn.std_dev * sqrt(12)).round(2) as std_dev_a
  106. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).std_dev_a == [0.353238,0.341759,0.075581].round(2);
  107. @testing: case = 'incep skewness' // [FAIL] SLIGHTLY OFF
  108. assert (select rtn.skewness.round(3) as skewness
  109. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).skewness == [-0.005075, 0.093952 , -0.285354].round(3);
  110. @testing: case = 'incep kurtosis' // [FAIL] SLIGHTLY OFF
  111. assert (select rtn.kurtosis.round(2) as kurtosis
  112. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kurtosis == [0.308324,0.454231,0.797176].round(2);
  113. @testing: case = 'incep wrst_month'
  114. assert (select rtn.wrst_month.round(3) as wrst_month
  115. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).wrst_month == [-0.275322, -0.275002, -0.060459].round(3);
  116. @testing: case = 'incep drawdown' // values from swagger and comparing disabled while NAV is not available
  117. assert (select rtn.drawdown.round(4) as drawdown
  118. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).drawdown == [0.688703,0.705232,0.126529].round(4);
  119. @testing: case = 'incep var' // [FAIL]
  120. assert (select rtn.var.round(4) as var
  121. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).var == [0.154051,0.150854,0.038232].round(4);
  122. @testing: case = 'incep cvar' // [FAIL]
  123. assert (select rtn.cvar.round(4) as cvar
  124. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).cvar == [0.218524,0.218365,0.047898].round(4);
  125. @testing: case = 'incep calmar' // [FAIL]
  126. assert (select rtn.calmar.round(4) as calmar
  127. from rtn where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).calmar == [0.011767 , -0.045007, 0.095769].round(4);
  128. // trailing 2 year lpms
  129. lpms = cal_omega_sortino_kappa(tb_ret, risk_free_rate, '24');
  130. @testing: case = 'trailing 2y downside deviation' // [FAIL] data from SWAGGER, slightly off
  131. assert (select (lpms.ds_dev).round(2) as ds_dev
  132. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).ds_dev == [0.079688,0.076460,0.020911].round(2);
  133. @testing: case = 'trailing 2y omega' // [FAIL]
  134. assert (select lpms.omega.round(4) as omega
  135. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).omega == [0.353282,0.398373,0.872859].round(4);
  136. @testing: case = 'trailing 2y sortino' // [FAIL]
  137. assert (select (lpms.sortino * sqrt(12)).round(4) as sortino_a
  138. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sortino_a == [-0.873490,-1.453471,-0.080323].round(4);
  139. @testing: case = 'trailing 2y kappa' // [FAIL]
  140. assert (select lpms.kappa.round(4) as kappa
  141. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kappa == [-1.688884,-1.500436,-0.235215].round(4);
  142. // ytd lpms
  143. lpms = cal_omega_sortino_kappa(tb_ret, risk_free_rate, 'ytd');
  144. @testing: case = 'ytd downside deviation' // data from SWAGGER, slightly off
  145. assert (select (ds_dev).round(2) as ds_dev_a
  146. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).ds_dev_a == [NULL, 0.109539 , NULL].round(2);
  147. @testing: case = 'ytd omega' // [FAIL]
  148. assert (select lpms.omega.round(4) as omega
  149. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).omega == [NULL,0.453621,NULL].round(4);
  150. @testing: case = 'ytd sortino' // [FAIL]
  151. assert (select (lpms.sortino * sqrt(12)).round(4) as sortino_a
  152. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sortino_a == [NULL,-1.228103,NULL].round(4);
  153. @testing: case = 'ytd kappa' // [FAIL]
  154. assert (select lpms.kappa.round(4) as kappa
  155. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kappa == [NULL, -1.324956, NULL].round(4);
  156. // since inception lpms
  157. lpms = cal_omega_sortino_kappa(tb_ret, risk_free_rate, 'incep');
  158. @testing: case = 'incep downside deviation' // data from SWAGGER, slightly off
  159. assert (select (lpms.ds_dev)round(2) as ds_dev_a
  160. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).ds_dev_a == [0.070906,0.069713,0.016293].round(2);
  161. @testing: case = 'incep omega' // [FAIL] slightly off
  162. assert (select lpms.omega.round(4) as omega
  163. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).omega == [ 1.231226, 0.908086, 1.110811].round(4);
  164. @testing: case = 'incep sortino' // [FAIL]
  165. assert (select (lpms.sortino * sqrt(12)).round(4) as sortino_a
  166. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sortino_a == [0.590138,0.063619,0.179043].round(4);
  167. @testing: case = 'incep kappa' // [FAIL]
  168. assert (select lpms.kappa.round(4) as kappa
  169. from lpms where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).kappa == [ 0.510057 , -0.207099, 0.163405].round(4);
  170. // 2y bechmark tracking
  171. bmk_tracking = cal_benchmark_tracking(tb_ret, primary_benchmark, bmk_ret, '24');
  172. @testing: case = 'trailing2y win rate' // data from SWAGGER, so Java use what benchmark for portfolio?
  173. assert (select winrate.round(4) as winrate
  174. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).winrate == [0.458333,0.375,0.666667].round(4);
  175. @testing: case = 'trailing2y tracking error' // data from SWAGGER
  176. assert (select (track_error * sqrt(12)).round(3) as track_error_a
  177. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).track_error_a == [0.250859,0.219641,0.159818].round(3);
  178. @testing: case = 'trailing2y information ratio' // [FAIL]
  179. assert (select info.round(4) as info
  180. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).info == [2.672563 ,2.860281 ,10.190751].round(4);
  181. // ytd bechmark tracking
  182. bmk_tracking = cal_benchmark_tracking(tb_ret, primary_benchmark, bmk_ret, 'ytd');
  183. @testing: case = 'ytd win rate' // data from SWAGGER
  184. assert (select winrate.round(4) as winrate
  185. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).winrate == [NULL,0.25,NULL].round(4);
  186. @testing: case = 'ytd tracking error' // datav_end_date from SWAGGER
  187. assert (select (track_error * sqrt(12)).round(4) as track_error_a
  188. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).track_error_a == [NULL,0.276661,NULL].round(4);
  189. @testing: case = 'ytd information ratio' // [FAIL]
  190. assert (select info.round(4) as info
  191. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).info == [NULL,1.450085,NULL].round(4);
  192. // incep bechmark tracking
  193. bmk_tracking = cal_benchmark_tracking(tb_ret, primary_benchmark, bmk_ret, 'incep');
  194. @testing: case = 'incep win rate' // [FAIL] data from SWAGGER, slightly off (could be fixed by read return rather than calculate it)
  195. assert (select winrate.round(4) as winrate
  196. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).winrate == [0.557692,0.526315,0.566667].round(4);
  197. @testing: case = 'incep tracking error' // [FAIL] data from SWAGGER, slightly off
  198. assert (select (track_error * sqrt(12)).round(4) as track_error_a
  199. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).track_error_a == [0.304504,0.293106,0.154709].round(4);
  200. @testing: case = 'incep information ratio' // [FAIL]
  201. assert (select info.round(4) as info
  202. from bmk_tracking where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).info == [3.107132 ,3.118240 ,11.039744].round(4);
  203. // 2y alpha, beta
  204. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, '24');
  205. @testing: case = 'trailing2y beta'
  206. assert (select beta.round(4) as beta
  207. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).beta == [1.100336,1.024391,0.279759].round(4);
  208. @testing: case = 'trailing2y alpha' // [FAIL]
  209. assert (select (alpha * 12).round(3) as alpha_a
  210. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).alpha_a == [-0.173090,-0.273727,0.010882].round(3);
  211. // ytd alpha, beta
  212. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'ytd');
  213. @testing: case = 'ytd beta'
  214. assert (select beta.round(4) as beta
  215. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).beta == [NULL,2.332485,NULL].round(4);
  216. @testing: case = 'ytd alpha' // [FAIL]
  217. assert (select (alpha * 12).round(3) as alpha_a
  218. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).alpha_a == [NULL,-0.380200,NULL].round(3);
  219. // incep alpha, beta
  220. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'incep');
  221. @testing: case = 'incep beta'
  222. assert (select beta.round(4) as beta
  223. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).beta == [0.991306,1.012644,0.201240].round(4);
  224. @testing: case = 'incep alpha' // [FAIL]
  225. assert (select (alpha * 12).round(3) as alpha_a
  226. from alpha_beta where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).alpha_a == [0.077511,0.046797,0.017298].round(3);
  227. // 2y capture indicators
  228. capture_r = cal_capture_ratio(tb_ret, primary_benchmark, bmk_ret, '24');
  229. @testing: case = 'trailing2y upside capture return' // [FAIL]
  230. assert (select upside_capture_ret.round(4) as upside_capture_ret
  231. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ret == [-0.003743,-0.003743, 0.002156, 0.001314].round(4);
  232. @testing: case = 'trailing2y upside capture ratio' // [FAIL]
  233. assert (select upside_capture_ratio.round(4) as upside_capture_ratio
  234. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ratio == [-0.223084,-0.223084, 0.365611, 0.237662].round(4);
  235. @testing: case = 'trailing2y downside capture return' // [FAIL]
  236. assert (select downside_capture_ret.round(4) as downside_capture_ret
  237. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ret == [-0.022388,-0.023754,-0.004776,-0.004776].round(4);
  238. @testing: case = 'trailing2y downside capture ratio' // [FAIL]
  239. assert (select downside_capture_ratio.round(4) as downside_capture_ratio
  240. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ratio == [1.341475,1.391050,1.014519,1.014519].round(4);
  241. // ytd capture indicators
  242. capture_r = cal_capture_ratio(tb_ret, primary_benchmark, bmk_ret, 'ytd');
  243. @testing: case = 'ytd upside capture return'
  244. assert (select upside_capture_ret.round(4) as upside_capture_ret
  245. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ret == [0.027001,0.027001,0.003630,0.001464].round(4);
  246. @testing: case = 'ytd upside capture ratio'
  247. assert (select upside_capture_ratio.round(4) as upside_capture_ratio
  248. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ratio == [1.622489,1.622489,0.501649,0.224863].round(4);
  249. @testing: case = 'ytd downside capture return' // [FAIL] we don't have null values
  250. assert (select downside_capture_ret.round(4) as downside_capture_ret
  251. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ret == [-0.043629,-0.041610,null,null].round(4);
  252. @testing: case = 'ytd downside capture ratio' // [FAIL] we don't have null values
  253. assert (select downside_capture_ratio.round(4) as downside_capture_ratio
  254. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ratio == [3.491326,3.412813,null,null].round(4);
  255. // incep capture indicators
  256. capture_r = cal_capture_ratio(tb_ret, primary_benchmark, bmk_ret, 'incep');
  257. @testing: case = 'incep upside capture return'
  258. assert (select upside_capture_ret.round(3) as upside_capture_ret
  259. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ret == [0.039939,0.039939,0.003983,0.003744].round(3);
  260. @testing: case = 'incep upside capture ratio' // [FAIL] slightly off
  261. assert (select upside_capture_ratio.round(3) as upside_capture_ratio
  262. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).upside_capture_ratio == [1.411524,1.411524,0.644453,0.612097].round(3);
  263. @testing: case = 'incep downside capture return'
  264. assert (select downside_capture_ret.round(2) as downside_capture_ret
  265. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ret == [-0.044225,-0.044131,-0.002280,-0.002280].round(2);
  266. @testing: case = 'incep downside capture ratio' // [FAIL] slightly off
  267. assert (select downside_capture_ratio.round(4) as downside_capture_ratio
  268. from capture_r where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).downside_capture_ratio == [1.490631,1.497454,0.498609,0.498609].round(4);
  269. // 2y sharpe
  270. rtn = cal_basic_performance(entity_info, tb_ret, '24');
  271. sharpe = cal_sharpe(tb_ret, rtn, risk_free_rate, '24');
  272. @testing: case = 'trailing2y sharpe' // [FAIL]
  273. assert (select (sharpe *sqrt(12)).round(4) as sharpe_a
  274. from sharpe where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sharpe_a == [ -0.553607, -1.380549, -0.046602].round(4);
  275. // ytd sharpe
  276. rtn = cal_basic_performance(entity_info, tb_ret, 'ytd');
  277. sharpe = cal_sharpe(tb_ret, rtn, risk_free_rate, 'ytd');
  278. @testing: case = 'ytd sharpe'
  279. assert (select (sharpe *sqrt(12)).round(3) as sharpe_a
  280. from sharpe where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sharpe_a == [ NULL,-1.159093,NULL].round(3);
  281. // incep sharpe
  282. rtn = cal_basic_performance(entity_info, tb_ret, 'incep');
  283. sharpe = cal_sharpe(tb_ret, rtn, risk_free_rate, 'incep');
  284. @testing: case = 'incep sharpe' // [FAIL]
  285. assert (select (sharpe *sqrt(12)).round(4) as sharpe_a
  286. from sharpe where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).sharpe_a == [0.332846, 0.043442, 0.121411].round(4);
  287. // 2y treynor
  288. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, '24');
  289. treynor = cal_treynor(tb_ret, risk_free_rate, alpha_beta, '24');
  290. @testing: case = 'trailing2y treynor'
  291. assert (select treynor.round(2) as treynor
  292. from treynor where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).treynor == [-0.353583,-0.359078,-0.034126,-0.114400].round(2);
  293. // ytd treynor
  294. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'ytd');
  295. treynor = cal_treynor(tb_ret, risk_free_rate, alpha_beta, 'ytd');
  296. @testing: case = 'ytd treynor' // [FAIL]
  297. assert (select treynor.round(4) as treynor
  298. from treynor where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).treynor == [-0.024472,-0.043101,-0.016843,-0.005406].round(4);
  299. // incep treynor
  300. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'incep');
  301. treynor = cal_treynor(tb_ret, risk_free_rate, alpha_beta, 'incep');
  302. @testing: case = 'incep treynor' // [FAIL] slightly off
  303. assert (select treynor.round(4) as treynor
  304. from treynor where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).treynor == [0.035549,0.034146,0.026646,0.022403].round(4);
  305. // 2y jensen
  306. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, '24');
  307. jensen = cal_jensen(tb_ret, bmk_ret, risk_free_rate, alpha_beta, '24');
  308. @testing: case = 'trailing2y treynor'
  309. assert (select (jensen*12).round(2) as jensen_a
  310. from jensen where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).jensen_a == [-0.156508,-0.163600,-0.001910,-0.012240].round(2);
  311. // ytd jensen
  312. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'ytd');
  313. jensen = cal_jensen(tb_ret, bmk_ret, risk_free_rate, alpha_beta, 'ytd');
  314. @testing: case = 'ytd treynor' // [FAIL] slightly off if annulize it
  315. assert (select jensen.round(4) as jensen_a
  316. from jensen where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).jensen_a == [-0.127035,-0.119907,0.158975 ,0.041382].round(4);
  317. // incep jensen
  318. alpha_beta = cal_alpha_beta(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'incep');
  319. jensen = cal_jensen(tb_ret, bmk_ret, risk_free_rate, alpha_beta, 'incep');
  320. @testing: case = 'incep treynor' // [FAIL] slightly off
  321. assert (select (jensen*12).round(4) as jensen_a
  322. from jensen where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).jensen_a == [ 0.006436 , 0.005535 , -0.000363, -0.002461].round(4);
  323. // 2y m2
  324. m2 = cal_m2(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, '24');
  325. @testing: case = 'trailing2y m2' // [FAIL] slightly off
  326. assert (select (m2*12).round(4) as m2_a
  327. from m2 where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).m2_a == [-0.072046, -0.076473, 0.016349 , 0.012278].round(4);
  328. // ytd m2
  329. m2 = cal_m2(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'ytd');
  330. @testing: case = 'ytd m2' // [FAIL] slightly off
  331. assert (select (m2*12).round(4) as m2_a
  332. from m2 where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).m2_a == [0.002002, -0.015889, 0.021197, 0.015593].round(4);
  333. // incep m2
  334. m2 = cal_m2(tb_ret, primary_benchmark, bmk_ret, risk_free_rate, 'incep');
  335. @testing: case = 'incep m2' // [FAIL] slightly off
  336. assert (select (m2*12).round(4) as m2_a
  337. from m2 where entity_id in entity_ids and end_date in (2024.03M, 2024.08M)).m2_a == [0.064601,0.063365,0.026901,0.025155].round(4);
  338. /* Tests for BFI indicators. CURRENTLY IT IS UN-TESTABLE BECAUSE LOGIC OF BENCHMARK COMPARING IS CHANGED */
  339. /* codes from cal_fund_bfi_indicators */
  340. start_month = 1990.01M;
  341. // 取基金和基准的对照表
  342. bfi_benchmark = SELECT fund_id AS entity_id, end_date.temporalParse('yyyy-MM') AS end_date, factor_id AS benchmark_id
  343. FROM get_fund_bfi_factors(fund_ids, start_month.temporalFormat('yyyy-MM'), end_day.temporalFormat('yyyy-MM'));
  344. bfi_bmk_ret = get_benchmark_return(bfi_benchmark, end_day);