returnCalculator.dos 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. module fundit::returnCalculator
  2. use fundit::operationDataPuller
  3. use fundit::performanceDataPuller
  4. /*
  5. * 通用月收益计算
  6. *
  7. * @param entity_info <TABLE>: [COLUMNS] entity_id, price_date, inception_date, ini_value
  8. * @param nav <TABLE>: [COLUMNS] entity_id, price_date, cumulative_nav
  9. *
  10. */
  11. def cal_monthly_returns_by_nav(entity_info, mutable nav) {
  12. tb_rets = null;
  13. if(nav.isVoid() || nav.size() == 0 || entity_info.isVoid() || entity_info.size() == 0 ) return null;
  14. // 所有月末日期和净值
  15. tb_monthly_nav = SELECT entity_id, price_date.month().last() AS end_date, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
  16. FROM nav.sortBy!(['entity_id', 'price_date'], [1, 1])
  17. GROUP BY entity_id, price_date.month();
  18. // 筛掉成立日之前的净值
  19. tb_monthly_nav = SELECT n.entity_id, end_date, price_date, cumulative_nav
  20. FROM tb_monthly_nav n
  21. INNER JOIN entity_info ei ON n.entity_id = ei.entity_id
  22. WHERE n.price_date >= ei.inception_date;
  23. // 补回来成立日初始净值
  24. INSERT INTO tb_monthly_nav
  25. SELECT entity_id, inception_date.month(), inception_date, ini_value
  26. FROM entity_info
  27. WHERE inception_date IS NOT NULL;
  28. if(tb_monthly_nav.isVoid() || tb_monthly_nav.size() == 0) { return tb_rets; }
  29. // 计算月收益
  30. tb_rets = SELECT entity_id, end_date, price_date, cumulative_nav, cumulative_nav.ratios() - 1 AS ret
  31. FROM tb_monthly_nav.sortBy!(['entity_id', 'price_date'], [1, 1])
  32. CONTEXT BY entity_id;
  33. // the records without return calculated but do have nav are still useful for some calculations (e.g. max drawdown)
  34. return ( SELECT * FROM tb_rets WHERE cumulative_nav > 0 );
  35. }
  36. /*
  37. * 根据最新更新的净值计算收益,并与数据库历史收益合并为完整收益, 数据源是MySQL
  38. *
  39. * @param entity_type <STRING>:
  40. * @param entity_info <TABLE>: COLUMN NEED entity_id, inception_date, benchmark_id, ini_value, price_date
  41. * @param start_date <DATETIME>: 净值(datasource='nav')、收益起始日(datasource='perf')、净值更新的最新日期(datasource='mix')
  42. * @param data_source <STRING>: nav(nav table), perf(xxx_performance table), mix(new updated nav + old performance table ret_1m)
  43. *
  44. *
  45. */
  46. def mix_monthly_returns(entity_type, entity_info) {
  47. ret = null;
  48. very_old_day = 1990.01.01;
  49. end_day = today();
  50. s_json = (SELECT entity_id AS sec_id, price_date FROM entity_info).toStdJson();
  51. // 取基金组合在包括各自的某净值日期的前值及之后的所有净值
  52. tb_nav = get_nav_for_return_calculation(entity_type, 'm', s_json);
  53. tb_nav.rename!('sec_id', 'entity_id');
  54. // 计算某净值日期所在月份及之后的所有月收益
  55. ret = cal_monthly_returns_by_nav(entity_info, tb_nav);
  56. if(ret.isVoid() || ret.size() == 0) return ret;
  57. // 筛掉引入的前值,这些记录用来计算第一期收益后就不再有用
  58. // 不知道为什么 delete ret from ej(ret, tb_entities, 'entity_id') where ret.price_date < tb_entities.price_date 会报错
  59. ret = SELECT ret.* FROM ej(ret, entity_info, 'entity_id')
  60. WHERE ret.price_date >= entity_info.price_date;
  61. // 取数据库中的所有历史收益
  62. historical_rets = get_monthly_ret(entity_type, entity_info.entity_id, very_old_day, end_day, true);
  63. // MIX 将新NAV计算的收益和数据库中的历史收益合并,相同月份时用新计算的收益代替
  64. INSERT INTO ret
  65. SELECT entity_id, end_date.temporalParse('yyyy-MM'), price_date, nav, ret
  66. FROM historical_rets h
  67. WHERE NOT EXISTS ( SELECT * FROM ret WHERE entity_id = historical_rets.entity_id AND end_date = historical_rets.end_date.temporalParse('yyyy-MM') );
  68. // 过滤掉成立日之前的收益
  69. return SELECT ret.*
  70. FROM ret INNER JOIN entity_info ei ON ret.entity_id = ei.entity_id
  71. WHERE ret.end_date >= ei.inception_date.month()
  72. ORDER BY entity_id, end_date, price_date;
  73. }
  74. /*
  75. * 【作废】根据基金净值序列计算月收益序列(适合提供给指标运算)
  76. *
  77. * Create: 20240907 Joey
  78. * TODO: missing pulling data from local
  79. * TODO: ONLY support month return now
  80. *
  81. * @param entity_type <STRING>: NAV universe, 'HF','MF','PF','EQ'... defined in get_nav_table_description()
  82. * @param fund_ids <STRING VECTOR>: 基金ID
  83. * @param isFromMySQL <BOOL>: 净值来源 1 - 远程MySQL、 0 - 本地 DolphinDB
  84. *
  85. * Example: cal_fund_monthly_returns('HF', "'HF000004KN','HF000103EU','HF00018WXG'", true);
  86. *
  87. */
  88. def cal_fund_monthly_returns(entity_type, fund_ids, isFromMySQL){
  89. tb_rets = null;
  90. // 暂时只支持公私募和组合
  91. if(!(entity_type IN ['HF', 'MF', 'PF'])) return tb_rets;
  92. // 用于保证老基金也能取到所有历史净值
  93. very_old_price_date = 1990.01.01;
  94. // 基金基本信息,包括初始净值
  95. tb_fund_info = get_fund_info(fund_ids);
  96. // 基金净值
  97. tb_nav = SELECT * FROM get_nav_by_price_date(entity_type, fund_ids, very_old_price_date, isFromMySQL);
  98. tb_month_end = table(100:0, ['entity_id', 'price_date'], [STRING, DATE]);
  99. // 填充好各基金有效期内所有月份的最后一天
  100. for( f in tb_fund_info )
  101. {
  102. INSERT INTO tb_month_end SELECT fund_id AS entity_id, price_date FROM table(f.fund_id.take(1) AS fund_id).cj(table(temporalSeq(f.inception_date, today(), 'M') AS price_date)) ;
  103. }
  104. UPDATE tb_month_end SET end_date = price_date.month();
  105. tb_monthly_nav = SELECT entity_id, monthEnd(price_date).month().last() AS end_date, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
  106. FROM tb_nav
  107. GROUP BY entity_id, monthEnd(price_date);
  108. // 完整月末日期的净值序列(包括缺失数据为NULL)
  109. tb_monthly_nav = SELECT me.entity_id, me.end_date, n.price_date, n.cumulative_nav
  110. FROM tb_month_end me
  111. LEFT JOIN tb_monthly_nav n ON me.entity_id = n.entity_id AND me.end_date = n.end_date
  112. ORDER BY me.entity_id, me.end_date;
  113. // 补一下成立日的初始净值
  114. // NOTE: DolphinDB 遇见 EXISTS 语句时,似乎主表的 alias 失效,只好用全名
  115. INSERT INTO tb_monthly_nav
  116. SELECT fund_id AS entity_id, inception_date.month(), inception_date, ifNull(ini_value, 1)
  117. FROM tb_fund_info fi
  118. WHERE NOT EXISTS ( SELECT * FROM tb_monthly_nav n WHERE entity_id = tb_fund_info.fund_id AND n.price_date = tb_fund_info.inception_date);
  119. if(tb_monthly_nav.isVoid() || tb_monthly_nav.size() == 0) { return tb_rets; }
  120. // 算 ratios 之前先把时间顺序排好
  121. tb_monthly_nav.sortBy!(['entity_id', 'end_date', 'price_date'], [1, 1, 1]);
  122. // 计算月收益
  123. tb_rets = SELECT entity_id AS fund_id, end_date, price_date, cumulative_nav, cumulative_nav.ratios() - 1 AS ret
  124. FROM tb_monthly_nav
  125. CONTEXT BY entity_id;
  126. // the records without return calculated but do have nav are still useful for some calculations
  127. return ( SELECT * FROM tb_rets WHERE cumulative_nav > 0 );
  128. }
  129. /*
  130. * 根据基金净值序列计算周收益序列
  131. *
  132. * Create: 20240907 Joey
  133. * TODO: missing pulling data from local
  134. *
  135. * @param entity_type <STRING>: NAV universe, 'HF','MF','PF','EQ'... defined in get_nav_table_description()
  136. * @param entity_info <TABLE>: COLUMN NEED entity_id, price_date, inception_date
  137. *
  138. *
  139. */
  140. def cal_weekly_returns(entity_type, entity_info){
  141. tb_rets_1w = null;
  142. if(!(entity_type IN ['HF', 'MF', 'PF', 'MI', 'FI', 'FA'])) return tb_rets_1w;
  143. // 将每支证券ID+某个日期转为JSON用于调用sp
  144. s_json = (SELECT entity_id AS sec_id, price_date FROM entity_info).toStdJson();
  145. // 取基金组合在包括各自的某净值日期的前值及之后的所有净值
  146. tb_nav = get_nav_for_return_calculation(entity_type, 'w', s_json);
  147. tb_nav.rename!('sec_id', 'entity_id');
  148. UPDATE tb_nav // 当12月31日是周四、五、六时,该周为第52周,所以次年前几天有可能是上一年的第52,53周
  149. SET year_week = iif(price_date.weekOfYear() >= 52 && price_date.monthOfYear() == 1, price_date.year()-1, price_date.year())$STRING + (price_date.weekOfYear()$STRING).lpad(2, "0");
  150. tb_weekly_nav = SELECT entity_id, year_week, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
  151. FROM tb_nav n
  152. INNER JOIN entity_info ei ON n.entity_id = ei.entity_id
  153. WHERE n.price_date >= ei.inception_date // 筛掉成立日之前的净值
  154. GROUP BY entity_id, year_week
  155. ORDER BY entity_id, year_week;
  156. // 这里选最简单的计算方式:不补任何净值空洞,净值前值日期不做任何限制
  157. // TODO: 可以考虑将月收益也改为这种方式
  158. tb_rets_1w = SELECT entity_id, year_week, price_date, cumulative_nav, cumulative_nav.ratios()-1 AS ret_1w
  159. FROM tb_weekly_nav
  160. ORDER BY entity_id, year_week;
  161. return tb_rets_1w;
  162. }
  163. /*
  164. * [RETIRED] 月末 fund_performance 表计算
  165. *
  166. * @param fund_ids <STRING>: 逗号分隔的ID
  167. * @param end_date <STRING>: YYYY-MM
  168. *
  169. * Example: cal_fund_performance("'HF000004KN','HF00018WXG','HF000103EU'", '2024-06', true);
  170. */
  171. def cal_fund_performance(fund_ids, month_end) {
  172. // 获取必要的基金月度净值
  173. tb_nav = get_nav_for_hedge_fund_performance(fund_ids, month_end);
  174. tb_rets = SELECT fund_id, price_date.month() AS end_date, price_date, cumulative_nav,
  175. cumulative_nav \ nav_1m - 1 AS ret_1m,
  176. cumulative_nav \ nav_3m - 1 AS ret_3m,
  177. cumulative_nav \ nav_6m - 1 AS ret_6m,
  178. cumulative_nav \ nav_1y - 1 AS ret_1y,
  179. cumulative_nav \ nav_2y - 1 AS ret_2y,
  180. cumulative_nav \ nav_3y - 1 AS ret_3y,
  181. cumulative_nav \ nav_4y - 1 AS ret_4y,
  182. cumulative_nav \ nav_5y - 1 AS ret_5y,
  183. cumulative_nav \ nav_10y - 1 AS ret_10y,
  184. cumulative_nav \ nav_ytd - 1 AS ret_ytd,
  185. cumulative_nav \ nav_incep - 1 AS ret_incep, inception_date
  186. FROM tb_nav;
  187. // NOTE: this is to keep consistance with MySQL, even it is NOT complied with GIPS standard
  188. UPDATE tb_rets SET ret_1m_a = (1 + ret_1m).pow(12\1) - 1, ret_3m_a = (1 + ret_3m).pow(12\3) - 1, ret_6m_a = (1 + ret_6m).pow(12\6) - 1,
  189. ret_1y_a= ret_1y, ret_2y_a = (1 + ret_2y).pow(12\24) - 1, ret_3y_a = (1 + ret_3y).pow(12\36) - 1,
  190. ret_4y_a = (1 + ret_4y).pow(12\48) - 1, ret_5y_a = (1 + ret_5y).pow(12\60) - 1, ret_10y_a = (1 + ret_10y).pow(12\120) - 1,
  191. ret_ytd_a = (1 + ret_ytd).pow(12\int(temporalFormat(end_date, 'MM')))-1,
  192. ret_incep_a = (1 + ret_incep).pow(12\(end_date - inception_date.month())) - 1,
  193. ret_incep_a_all = (1 + ret_incep).pow(12\(end_date - inception_date.month()))- 1,
  194. ret_incep_a_gips = iif(end_date - inception_date.month() < 12, ret_incep,
  195. (1 + ret_incep).pow(12\(end_date - inception_date.month()))- 1);
  196. return tb_rets;
  197. }
  198. /*
  199. * [RETIRED] 批量计算公募历史基金月度收益(fund_performance)
  200. * NOTE: 任何数据频率快于或等于月度的净值数据都可以用此函数一次性计算完整历史记录。双月频、季频甚至更低频率的基金只能按月计算
  201. *
  202. * cal_mutual_fund_performance("'HF000004KN','HF00018WXG','HF000103EU'", true)
  203. *
  204. */
  205. def cal_mutual_fund_performance(fund_ids, isFromMySQL) {
  206. // 计算月收益
  207. tb_tmp = cal_fund_monthly_returns('MF', fund_ids, isFromMySQL);
  208. tb_rets = SELECT fund_id, end_date, ret_1m,
  209. (1 + ret_1m).mprod(3) - 1 AS ret_3m, (1 + ret_1m).mprod(6) - 1 AS ret_6m, (1 + ret_1m).mprod(12) - 1 AS ret_1y,
  210. (1 + ret_1m).mprod(24) - 1 AS ret_2y, (1 + ret_1m).mprod(36) - 1 AS ret_3y, (1 + ret_1m).mprod(48) - 1 AS ret_4y,
  211. (1 + ret_1m).mprod(60) - 1 AS ret_5y, (1 + ret_1m).mprod(120) - 1 AS ret_10y
  212. FROM tb_tmp
  213. CONTEXT BY fund_id;
  214. // NOTE: this is to keep consistance with MySQL, even it is NOT complied with GIPS standard
  215. UPDATE tb_rets SET ret_1m_a = (1 + ret_1m).pow(12) - 1, ret_3m_a = (1 + ret_3m).pow(4) - 1, ret_6m_a = (1 + ret_6m).pow(2) - 1, ret_1y_a= ret_1y,
  216. ret_2y_a = (1 + ret_2y).pow(1\2) - 1, ret_3y_a = (1 + ret_3y).pow(1\3) - 1, ret_4y_a = (1 + ret_4y).pow(1\4) - 1,
  217. ret_5y_a = (1 + ret_5y).pow(1\5) - 1, ret_10y_a = (1 + ret_10y).pow(1\10) - 1;
  218. // ytd 不会用上面的CONTEXT BY语句实现
  219. tb_ret_ytd = SELECT a.fund_id, a.end_date, a.price_date, a.cumulative_nav, -1 + a.cumulative_nav \ b.cumulative_nav AS ret_ytd,
  220. (a.cumulative_nav \ b.cumulative_nav).pow(12\(a.end_date - b.end_date)) - 1 AS ret_ytd_a
  221. FROM tb_rets a
  222. INNER JOIN tb_rets b ON a.fund_Id = b.fund_id
  223. AND b.end_date = a.price_date.yearEnd().datetimeAdd(-1y).month()
  224. // since inception 不会用上面的CONTEXT BY语句实现
  225. tb_ret_incep = SELECT a.fund_id, a.end_date, a.price_date, cumulative_nav, -1 + cumulative_nav \ ini_value AS ret_incep
  226. FROM tb_rets a
  227. INNER JOIN tb_fund_info fi ON a.fund_id = fi.fund_id
  228. UPDATE tb_ret_incep SET ret_incep_a = (1 + ret_incep).pow(12\(end_date - end_date.first())) - 1 CONTEXT BY fund_Id
  229. UPDATE tb_ret_incep SET ret_incep_a_gips = iif( end_date - end_date.first() < 12, ret_incep, ret_incep_a ), ret_incep_a_all = ret_incep_a CONTEXT BY fund_id
  230. // 只选需要更新的记录
  231. tb_fund_performance = SELECT a.fund_id, a.end_date.datetimeFormat("yyyy-MM") AS end_date, c.price_date, c.cumulative_nav,
  232. a.ret_1m, a.ret_1m_a, a.ret_3m, a.ret_3m_a, a.ret_6m, a.ret_6m_a,
  233. a.ret_1y, a.ret_1y_a, a.ret_2y, a.ret_2y_a, a.ret_3y, a.ret_3y_a,
  234. a.ret_4y, a.ret_4y_a, a.ret_5y, a.ret_5y_a, a.ret_10y, a.ret_10y_a,
  235. b.ret_ytd, b.ret_ytd_a, c.ret_incep, c.ret_incep_a, c.ret_incep_a_all, c.ret_incep_a_gips
  236. // , 123 AS creatorid, now() AS createtime, 123 AS updaterid, now() AS updatetime, 1 AS isvalid
  237. FROM tb_rets a
  238. LEFT JOIN tb_ret_ytd b ON a.fund_id = b.fund_id AND a.end_date = b.end_date
  239. LEFT JOIN tb_ret_incep c ON a.fund_id = c.fund_id AND a.end_date = c.end_date
  240. WHERE c.price_date IS NOT NULL
  241. ORDER BY a.fund_id, c.price_date
  242. return tb_fund_performance
  243. }
  244. /*
  245. * 批量计算区间收益
  246. * TODO: mySQL version 向前取4天,向后不做限制。这里的逻辑是向前取4个交易日, 遇到大长假后可能取不到数据
  247. * 【老程序,可优化】
  248. *
  249. * get_trailing_return(tb_last_nav, tb_nav, -7d, "ret_1w")
  250. *
  251. */
  252. def get_trailing_return(table_last_nav, table_nav, duration, return_column_name) {
  253. tb = SELECT a.entity_id, a.price_date.last() AS price_date, a.cumulative_nav.last() \ b.cumulative_nav.last() - 1 AS ret
  254. FROM table_last_nav a
  255. INNER JOIN table_nav b ON a.entity_id = b.entity_id
  256. WHERE b.price_date <= a.price_date.datetimeAdd(duration)
  257. AND b.price_date >= a.price_date.datetimeAdd(duration).datetimeAdd(-4d).businessDay()
  258. GROUP BY a.entity_id
  259. ORDER BY entity_id;
  260. tb.rename!("ret", return_column_name);
  261. return tb;
  262. }
  263. /*
  264. * 批量计算最新收益
  265. *
  266. 【老程序,可优化】
  267. *
  268. */
  269. def cal_latest_performance(entity_type, entity_info, isFromMySQL) {
  270. // 用于保证老基金也能取到所有历史净值
  271. very_old_price_date = 1990.01.01
  272. tb_nav = get_nav_by_price_date(entity_type, entity_info.entity_id, very_old_price_date, isFromMySQL)
  273. tb_last_nav = SELECT entity_id, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
  274. FROM tb_nav
  275. GROUP BY entity_id
  276. ORDER BY entity_id
  277. // 近1期收益,对应mySQL fund_latest_nav_performance 中的 net_value_change
  278. // 因为是倒序,所以算出来的 ratios() = n0 / n1, 要把它改换成 n1 / n0 - 1 的收益
  279. tb_last_return = SELECT TOP 2 entity_id, price_date.first() AS price_date, price_date AS pre_preice_date,
  280. nav.first() AS nav, cumulative_nav.first() AS cumulative_nav, 1\cumulative_nav.ratios() - 1 AS net_value_change
  281. FROM ( SELECT * FROM tb_nav ORDER BY price_date DESC )
  282. CONTEXT BY entity_id
  283. ORDER BY entity_id
  284. tb_last_return = SELECT * FROM tb_last_return WHERE net_value_change IS NOT NULL
  285. // 近1交易日收益
  286. tb_1d = SELECT a.entity_id, a.price_date, a.cumulative_nav \ b.cumulative_nav - 1 AS ret_1d
  287. FROM tb_last_nav a
  288. INNER JOIN tb_nav b ON a.entity_id = b.entity_id AND b.price_date = a.price_date.datetimeAdd(-1d).businessDay()
  289. ORDER BY entity_id
  290. // 近1周、1/3/6月、1/2/3/4/5/10年收益
  291. tb_1w = get_trailing_return(tb_last_nav, tb_nav, -7d, "ret_1w")
  292. tb_1m = get_trailing_return(tb_last_nav, tb_nav, -1M, "ret_1m")
  293. tb_3m = get_trailing_return(tb_last_nav, tb_nav, -3M, "ret_3m")
  294. tb_6m = get_trailing_return(tb_last_nav, tb_nav, -6M, "ret_6m")
  295. tb_1y = get_trailing_return(tb_last_nav, tb_nav, -1y, "ret_1y")
  296. tb_2y = get_trailing_return(tb_last_nav, tb_nav, -2y, "ret_2y")
  297. tb_3y = get_trailing_return(tb_last_nav, tb_nav, -3y, "ret_3y")
  298. tb_4y = get_trailing_return(tb_last_nav, tb_nav, -4y, "ret_4y")
  299. tb_5y = get_trailing_return(tb_last_nav, tb_nav, -5y, "ret_5y")
  300. tb_10y = get_trailing_return(tb_last_nav, tb_nav, -10y, "ret_10y")
  301. // ytd return
  302. tb_ytd = SELECT a.entity_id, a.price_date.last() AS price_date, a.cumulative_nav.last() \ b.cumulative_nav.last() - 1 AS ret_ytd
  303. FROM tb_last_nav a
  304. INNER JOIN tb_nav b ON a.entity_id = b.entity_id
  305. WHERE b.price_date < a.price_date.yearBegin()
  306. AND b.price_date >= a.price_date.yearBegin().datetimeAdd(-4d)
  307. GROUP BY a.entity_id
  308. // since inception return
  309. tb_incep = SELECT a.entity_id, a.price_date, -1 + cumulative_nav \ ini_value AS ret_incep, fi.inception_date
  310. FROM tb_last_nav a
  311. INNER JOIN entity_info fi ON a.entity_id = fi.entity_id
  312. // annulized since reception return following GIPS rule
  313. UPDATE tb_incep SET ret_incep_a = iif((price_date-inception_date)<=365, ret_incep, (1 + ret_incep).pow(365.25\(price_date-inception_date)) - 1)
  314. UPDATE tb_incep SET ret_incep_a_all = ret_incep_a,
  315. ret_incep_a_gips = ret_incep_a
  316. // 最大回撤
  317. tb_drawdown_1m = SELECT a.entity_id, max( 1 - b.cumulative_nav \ b.cumulative_nav.cummax() ) AS drawdown_1m
  318. FROM tb_last_return a
  319. INNER JOIN tb_nav b ON a.entity_id = b.entity_id
  320. WHERE b.price_date >= a.price_date.datetimeAdd(-1M)
  321. GROUP BY a.entity_id
  322. tb_drawdown_3m = SELECT a.entity_id, max( 1 - b.cumulative_nav \ b.cumulative_nav.cummax() ) AS drawdown_3m
  323. FROM tb_last_return a
  324. INNER JOIN tb_nav b ON a.entity_id = b.entity_id
  325. WHERE b.price_date >= a.price_date.datetimeAdd(-3M)
  326. GROUP BY a.entity_id
  327. tb_drawdown_1y = SELECT a.entity_id, max( 1 - b.cumulative_nav \ b.cumulative_nav.cummax() ) AS drawdown_1y
  328. FROM tb_last_return a
  329. INNER JOIN tb_nav b ON a.entity_id = b.entity_id
  330. WHERE b.price_date >= a.price_date.datetimeAdd(-1y)
  331. GROUP BY a.entity_id
  332. tb_drawdown_incep = SELECT entity_id, max( 1 - cumulative_nav \ cumulative_nav.cummax() ) AS drawdown_incep
  333. FROM tb_nav GROUP BY entity_id
  334. tb_rets = SELECT a.entity_id, a.price_date.datetimeFormat("yyyy-MM") AS end_date, a.price_date, a.pre_preice_date, a.nav, a.cumulative_nav,
  335. a.net_value_change, d1.ret_1d, w1.ret_1w, m1.ret_1m, m3.ret_3m, m6.ret_6m,
  336. y1.ret_1y, y2.ret_2y, y3.ret_3y, y4.ret_4y, y5.ret_5y, y10.ret_10y,
  337. ytd.ret_ytd, incep.ret_incep, incep.ret_incep_a, incep.ret_incep_a_all, incep.ret_incep_a_gips,
  338. dd_m1.drawdown_1m AS maxdrawdown_1m, dd_m3.drawdown_3m AS maxdrawdown_3m, dd_y1.drawdown_1y AS maxdrawdown_1y,
  339. dd_incep.drawdown_incep AS maxdrawdown_incep,
  340. iif(dd_incep.drawdown_incep.round(4) == 0 || incep.ret_incep_a > 1000, NULL, incep.ret_incep_a \ dd_incep.drawdown_incep) AS calmarratio_incep
  341. FROM tb_last_return a
  342. LEFT JOIN tb_1d d1 ON a.entity_id = d1.entity_id
  343. LEFT JOIN tb_1w w1 ON a.entity_id = w1.entity_id
  344. LEFT JOIN tb_1m m1 ON a.entity_id = m1.entity_id
  345. LEFT JOIN tb_3m m3 ON a.entity_id = m3.entity_id
  346. LEFT JOIN tb_6m m6 ON a.entity_id = m6.entity_id
  347. LEFT JOIN tb_1y y1 ON a.entity_id = y1.entity_id
  348. LEFT JOIN tb_2y y2 ON a.entity_id = y2.entity_id
  349. LEFT JOIN tb_3y y3 ON a.entity_id = y3.entity_id
  350. LEFT JOIN tb_4y y4 ON a.entity_id = y4.entity_id
  351. LEFT JOIN tb_5y y5 ON a.entity_id = y5.entity_id
  352. LEFT JOIN tb_10y y10 ON a.entity_id = y10.entity_id
  353. LEFT JOIN tb_ytd ytd ON a.entity_id = ytd.entity_id
  354. LEFT JOIN tb_incep incep ON a.entity_id = incep.entity_id
  355. LEFT JOIN tb_drawdown_1m dd_m1 ON a.entity_id = dd_m1.entity_id
  356. LEFT JOIN tb_drawdown_3m dd_m3 ON a.entity_id = dd_m3.entity_id
  357. LEFT JOIN tb_drawdown_1y dd_y1 ON a.entity_id = dd_y1.entity_id
  358. LEFT JOIN tb_drawdown_incep dd_incep ON a.entity_id = dd_incep.entity_id
  359. ORDER BY a.entity_id
  360. // 忽略掉非GIPS标准的所有年化收益字段(包括ytd_a)
  361. UPDATE tb_rets SET ret_1y_a = ret_1y, ret_2y_a = (1 + ret_2y).pow(1\2) - 1, ret_3y_a = (1 + ret_3y).pow(1\3) - 1,
  362. ret_4y_a = (1 + ret_4y).pow(1\4) - 1, ret_5y_a = (1 + ret_5y).pow(1\5) - 1, ret_10y_a = (1 + ret_10y).pow(1\10) - 1
  363. return tb_rets
  364. }