sample_rbsa.dos 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. login(`admin, `123456)
  2. loadPlugin("ODBC")
  3. go
  4. clearCachedModules()
  5. use fundit::fundCalculator
  6. use fundit::dataPuller
  7. /* Sample 1: 单基金或组合RBSA ------------------------------ */
  8. IN00000008 = (0.007075 -0.007000 -0.002121 0.008571 -0.025775 0.018936 0.012031 0.005613 0.017171 0.003189 -0.020797 -0.005986);
  9. IN00000077 = (-0.002598 0.001488 0.001404 0.001031 0.002389 0.002999 -0.000246 -0.000533 -0.000656 0.001313 0.001352 0.001514);
  10. IN0000007G =(0.000373 0.000367 0.000379 0.000272 0.000461 0.000355 0.000349 0.000195 0.000478 0.000331 0.000330 0.000324);
  11. IN0000009M = (0.010477 0.010086 0.019914 0.027885 0.061534 0.011593 -0.032239 -0.003192 0.011932 0.024807 0.006475 0.014500);
  12. mt0 = table(IN00000008, IN00000077, IN0000007G, IN0000009M);
  13. MF00003PW1 = (0.017450 0.002639 -0.026316 -0.005405 -0.013587 -0.001377 0.024828 0.014805 0.011936 -0.003932 -0.021053 0.006721);
  14. res = cal_rbsa(MF00003PW1, mt0, false)
  15. // lb = 0 时仍有微小负数,估计可以换成0
  16. // 不清除后面的12维数字是干啥的
  17. beta = res[1][0:4]
  18. print(beta)
  19. /* Sample 1 end --------------------------------------------- */
  20. /* Sample 2 start ------------------------------ */
  21. // pull all historical weekly returns from mysql, then save it to local
  22. // tb_all = get_fund_weekly_rets(null, 2021.07.01, null, true)
  23. // save_table(tb_all, "mfdb.fund_performance_weekly", false)
  24. // pull historical weekly returns from pre-saved local table
  25. // NOTE: if the table doesn't exist, run the commented code above
  26. tb_all_weekly_ret = load_table_from_local("fundit", "mfdb.fund_performance_weekly")
  27. // get index weekly return based on RBSA asset_type
  28. def get_standard_rbsa_index_return(asset_type_id, start_date, end_date) {
  29. index_ids = ""
  30. // asset allocation: stock-bond-cash-gold
  31. if(asset_type_id == "AS0000005Q")
  32. index_ids = "'IN00000008','IN00000077','IN0000007G','IN0000009M'"
  33. // asset allocation: stock-bond-cash-gold
  34. else if(asset_type_id == "Large4Assets" )
  35. index_ids = "'IN00000008','IN00000077','IN0000007G','IN0000009M'"
  36. // Fundit style: large-small-cash
  37. else if(asset_type_id == "Cap3Style" )
  38. index_ids = "'FA00000WKG','FA00000WKH','IN0000007G'"
  39. // Fundit sector: 防守-周期-敏感-科技-cash
  40. else if(asset_type_id == "CSI5" )
  41. index_ids = "'FA00000VML','FA00000VMM','FA00000VMN','FA00000VMO','IN0000007G'"
  42. // CNI style: LG-LV-SG-SV-cash
  43. else if(asset_type_id == "CNI5Style" )
  44. index_ids = "'IN0000000S','IN0000000T','IN0000000U','IN0000000V','IN0000007G'"
  45. // CNI style: LG-LV-SG-SV-MG-MV-cash
  46. else if(asset_type_id == "CNI7Style" )
  47. index_ids = "'IN0000000S','IN0000000T','IN0000000U','IN0000000V','IN0000000W', 'IN0000000X','IN0000007G'"
  48. // CSI old sector: 材料-电信-工业-公用-金融-可选-能源-消费-信息-医药-cash
  49. else if(asset_type_id == "CSI11" )
  50. index_ids = "'IN0000000Y','IN0000000Z','IN00000010','IN00000011','IN00000012','IN00000013','IN00000014','IN00000015','IN00000016','IN00000017','IN0000007G'"
  51. // bond type: gov-cash-cnvt-corp
  52. else if(asset_type_id == "BondType" )
  53. index_ids = "'IN0000007A','IN0000007G','IN0000008J','IN000002CM'"
  54. ret = get_index_weekly_rets(index_ids, start_date, end_date)
  55. return ret
  56. }
  57. win = 24
  58. step = 24
  59. // pick the fund to be compared with
  60. the_fund = 'MF00003PW1'
  61. the_fund_ret = (SELECT * FROM tb_all_weekly_ret WHERE year_week >= '202211' AND fund_id = the_fund).ret_1w
  62. // get all mutual fund ids for testing
  63. fund_ids = (SELECT DISTINCT fund_id FROM tb_all_weekly_ret).fund_id
  64. // all rbsa results will be saved here
  65. tb_all_rbsa = table(fund_ids.size()*(the_fund_ret.size()):0, "fund_id" "date" "asset_type_id" "weights", [STRING, STRING, STRING, STRING])
  66. // all the distances will be saved here
  67. tb_dis = table(fund_ids.size():0, "fund_id" "date" "asset_type_id" "dist", [STRING, STRING, STRING, DOUBLE])
  68. // asset_type_id = "Cap3Style"
  69. asset_type = "CSI5"
  70. tb_index_raw_ret = get_standard_rbsa_index_return(asset_type, 2021.07.01, 2024.07.26)
  71. // transform the data structure for rbsa calculation
  72. tb_index_weekly_ret = SELECT ret_1w
  73. FROM tb_index_raw_ret
  74. PIVOT BY year_week, index_id
  75. tb_index_weekly_ret.rename!("year_week", "date")
  76. // loop thru the fund list and calculate historical rbsa
  77. // NOTE: it takes about 6 minutes for 20,000 mutual funds with 3 year history
  78. for(f_id in fund_ids) {
  79. ret = SELECT year_week AS date, ret_1w AS ret
  80. FROM tb_all_weekly_ret
  81. WHERE fund_id = f_id
  82. ORDER BY year_week
  83. // calculate historical rbsa
  84. tb = cal_rolling_rbsa(ret, tb_index_weekly_ret, false, win, step)
  85. INSERT INTO tb_all_rbsa
  86. SELECT f_id, date, asset_type, weights
  87. FROM tb
  88. WHERE status = "solved"
  89. }
  90. SELECT * FROM tb_all_rbsa WHERE fund_id = the_fund
  91. // tb_all_weekly_ret = null
  92. // save to local db, just for the sake of saving time to run rbsa again
  93. save_table(tb_all_rbsa, "mfdb.fund_rbsa_weekly", false)
  94. tb_all_rbsa = null
  95. asset_type = "CSI5"
  96. the_dates = (SELECT DISTINCT date FROM tb_all_rbsa WHERE fund_id = the_fund AND asset_type_id = asset_type).date
  97. // loop thru all the dates
  98. for(d in the_dates) {
  99. // the rbsa weights of target fund of one date
  100. the_weights = (SELECT * FROM tb_all_rbsa WHERE fund_id = the_fund AND date = d AND asset_type_id = asset_type).weights[0]
  101. // all rbsa results with the same date
  102. t = SELECT * FROM tb_all_rbsa WHERE date = d AND asset_type_id = asset_type
  103. // calculate the distance between the target fund and any fund
  104. for(r in t) {
  105. // euclidean distance
  106. tb_dis.tableInsert(r.fund_id, d, asset_type, ((the_weights.split(" ")$DOUBLE).euclidean(r.weights.split(" ")$DOUBLE)).round(4))
  107. // tanimoto distance
  108. // tb_dis.tableInsert(r.fund_id, d, asset_type, ((the_weights.split(" ")$DOUBLE).tanimoto(r.weights.split(" ")$DOUBLE)).round(4))
  109. }
  110. }
  111. select * from tb_dis where fund_id = the_fund
  112. n = the_dates.size()
  113. select fund_id, avg
  114. from (
  115. select fund_id, count(dist) as cnt, avg(dist) as avg
  116. from tb_dis
  117. where fund_id <> the_fund
  118. and asset_type_id = "Large4Assets"
  119. group by fund_id )
  120. order by avg
  121. limit 500
  122. select fund_Id, avg from (
  123. select fund_id, sum(dist) as total_dist, count(dist) as cnt, avg(dist) as avg
  124. from tb_dis
  125. where fund_id <> the_fund and asset_type_id <> "Cap3Style"
  126. group by fund_id )
  127. where cnt = n*2
  128. order by avg
  129. limit 500
  130. select * from tb_all_rbsa where fund_id IN ('MF00003PW1', 'MF00006EQ6', 'MF00003QZR','MF000074ZM','MF00006FQ5')
  131. t = select ret_1w from tb_all_weekly_ret where fund_id IN ('MF00003PW1', 'MF00006EQ6', 'MF00003QZR','MF000074ZM','MF00006FQ5') and year_week >= '202211' pivot by year_week, fund_id
  132. plot([t.MF00003PW1, t.MF00006EQ6], t.year_week, , LINE)
  133. plot([t.MF00003PW1, t.MF00003QZR], t.year_week, , LINE)
  134. plot([t.MF00003PW1, t.MF000074ZM], t.year_week, , LINE)
  135. plot([t.MF00003PW1, t.MF00006FQ5], t.year_week, , LINE)
  136. // calculate correlation
  137. t = (SELECT ret_1w FROM tb_all_weekly_ret WHERE year_week >= '202211' PIVOT BY year_week, fund_id ).dropColumns!("year_week")
  138. v_cols = t.colNames()
  139. m = matrix(t)
  140. tb_corr = table(fund_ids.size():0, "fund_id" "corr", [STRING, DOUBLE])
  141. i = 0
  142. for(c in m) {
  143. if(c.dropna().size() == the_fund_ret.rows()) {
  144. tb_corr.tableInsert(v_cols[i], the_fund_ret.corr(c).round(6))
  145. }
  146. i += 1
  147. }
  148. select * from tb_corr order by corr desc limit 500
  149. /* Sample 2 end --------------------------------------------- */