量化投资策略与技术学习PART5:量化选股之资金流

量化投资策略与技术学习PART5:量化选股之资金流在市场中 经常存在交易性机会 这是指股价在短期内可能受到某些消息的影响 或者某些市场内在因素的改变从而产生剧烈波动带来的价差投资机会

大家好,欢迎来到IT知识分享网。

在市场中,经常存在交易性机会,这是指股价在短期内可能受到某些消息的影响,或者某些市场内在因素的改变从而产生剧烈波动带来的价差投资机会。其中一个经典的交易性策略就是资金流模型,该模型使用资金流流向来判断股票在未来一段时间的涨跌情况。如果是资金流入的股票,则股价在未来一段时间可能会上涨;如果是资金流出的股票,则股价在未来一段时间可能会下跌。那么,根据资金流向就可以构建相应的投资策略。

1、基本概念

资金流是一种反映股票供求关系的指标。传统的量价无法区分市场微观结构中的流动性和私有信息的影响,而根据委托测算的资金流,能够有效地观察微观市场交易者的真实意图及对股价造成的影响。
资金流定义如下:证券价格在约定的时间段中处于上升状态时产生的交易额是推动指数上涨的力量,这部分成交额被定义为资金流入;证券价格在约定的时间段中下跌时的成交额是推动指数下跌的力量,这部分成交额被定义为资金流出;若证券价格在约定的时间段前后没有发生变化,则这段时间中的成交额不计入资金流量。当天资金流入和资金流出的差额可以认为是该证券当天买卖两者力量相抵之后,推动价格变化的净作用量,被定义为当天资金净流量。
数量化定义如下:
M o n e y F l o w = ∑ i = 1 n ( V o l u m e   i ) × P i P i − P i − 1 ∣ P i − P i − 1 ∣ MoneyFlow=\sum_{i=1}^n(Volume \ i)\times P_i\frac{P_i-P_{i-1}}{|{P_i-P_{i-1}}|} MoneyFlow=i=1n(Volume i)×PiPiPi1PiPi1
其中 V o l u m e Volume Volume为成交量, P i P_i Pi i i i时刻收盘价, P i − 1 P_{i-1} Pi1为上一时刻收盘价。

严格意义上讲,每一个买单必须有一个相应的卖单,任何证券当日的买入金额总等于卖出金额,因此资金净流量并不表示当日真正新买进证券的资金量,而仅表示当日推升或压低股票价格的买卖力量对比。

2、资金流测算方法

GSMS指标介绍:
GSMS指标介绍
GSMS指标介绍2

3、策略模型

逆向选择理论

在非强势有效的A股市场,普遍存在信息不对称的问题。机构投资者与散户投资者在对同一信息的评估能力上存在差异。在大部分情况下,散户投资者缺乏专业的投资能力和精力,那么根据“搭便车”理论,希望借助机构投资者对股价的判断进行投资,一旦机构投资者率先对潜在市场信息做出反应,羊群效应的散户投资者则追涨杀跌,往往导致在很多情况下市场对潜在信息反应过度。这样根据逆向选择理论,能够准确评估信息价值的投资者便会对反应过度的股价做出交易,买入低估的、卖出高估的股票,从而纠正这种信息反应过度行为。
根据市场对潜在信息反应过度的结论及市场投资者的行为特征,可以采取逆向选择模型理论来构建选股模型,即卖出前期资金流入、价格上涨的股票,买入前期资金流出、价格下跌的股票。按照这个思路,对一些指标参数进行回测分析,可以得到稳定的选股模型。

策略模型

根据资金流各个指标的特点,在选股模型中采用比较简单的方法,即以指标排序打分的方式来筛选股票,首先通过对各个资金流指标进行排序打分,然后将股票各个指标的得分进行求和,最后以总得分值大小来筛选股票。具体步骤如下:
(1)确定待选股票池,在选择组合构建时,剔除上市不满一个月的股票;剔除调仓期涨跌停及停牌的股票,防止因涨跌停无法交易;剔除信息含量小于10%的股票,因为这部分股票信号不明显,无法取得有效信息。
(2)构建股票组合。

  • 指标打分:首先将待选股票池中的股票按照各个指标进行排序(指标即为前面介绍的GSMS和CMSMF指标),然后采用百分制整数打分法进行指标打分,即以股票在各个指标中所处位置的百分数作为股票对于该指标的得分,前1%得分为1,依次递减,最后1%得分为100%。
  • 求和排序:将股票相对于各个指标的得分进行求和,将和值从小到大排序,进行分组比较;另外,选择排名靠前的N只股票构建组合。
  • 股票权重:采用等量权重

