投资者往往只是热衷认购新股,一旦上市便获利了结,很少想一想这些新股中哪些是值得长线持有的。当新股的中签率越来越低,新股认购已经不能满足攫取暴利的大机构的需要时,他们便会主动停止认购新股的游戏,到二级市场抛售压力巨大的新股板块中寻找获利机会,所以一批真正有业绩有高成长性的次新股成为机构吸纳的主要对象。
次新股具有的最重要的特点,便是兼具投资投机价值。从投资角度来看,次新股大多业绩较好,又主要属于高科技成长性行业,而且许多流通规模也不大,使其具有业绩和股本的同步成长性,在市场中具有较好的活性。尤其是一些行业特殊的个股,市场炒作的朦胧题材较多。再加上次新股发行上市时所募集资金投资的系列项目,目前已进入回报期,新的利润增长点不断产生,对其业绩的大幅提高有较好的支撑作用。从投机价值看,次新股常呈现出阶段性的热点炒作,一旦大盘步入调整或市场缺乏热点时,次新股便会被主力挖出来炒作,以吸引市场人气。因此市场中的机会较多是其重要特点。
次新股的基本走势与投资策略
从过去新股的炒作来看,有一小部分新股上市后就大幅上升,这种股票对中小散户来说,只能是“望梅止渴”,并不敢杀入,但大多数新股上市后并不是马上大幅上升,而是经过一段时间后才会有较好的表现,这些股票对中小散户来说是可以参与的。中小散户如何参与这类股票的炒作,就必须对新股上市后的走势进行分析,才能做出投资决策。
次新股每一轮超跌之后,都会出现强势的反弹。为什么?因为他们流通盘小,流通市值都是在10来亿左右,有的只有5个亿、6个亿的流通市值。所以易上涨,易涨停。这是非常好的广告,自然在反弹时,吸引资金的围剿。A股是散户为主体的市场,追涨风格,重在涨得猛。次新股易上涨,波动大,涨起来易涨停,这非常吸引人。所以每一轮调整,超跌反弹都是有次新股的身影。这也是资金对炒新乐死不疲的原因。
同时,现在炒重组易被点名,炒故事股。有涨停的都是容易被点名。唯独次新股连板很少被点名,被点名时基本上都是涨了几个板的。这种点名空间大。资金炒作就大胆些。
我们股市重融资,不敢让次新太死,也有一定的心理因素。但这些还长远,规律不变条件下。超跌反弹先看次新的规律还是不会变。但注意,市场波动大,开板次新易反弹,但老次新反弹易被获利盘和套空盘、解禁盘冲击捕杀。操作上,次新股只合适短线快进快出为主。
次新股实战策略
1、选择中小板中市值最小的10只股票;
2、在这10只股票中选择估值最小、最近2个月涨幅最大的5只股票按平均仓位买入。
策略回测
策略代码(聚宽量化平台)
from jqdata import *
from kuanke.wizard import *
from abc import ABCMeta, abstractmethod
def initialize(context):
set_benchmark('000300.XSHG')
set_option('use_real_price', True)
log.info('策略启动')
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
g.strategy_manager = StrategyManager()
## 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'000300.XSHG'或'510300.XSHG'是一样的)
run_daily(timer_event, time='14:30', reference_security='000300.XSHG')
def timer_event(context):
g.strategy_manager.timer_event(context)
#--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+
class Configure(object):
def __init__(self):
self.select_symbol_peroid = 10
self.use_remote_algorithm = False
class StrategyBase(object):
def __init__(self, root, name, config):
self.root = root
self.name = name
self.config = config
self.trade_days = 0
self.symbol_list = []
#print('StrategyBase.__init__')
pass
# 风控管理
@abstractmethod
def risk_control(self, context):
return True
# 选股
@abstractmethod
def select_symbol(self, context):
return []
# 定时器
def timer_event(self, context):
self.risk_control(context)
self.select_symbol(context)
#--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+
class Strategy001(StrategyBase):
def __init__(self, root, name, config):
StrategyBase.__init__(self, root, name, config)
pass
# 风控管理
def risk_control(self, context):
#print('Strategy001.risk_control %r %s' % (context.current_dt.strftime('%Y-%m-%d %H:%M:%S'), self.root.name))
# TODO ...
return True
# 选股
def select_symbol(self, context):
if self.trade_days % self.config.select_symbol_period != 0:
self.trade_days += 1
return self.symbol_list
self.trade_days += 1
# 原始股票池
#symbol_list = get_security_universe(context, ['000300.XSHG'], []) # 沪深300
#symbol_list = get_security_universe(context, ['000905.XSHG'], []) # 中证500
symbol_list = get_security_universe(context, ['399101.XSHE'], []) # 中小板
q = query(valuation.code, valuation.pb_ratio, valuation.market_cap, indicator.roe, indicator.inc_total_revenue_year_on_year, indicator.inc_operation_profit_year_on_year, indicator.inc_net_profit_year_on_year
).filter(
valuation.code.in_(symbol_list))
df = get_fundamentals(q)
if df is None or df.shape[0] == 0:
log.error('--------------------------------------------')
return self.symbol_list
df.rename(columns={
'inc_total_revenue_year_on_year': 'ystb',
'inc_operation_profit_year_on_year': 'lrtb',
'inc_net_profit_year_on_year': 'jlrtb',
'anon_1': 'fuzhailv'}, inplace=True)
#删除空值
df = df.dropna(axis=0, how='all')
if self.config.use_remote_algorithm:
# TODO ...
pass
else:
#df = df[(df.roe > 4) & (df.pb_ratio > 0) & (df.pb_ratio < 15) & (df.ystb > 10) & (df.ystb < 200)].sort_values('roe', ascending=False)
#df = df[:max(50, min(100, self.root.max_stock_count * 10))]
df = df[(df.pb_ratio > 0) & (df.pb_ratio < 50) & (df.ystb > 10) & (df.ystb < 2000)].sort_values('market_cap', ascending=True)
df = df[:10]
if df is None or df.shape[0] == 0:
return self.symbol_list
df.index = df['code'].values
chg_list = []
for index, row in df.iterrows():
chg_list.append(get_n_day_chg(row.code, 30))
df['chg'] = chg_list
df['1/pb_ratio'] = 1 / df['pb_ratio']
#df['score'] = df[['1/pb_ratio', 'chg', 'ystb']].rank().T.apply(lambda x : x[0] * 2 + x[1] * 2 + x[2] * 1)
#df['score'] = df[['market_cap', 'chg', 'ystb']].rank().T.apply(lambda x : x[0] * 2 + x[1] * 2 + x[2] * 1)
df['score'] = df[['1/pb_ratio', 'chg', 'ystb']].rank().T.apply(lambda x : x[0] * 2 + x[1] * 2 + x[2] * 2)
#按得分进行排序,取指定数量的股票
df = df.sort_values('score', ascending=False)
self.symbol_list = df.index
#self.symbol_list = [symbol for symbol in self.symbol_list if n_day_chg_dayu(symbol, 90, 0.1)]
self.symbol_list = self.symbol_list[:int(self.root.max_stock_count / len(self.root.strategy_list))]
log.info('最新信号 ==> %s' % self.name)
for symbol in self.symbol_list:
log.info('%s %s' % (symbol, get_security_info(symbol).display_name))
return self.symbol_list
class Strategy002(StrategyBase):
def __init__(self, root, name, config):
StrategyBase.__init__(self, root, name, config)
pass
# 风控管理
def risk_control(self, context):
#print('Strategy002.risk_control %r %s' % (context.current_dt.strftime('%Y-%m-%d %H:%M:%S'), self.root.name))
# TODO ...
return True
# 选股
def select_symbol(self, context):
if self.trade_days % self.config.select_symbol_period != 0:
self.trade_days += 1
return self.symbol_list
self.trade_days += 1
# 原始股票池
#symbol_list = get_security_universe(context, ['000300.XSHG'], []) # 沪深300
symbol_list = get_security_universe(context, ['000905.XSHG'], []) # 中证500
q = query(valuation.code, valuation.pb_ratio, valuation.market_cap, indicator.roe, indicator.inc_total_revenue_year_on_year,
indicator.inc_operation_profit_year_on_year, indicator.inc_net_profit_year_on_year
).filter(
valuation.code.in_(symbol_list))
df = get_fundamentals(q)
if df is None or df.shape[0] == 0:
log.error('--------------------------------------------')
return self.symbol_list
df.rename(columns={
'inc_total_revenue_year_on_year': 'ystb',
'inc_operation_profit_year_on_year': 'lrtb',
'inc_net_profit_year_on_year': 'jlrtb',
'anon_1': 'fuzhailv'}, inplace=True)
#删除空值
df = df.dropna(axis=0, how='all')
if self.config.use_remote_algorithm:
# TODO ...
pass
else:
df = df[(df.roe > 4) & (df.pb_ratio > 0) & (df.pb_ratio < 15) & (df.ystb > 10) & (df.ystb < 200)].sort_values('roe', ascending=False)
df = df[:max(50, min(100, self.root.max_stock_count * 10))]
if df is None or df.shape[0] == 0:
return self.symbol_list
df.index = df['code'].values
chg_list = []
for index, row in df.iterrows():
chg_list.append(get_n_day_chg(row.code, 30))
df['chg'] = chg_list
df['1/pb_ratio'] = 1 / df['pb_ratio']
df['score'] = df[['1/pb_ratio', 'chg', 'ystb']].rank().T.apply(lambda x : x[0] * 2 + x[1] * 2 + x[2] * 1)
#按得分进行排序,取指定数量的股票
df = df.sort_values('score', ascending=False)
self.symbol_list = df.index
self.symbol_list = [symbol for symbol in self.symbol_list if n_day_chg_dayu(symbol, 90, -0.1)]
self.symbol_list = self.symbol_list[:int(self.root.max_stock_count / len(self.root.strategy_list))]
log.info('最新信号 ==> %s' % self.name)
for symbol in self.symbol_list:
log.info('%s %s' % (symbol, get_security_info(symbol).display_name))
return self.symbol_list
#--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+
class StrategyManager():
def __init__(self):
self.name = 'root'
self.strategy_list = []
self.max_stock_count = 10
config = Configure()
config.select_symbol_period = 10
self.strategy_list.append(Strategy001(self, '次新股策略', config))
#self.strategy_list.append(Strategy002(self, '中证500指数增强', config))
# 定时器
def timer_event(self, context):
print('\n==========================================================\n')
symbol_list = []
for strategy in self.strategy_list:
strategy.timer_event(context)
symbol_list.extend(strategy.symbol_list)
# TODO tet
#log.info('大盘预测[%s]' % '上涨' if rsi_judge('000300.XSHG', '1d', 15, (50, 100)) else '下跌')
#log.info('held symbol:%s' % symbol_list)
self.adjust_position(context, symbol_list, self.max_stock_count)
@staticmethod
def adjust_position(context, pool, max_stock_count):
cash = context.portfolio.total_value / max_stock_count #* StrategyManager.get_dynamic_turnover()
hold_stock = context.portfolio.positions.keys()
#卖出不在持仓中的股票
for stock in hold_stock:
if stock not in pool:
order_target_value(stock, 0)
#买入股票
held_stock = context.portfolio.positions.keys()
for stock in pool:
if len(held_stock) < max_stock_count + 1:
order_target_value(stock, cash)
@staticmethod
def get_dynamic_turnover():
count = 0
periods = [15, 30, 45, 60, 90]
for period in periods:
if RSI_judge_qujian('000300.XSHG', (53, 99), period):
count += 1
if RSI_judge_qujian('000001.XSHG', (53, 99), period):
count += 1
return count / len(periods) / 2
#--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+
# 获取股票股票池
def get_security_universe(context, security_universe_index, security_universe_user_securities):
temp_index = []
for s in security_universe_index:
if s == 'all_a_securities':
temp_index += list(get_all_securities(['stock'], context.current_dt.date()).index)
else:
temp_index += get_index_stocks(s)
for x in security_universe_user_securities:
temp_index += x
return sorted(list(set(temp_index)))
# 行业过滤
def industry_filter(context, security_list, industry_list):
if len(industry_list) == 0:
# 返回股票列表
return security_list
else:
securities = []
for s in industry_list:
temp_securities = get_industry_stocks(s)
securities += temp_securities
security_list = [stock for stock in security_list if stock in securities]
# 返回股票列表
return security_list