QDII 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. # 克隆自聚宽文章:https://www.joinquant.com/post/52938
  2. # 标题:QDII ETF/LOF 折溢价策略
  3. # 作者:Gyro^.^
  4. # 实际地址:https://www.joinquant.com/algorithm/index/edit?algorithmId=9608e6e01e60c8b9c1e0d8521b10882d
  5. import pandas as pd
  6. import datetime as dt
  7. from jqdata import *
  8. def initialize(context):
  9. # setting system
  10. log.set_level('order', 'error')
  11. set_option('use_real_price', True)
  12. set_option('avoid_future_data', True)
  13. # setting strategy
  14. run_daily(iUpdate, 'before_open')
  15. run_daily(iTrader, 'every_bar')
  16. run_daily(iReport, 'after_close')
  17. def iUpdate(context):
  18. # parameters
  19. keys = ['标普', '美国', '纳指', '纳斯达克', '法国', '德国', \
  20. '日经', '亚太', '东南亚', '印度', '沙特',]
  21. black_list = ['562060.XSHG']
  22. # all funds
  23. dt_last = context.previous_date
  24. all_fund = get_all_securities('fund', dt_last)
  25. # onlist 1-month
  26. dt_1m = dt_last - dt.timedelta(days=30)
  27. funds = all_fund[all_fund.start_date < dt_1m].index.tolist()
  28. # filter, liquity
  29. hm = history(20, '1d', 'money', funds).mean()
  30. funds = hm[hm > 1e6].index.tolist()
  31. # key funds
  32. funds = [s for k in keys for s in funds if k in all_fund.display_name[s]]
  33. # black_list
  34. funds = [s for s in funds if s not in black_list]
  35. # net value
  36. value = get_extras('unit_net_value', funds, end_date=dt_last, count=2).iloc[0]
  37. # price
  38. price = history(1, '1d', 'close', funds).iloc[0]
  39. # low price
  40. r = price/value - 1.0
  41. r = r[r < 0.15].sort_values()
  42. # choice funds
  43. g.ratio = r
  44. log.info('funds', len(g.ratio))
  45. def iTrader(context):
  46. # load data
  47. r = g.ratio
  48. position_size = 1.0/max(3, len(g.ratio)) * context.portfolio.total_value
  49. lm_value = 0.8*position_size
  50. hm_value = 1.3*position_size
  51. cash_size = 0.1 * context.portfolio.total_value
  52. cdata = get_current_data()
  53. # sell
  54. for s in context.portfolio.positions:
  55. if cdata[s].paused:
  56. continue
  57. if s not in r.index:
  58. log.info('sell', s, cdata[s].name)
  59. order_target(s, 0, MarketOrderStyle(0.99*cdata[s].last_price))
  60. # buy stocks
  61. for s in r.index:
  62. if context.portfolio.available_cash < cash_size:
  63. break
  64. if cdata[s].paused:
  65. continue
  66. if s not in context.portfolio.positions:
  67. if r[s] < 0:
  68. log.info('buy', s, cdata[s].name)
  69. order_target_value(s, position_size, MarketOrderStyle(1.01*cdata[s].last_price))
  70. elif context.portfolio.positions[s].value < lm_value:
  71. if r[s] < 0:
  72. log.info('balance+', s, cdata[s].name)
  73. order_target_value(s, position_size, MarketOrderStyle(1.01*cdata[s].last_price))
  74. elif context.portfolio.positions[s].value > hm_value:
  75. if r[s] > 0:
  76. log.info('balance-', s, cdata[s].name)
  77. order_target_value(s, position_size, MarketOrderStyle(0.99*cdata[s].last_price))
  78. def iReport(context):
  79. # load data
  80. cdata = get_current_data()
  81. tvalue = context.portfolio.total_value
  82. # table of positions
  83. ptable = pd.DataFrame(columns=['amount', 'value', 'weight', 'name'])
  84. for s in context.portfolio.positions:
  85. ps = context.portfolio.positions[s]
  86. ptable.loc[s] = [ps.total_amount, int(ps.value), 100*ps.value/tvalue, cdata[s].name]
  87. ptable = ptable.sort_values(by='weight', ascending=False)
  88. # daily report
  89. pd.set_option('display.max_rows', None)
  90. log.info(' positions', len(ptable), '\n', ptable.head(10))
  91. log.info(' total value %.2f, cash %.2f', \
  92. context.portfolio.total_value/10000, context.portfolio.available_cash/10000)
  93. # end