4、实战应用

以上为书本上介绍的资金流模型及使用方法,但是在实际的量化编程中并不好用,首先就是CMSMF这个指标,我在网上基本搜不到相关详细的策略模型,而且关于资金流量这个数据也很难获取,所有我在这里找了一个其他的指标作为替代:MFI指标

(1)MFI指标含义

MFI指标(Money Flow Index):也可以叫资金流量指标,1989年3月由JWellesWilder’s首次发表MFI指标的用法。MFI指标实际是将RSI加以修改后,演变而来。RSI以成交价为计算基础;MFI指标则结合价和量,将其列入综合考虑的范围。可以说,MFI指标是成交量的RSI指标。
MFI又称为资金流向指标,其指标源码如下:
典型价格: T Y P = H I G H + L O W + C L O S E 3 ; 典型价格:TYP=\frac{HIGH+LOW+CLOSE}{3}; 典型价格:TYP=3HIGH+LOW+CLOSE;
V 1 : = ∑ i = 1 n ( I F ( T Y P > R E F ( T Y P , 1 ) , T Y P ∗ V O L , 0 ) ) ∑ i = 1 n ( I F ( T Y P < R E F ( T Y P , 1 ) , T Y P ∗ V O L , 0 ) ) V_1:=\frac{\sum_{i=1}^n(IF(TYP>REF(TYP,1),TYP*VOL,0))}{\sum_{i=1}^n(IF(TYP<REF(TYP,1),TYP*VOL,0))} V1:=i=1n(IF(TYPREF(TYP,1),TYPVOL,0))i=1n(IF(TYPREF(TYP,1),TYPVOL,0))
M F I = 100 − 100 1 + V 1 ; MFI=100-\frac{100}{1+V_1}; MFI=1001+V1100;
具体是说:
1.先计算一定期限内(一般14天)每天的典型价格(即TYP),它是当天最高价,最低价和收盘价三者的均值。也有给收盘价更大权值再算三者均值的算法。
2.如果当天的典型价格大于昨天的则定义为流入,反之为流出,流入流出金额为典型价格乘以当天交易量。这样把14天每天结果计算出来,然后再把流出额和流入额分别加总,得到14天内的流入总额和流出总额,接着前者除以后者,大于1则14天内的资金为流入,反之为流出。V1就是代表这个比值。
3.MFI就是在V1的基础上,为了更好地在坐标上显示出来,进行的数据处理。

(2)MFI买入信号

1.MFI<20时,代表资金短期冷却讯号.但是,必须等待MFI指标再度向上突破20时,才能确认资金转向.
2.MFI在20左右的水平,出现一底比一底高,和股价“背离”的现象时,可视为中期反转上涨的讯号.
3.MFI指标连续二次向上交叉其平均线时,视为买进讯号.(平均线一般设定为6天).

(3)MFI卖出信号

1.MFI>80时,代表资金短期过热讯号.但是,必须等待MFI指标再度向下跌破80时,才能确认资金转向.
2.MFI在80左右的水平,出现一顶比一顶低,和股价“背离”的现象时,可视为中期反转下跌的讯号.
3.MFI指标连续两次向下交叉其平均线时,视为卖出讯号.(平均线一般设定为6天).

(4)使用技巧

MFI指标的“背离”讯号,比RSI指标的“背离”讯号,更能忠实的反应股价的反转现象.一次完整的波段行情,至少都会维持一定相当的时间,反转点出现的次数并不会太多.如果指标出现反转讯号的次数太频繁,发生假讯号的可能性必然增加.基于此指标参数的周期,最好不要设得太短,以免产生指标陷阱过多的困扰.
将MFI指标的参数设定为14天时,其背离讯号产生的时机,大致上都能和股价的波段高低点吻合.因此,实际使用MFI指标时,在参数设定方面,应尽量维持14天的原则.
理论上,价涨量增及价跌量缩是一种惯性作用.股价进行波段涨升时,成交量必须伴随上升.MFI指标爬升至80以上时,代表短期内资金有消耗过量的疑虑,但是,这只是一种警告而已,未来必须视MFI指标是否持续下降,才能确认资金已经退潮.当然,一个资金已经退潮的行情,不仅不利于股价的推升,更容易造成股价回档.相反的,当MFI指标下降至20以下的水平时,代表短期内资金已达冷却的效果.但是,虽然股价经常在资金冷却至一定极限后开始弹升.不过,也有可能因为市场情绪过度沮丧的原故,造成股价变成一滩死水,形成在底部区盘整的局面.
因此,当MFI指标到达资金超买的状况时,不一定需要立刻做出反应,等待资金再稍微退潮一点,确认能量已经消失时,再执行卖出的动作不迟.
底部区的资金状况与头部区不同.股价的涨升虽然必须伴随成交量.但是,底部区成交量的扩增,却不一定能立刻促使股价上涨.因此,MFI指标在底部区的讯号表现会比较迟缓.应等待MFI指标,形成一底比一底高的“背离”走势,再确认买进的动作.注意MFI指标确认反转的讯号,主要运用的头部区/底部区的反转确认,可靠性较低。

(5)采用talib数据库获取股票的MFI指标

from gm.api import * import talib import numpy as np import matplotlib.pyplot as plt """ MFI - Money Flow Index 资金流量指标 > 函数名:MFI 名称:资金流量指标 简介:属于量价类指标,反映市场的运行趋势 分析和应用:[百度百科](https://baike.baidu.com/item/mfi/?fr=aladdin) [同花顺学院](http://www.iwencai.com/school/search?cg=100&w=MFI) NOTE: The ``MFI`` function has an unstable period. python real = MFI(high, low, close, volume, timeperiod=14) """ # 与数据库进行通信,将账号密码加密后发送给服务器 set_token("自己的token码") timeperiod=14 # 获取历史交易数据 data = history_n(symbol="SZSE.002415",frequency="1d",count=100,end_time="2024-05-31",fields="high, low, close, volume",fill_missing="last",adjust=ADJUST_PREV,df=True) # 从dataframe中提取数据 high = data["high"].values low = data["low"].values close = data["close"].values # ta-lib是一个金融技术分析库,它要求所有输入数据都是双精度浮点型(double) volume = np.array(data["volume"].values, dtype=np.double) MFI_index = talib.MFI(high, low, close, volume, timeperiod=timeperiod) MFI_index = np.nan_to_num(MFI_index) MFI_index = MFI_index[timeperiod:] #前timeperiod都为0,因此需要剔除 print(MFI_index) #绘制MFI曲线 fig = plt.figure() ax = fig.add_subplot(111) ax.set_title("MFI") ax.set_xlabel("count") ax.set_ylabel("MFI Date") ax.plot(MFI_index,"b") close = close[timeperiod:] #需要和MFI_INDEX保持一致 close_guiyi = (close-min(close))/(max(close)-min(close))*100 #归一化 ax.plot(close_guiyi,"r") plt.show() #求MFI和收盘价之间的相关系数 corr =np.corrcoef(-close,MFI_index) print(corr[0][1]) 

得到的结果如下,可以看出股价的运动趋势基本和MFI的趋势相同;相关系数为0.5624,为正相关。
在这里插入图片描述

根据MFI制定买卖策略

(1) 股票的资源池,选择沪深300的指数成分股作为股票资源池
(2)指标打分:将股票池中的股票按照MFI指标进行从小到大排序,选择排名靠前的N只股票构建组合。
(3)股票权重:采用等量权重
(4)组合定期调整,调整时间为1~3个月不等,持有到期后,利用更新后的指标数据重新确定待选股票池,重复步骤(2)将股票按照指标得分从小到大排序,将原来分组中跌出组合的股票剔除,调进新的股票,同时将新组合内样本股的权重调整到相等。
(4)统计检验,分布计算各组合的收益率情况,考察组合效果。
具体实现代码如下:
建立一个py文件存储函数,文件名:MFI_strategy.py

from gm.api import * import talib import numpy as np """ MFI - Money Flow Index 资金流量指标 > 函数名:MFI 名称:资金流量指标 简介:属于量价类指标,反映市场的运行趋势 分析和应用:[百度百科](https://baike.baidu.com/item/mfi/?fr=aladdin) [同花顺学院](http://www.iwencai.com/school/search?cg=100&w=MFI) NOTE: The ``MFI`` function has an unstable period. python real = MFI(high, low, close, volume, timeperiod=14) """ def MFI_strategy(index,time,MFI_MIN=20,MFI_MAX=80,timeperiod=14,hold_num = 6): A_share_main = {"600", "601", "602", "603", "000", "001", "002", "003"} # 沪深主板股票代码的开头 symbols_data = stk_get_index_constituents(index, trade_date=time) symbol_list = symbols_data[['index', 'symbol']] # 新建一个dataframe表格用于存储需要用到的数据 symbol_list.insert(len(symbol_list.columns), 'MFI', '') # 在指定位置添加空白列,存储MFI值 for row in symbol_list.index: # print(symbol_list.loc[row]['symbol']) data = history_n(symbol=symbol_list.loc[row]['symbol'], frequency="1d", count=16, end_time=time, fields="high, low, close, volume", fill_missing="last", adjust=ADJUST_PREV, df=True) # 从dataframe中提取数据 high = data["high"].values low = data["low"].values close = data["close"].values # ta-lib是一个金融技术分析库,它要求所有输入数据都是双精度浮点型(double) volume = np.array(data["volume"].values, dtype=np.double) MFI_index = talib.MFI(high, low, close, volume, timeperiod=timeperiod) MFI_index = np.nan_to_num(MFI_index) MFI_index = MFI_index[timeperiod:] #前timeperiod都为0,因此需要剔除 symbol_list.at[row, 'MFI'] = MFI_index[-1] #将最新的MFI值存储到dataframe中 MFI_symbol_list = symbol_list.sort_values("MFI", ascending=True) # 根据选择的因子对股票进行从小到大排序 MFI_symbol_list = MFI_symbol_list.dropna() # 删除无效数据 MFI_symbol_list = MFI_symbol_list.reset_index(drop=True) # 重置索引值 buy_list = MFI_symbol_list print(buy_list) # 对列表中的股票进行再一次的筛选,选择MFI值小于MFI_MIN的加入到购买股票清单中 for i in range(0, len(buy_list)): if buy_list.at[i, 'MFI'] >= MFI_MIN or buy_list.at[i, 'symbol'][5:8] not in A_share_main: # 对MFI值大于阈值或者非主板股票进行舍弃 buy_list = buy_list.drop([i]) if len(buy_list) >= hold_num: # 如果最终股票清单数量大于要买入的股票数,则提取前n个股票进行买入 target_list = buy_list["symbol"].values # 获得排序后的股票代码清单 target_symbol_top = target_list[:hold_num] # 取列表中前n只作为标的股票 else: # 如果股票清单数小于要购买的股票数,则有多少算多少,都列入股票清单 target_symbol_top = buy_list["symbol"].values # 获得排序后的股票代码清单 print(buy_list) print(target_symbol_top) return target_symbol_top 

之后再建立一个py文件建立买卖策略,文件名:MFI_strategy_test.py

# coding=utf-8 from __future__ import print_function, absolute_import, unicode_literals from gm.api import * import datetime import numpy as np import talib import pandas as pd import MFI_strategy ''' 示例策略仅供参考,不建议直接实盘使用。 行业轮动策略 逻辑:以场内ETF基金代表,按照月收益率进行排序,选择收益率最高的10只ETF基金进行买入,每月进行一次调仓换股 ''' def init(context): # 用于统计数据的天数 context.timeperiod = 14 #股票池指数 # context.index = "SHSE.000922" # 中证红利指数 context.index = "SHSE.000300" # 沪深300指数 #买入股票的MFI阈值 context.MFI_MIN = 20 #卖出股票的MFI阈值 context.MFI_MAX = 80 # 持股数量 context.hold_num = 6 # 每日定时任务 schedule(schedule_func=algo, date_rule='1m', time_rule='09:30:00') # schedule(schedule_func=algo_zhiyingzhisun, date_rule='1d', time_rule='09:30:00') #根据盈利亏损值止盈止损 schedule(schedule_func=algo_MFIzhisun, date_rule='1d', time_rule='09:30:00') #根据MFI值止盈止损 def algo(context): # 当天日期 now_str = context.now.strftime('%Y-%m-%d') # 获取上一个交易日及上一周交易日 print(now_str + "执行策略1") last_day = get_previous_n_trading_dates(exchange='SHSE', date=now_str, n=1)[0] target_symbol_top = MFI_strategy.MFI_strategy(index=context.index, time=last_day, MFI_MIN=context.MFI_MIN, MFI_MAX=context.MFI_MAX, timeperiod=context.timeperiod) # (如果收益率大于0,再执行买入操作),收益率小于0,直接清仓 if len(target_symbol_top) > 0: # 获取购买股票清单 to_buy = list(target_symbol_top) # 计算权重 percent = 1.0 / len(to_buy) # 获取当前所有仓位 positions = get_position() # 平不在标的池的股票(注:本策略交易以开盘价为交易价格,当调整定时任务时间时,需调整对应价格) for position in positions: symbol = position['symbol'] if symbol not in to_buy: # 开盘价(日频数据) new_price = \ history_n(symbol=symbol, frequency='1d', count=1, end_time=now_str, fields='open', adjust=ADJUST_PREV, adjust_end_time=context.backtest_end_time, df=False)[0]['open'] # # 当前价(tick数据,免费版本有时间权限限制;实时模式,返回当前最新 tick 数据,回测模式,返回回测当前时间点的最近一分钟的收盘价) # new_price = current(symbols=symbol)[0]['price'] order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Limit, position_side=PositionSide_Long, price=new_price) # 买入标的池中的股票(注:本策略交易以开盘价为交易价格,当调整定时任务时间时,需调整对应价格) for symbol in to_buy: # 开盘价(日频数据) new_price = \ history_n(symbol=symbol, frequency='1d', count=1, end_time=now_str, fields='open', adjust=ADJUST_PREV, adjust_end_time=context.backtest_end_time, df=False)[0]['open'] # # 当前价(tick数据,免费版本有时间权限限制;实时模式,返回当前最新 tick 数据,回测模式,返回回测当前时间点的最近一分钟的收盘价) # new_price = current(symbols=symbol)[0]['price'] order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Limit, position_side=PositionSide_Long, price=new_price) else: order_close_all() # 全部清仓上一阶段股票 print("没有股票可以买入") def algo_zhiyingzhisun(context): now_str = context.now.strftime('%Y-%m-%d') print(now_str+"执行策略2") positions = get_position() for position in positions: symbol = position['symbol'] price = position["price"] #当前行情价格 vwap = position["vwap"] #持仓均价 # print(position) if (price - vwap)/vwap < -0.1: #当亏损大于10%,止损 order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market, position_side=PositionSide_Long) print(symbol+"止损"+now_str) elif (price - vwap)/vwap > 0.4: #当盈利大于30%,止盈 order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market, position_side=PositionSide_Long) print(symbol+"止盈"+now_str) def algo_MFIzhisun(context): now_str = context.now.strftime('%Y-%m-%d') print(now_str+"执行策略3") positions = get_position() for position in positions: symbol = position['symbol'] data = history_n(symbol=symbol, frequency="1d", count=16, end_time=now_str, fields="high, low, close, volume", fill_missing="last", adjust=ADJUST_PREV, df=True) # 从dataframe中提取数据 high = data["high"].values low = data["low"].values close = data["close"].values # ta-lib是一个金融技术分析库,它要求所有输入数据都是双精度浮点型(double) volume = np.array(data["volume"].values, dtype=np.double) MFI_index = talib.MFI(high, low, close, volume, timeperiod=context.timeperiod) MFI_index = np.nan_to_num(MFI_index) MFI_index = MFI_index[context.timeperiod:] #前timeperiod都为0,因此需要剔除 MFI_COUNT = MFI_index[-1] if MFI_COUNT > context.MFI_MAX: order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market, position_side=PositionSide_Long) def on_order_status(context, order): # 标的代码 symbol = order['symbol'] # 委托价格 price = order['price'] # 委托数量 volume = order['volume'] # 目标仓位 target_percent = order['target_percent'] # 查看下单后的委托状态,等于3代表委托全部成交 status = order['status'] # 买卖方向,1为买入,2为卖出 side = order['side'] # 开平仓类型,1为开仓,2为平仓 effect = order['position_effect'] # 委托类型,1为限价委托,2为市价委托 order_type = order['order_type'] if status == 3: if effect == 1: if side == 1: side_effect = '开多仓' else: side_effect = '开空仓' else: if side == 1: side_effect = '平空仓' else: side_effect = '平多仓' order_type_word = '限价' if order_type == 1 else '市价' print('{}:标的:{},操作:以{}{},委托价格:{},委托数量:{}'.format(context.now, symbol, order_type_word, side_effect, price, volume)) def on_backtest_finished(context, indicator): print('*' * 50) print('回测已完成,请通过右上角“回测历史”功能查询详情。') if __name__ == '__main__': ''' strategy_id策略ID,由系统生成 filename文件名,请与本文件名保持一致 mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST token绑定计算机的ID,可在系统设置-密钥管理中生成 backtest_start_time回测开始时间 backtest_end_time回测结束时间 backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST backtest_initial_cash回测初始资金 backtest_commission_ratio回测佣金比例 backtest_slippage_ratio回测滑点比例 backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1 ''' run(strategy_id='自己的策略ID', filename='MFI_strategy_test.py', mode=MODE_BACKTEST, token='自己的token码', backtest_start_time='2021-03-01 09:00:00', backtest_end_time='2023-03-01 15:00:00', backtest_adjust=ADJUST_PREV, backtest_initial_cash=, backtest_commission_ratio=0.0001, backtest_slippage_ratio=0.0001, backtest_match_mode=1) 

回测结果如下,超额收益58%,回撤20%,算是一个比较不错的结果
在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143783.html

(0)
上一篇 2025-05-01 13:33
下一篇 2025-05-01 13:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信