说明 / 示例
```py
#encoding:gbk
'''
策略(小市值)
沪深全部A股,4个月调仓一次:
01 选择市值最小的200只股票
02 均线多头排列MA5>MA20>MA60
#03 ROE > 5% & PB > 2
选择符合这三个条件的股票
'''
import pandas as pd
import numpy as np
import talib
import datetime
pd.set_option('expand_frame_repr', False) # 不换行
pd.set_option('display.max_rows', 5000) # 最多显示数据的行数
pd.set_option('display.unicode.ambiguous_as_wide', True) # 中文字段对齐
pd.set_option('display.unicode.east_asian_width', True)
pd.set_option('display.float_format', lambda x: '%.3f' % x)
def init(ContextInfo):
#设置选股的备选股票池如"沪深300","沪深A股"
ContextInfo.trade_code_list = ContextInfo.get_stock_list_in_sector('沪深300')
#print(len(ContextInfo.trade_code_list))
ContextInfo.set_universe(ContextInfo.trade_code_list)
ContextInfo.accID = '410038217334'
ContextInfo.data_info_level = 2
# 参数
ContextInfo.g_days = 0 # 记录交易天数
ContextInfo.stocks_list = []
# 参数
ContextInfo.choice_nums = 20 # 待挑选小市值股票数量
ContextInfo.changecircle = 20 #调仓周期设定
ContextInfo.market_lengths = 65 # 行情数据长度
#ContextInfo.ROE_rate = 0.00 # roe最小值
#ContextInfo.PB_value = 8 # PB最小值
def handlebar(ContextInfo):
g=ContextInfo;
_nd=datetime.datetime.now()
#print()
d = ContextInfo.barpos #当前K线索引号
td=timetag_to_datetime(ContextInfo.get_bar_timetag(d),'%Y%m%d %H:%M:%S')#当前K线时间
td_strptime = datetime.datetime.strptime(td,'%Y%m%d %H:%M:%S')#转换成日期格式
delta=datetime.timedelta(days=40)
yd_strptime = td_strptime - delta #获取财务因子的初始时间
yd = datetime.datetime.strftime(yd_strptime,'%Y%m%d %H:%M:%S')
start_time = yd[:8]
end_time = td[:8]
if ContextInfo.g_days % ContextInfo.changecircle == 0:
print('{} 开始选股: '.format(end_time))
#股票行情数据
stock_close_panel =ContextInfo.get_market_data(['close'], stock_code=ContextInfo.trade_code_list, skip_paused=True, period='1d', count=1)
stock_close_se = stock_close_panel.ix[:,0,0]
# ============= 选股条件01: 200只小市值股票 ===============
stocks_list = is_selected_condition01(ContextInfo, stock_close_se, start_time, end_time)
# =========== 选股条件02:均线多头排列MA5>MA20>MA60 ============
stocks_list = is_selected_condition02(ContextInfo, stocks_list)
#print(stocks_list)
ContextInfo.stocks_list=stocks_list
if ContextInfo.g_days % ContextInfo.changecircle == 1: # 每四个月等权重买入一次
print('{} 等权重买入: '.format(end_time))
print("买入股票数量: ", len(ContextInfo.stocks_list))
if len(ContextInfo.stocks_list) == 0:
return
g_everyStock = round(get_totalvalue(ContextInfo.accID,'STOCK')/len(ContextInfo.stocks_list), 0) - 100
for stock in ContextInfo.stocks_list:
order_target_value(stock, g_everyStock, ContextInfo, ContextInfo.accID)
if (ContextInfo.g_days % ContextInfo.changecircle) == (ContextInfo.changecircle - 1): # 每四个月清仓一次
print('{} 清仓: '.format(end_time))
for stock in ContextInfo.stocks_list:
order_target_value(stock, 0, ContextInfo, ContextInfo.accID)
ContextInfo.g_days += 1
#log(g,start_time,end_time)
#按时间节点过滤打印日记
def log(ContextInfo,*args):
_nd=datetime.datetime.now()
pos = ContextInfo.barpos #当前K线索引号
tds=timetag_to_datetime(ContextInfo.get_bar_timetag(pos),'%Y%m%d %H:%M:%S')#当前K线时间
td = datetime.datetime.strptime(tds,'%Y%m%d %H:%M:%S')#转换成日期格式
if _nd.year==td.year and _nd.month==td.month and _nd.day==td.day:
for arg in args:
print(arg)
def get_totalvalue(accountid,datatype):
"""
获取账户可用资金
"""
result=0
resultlist=get_trade_detail_data(accountid,datatype,"ACCOUNT")
for obj in resultlist:
result=obj.m_dAvailable #:可用金额
return result
def is_selected_condition01(ContextInfo, stock_close_se, start_time, end_time):
"""
选股条件01: 选择市值最小的200只股票
01 ContextInfo: 全局变量
02 stock_close_se:股票收盘价
03 finance_data_panel:财务数据
构建股票名、总股本、流通股本、限售股本、总市值、流通市值dataframe, 并按照总市值大小进行先后排名
return: list
"""
# 获取财务数据:总股本、流动股本
finance_field_list = ['CAPITALSTRUCTURE.total_capital', 'CAPITALSTRUCTURE.circulating_capital', 'CAPITALSTRUCTURE.restrict_circulating_capital']
finance_data_panel = ContextInfo.get_financial_data(finance_field_list, ContextInfo.trade_code_list, start_time, end_time)
#print('财务数据:',finance_data_panel)
stock_finance = pd.DataFrame()
_list = [];
for st in ContextInfo.trade_code_list:
if not stock_close_se[st] is None:
_list.append(st)
print('_list:',len(_list))
print('trade_code_list:',len(ContextInfo.trade_code_list))
for stock in _list:
#须要解决收盘价为none的错误
_ltg=ContextInfo.get_last_volume(stock)
_total=ContextInfo.get_total_share(stock)
stock_finance.loc[stock, '股票名称'] = ContextInfo.get_stock_name(stock)
stock_finance.loc[stock, '总股本'] = _total
stock_finance.loc[stock, '流通股本'] = _ltg
stock_finance.loc[stock, '限售股本'] = _total-_ltg
#print('close:',stock_close_se[stock])
stock_finance.loc[stock, '总市值'] = _total * stock_close_se[stock]
stock_finance.loc[stock, '流通市值'] = _ltg * stock_close_se[stock]
stock_finance.loc[stock, '限售市值'] = (_total-_ltg) * stock_close_se[stock]
stock_finance = stock_finance.sort_values(by='总市值', ascending=True)
stock_finance = stock_finance.apply(lambda x: transform_format(x), axis=1)
stock_finance = stock_finance.iloc[0: ContextInfo.choice_nums]
#print(stock_finance)
return list(stock_finance.index)
def is_selected_condition02(ContextInfo, stocks_list):
"""
选股条件02: 均线多头排列MA5>MA20>MA60
返回值:list
"""
market_data = ContextInfo.get_market_data(['close'], stock_code=stocks_list, skip_paused = True, period='1d', dividend_type='front', count=ContextInfo.market_lengths)
market_data_df = market_data.ix[:, :, 0]
selected_list = []
for stock in stocks_list:
stock_df = pd.DataFrame(market_data_df[stock])
stock_df['ma_5']=stock_df[stock].shift(1).rolling(window=5).mean()# 注意,此时的均线是对于前一天的均线
stock_df['ma_20']=stock_df[stock].shift(1).rolling(window=20).mean()# 注意,此时的均线是对于前一天的均线
stock_df['ma_60']=stock_df[stock].shift(1).rolling(window=60).mean()# 注意,此时的均线是对于前一天的均线
if stock_df.ix[-1, 'ma_5'] > stock_df.ix[-1, 'ma_20'] and stock_df.ix[-1, 'ma_20'] > stock_df.ix[-1, 'ma_60']:
selected_list.append(stock)
return selected_list
def is_selected_condition03(ContextInfo, stocks_list, stock_close_se, start_time, end_time):
"""
选股条件03: ROE > 8% & PB > 5
返回值:list
"""
# PERSHAREINDEX.s_fa_bps:每股净资产 、ASHAREBALANCESHEET.tot_assets: 资产总计、 ASHAREBALANCESHEET.tot_liab 负债合计 ASHAREINCOME.net_profit_incl_min_int_inc: 净利润
finance_field_list = ['PERSHAREINDEX.s_fa_bps', 'ASHAREBALANCESHEET.tot_assets', 'ASHAREBALANCESHEET.tot_liab', 'ASHAREINCOME.net_profit_incl_min_int_inc']
finance_data_panel = ContextInfo.get_financial_data(finance_field_list, stocks_list, start_time, end_time)
stock_df = pd.DataFrame()
for stock in stocks_list:
df = finance_data_panel[stock].dropna(axis=0,how='any')
if df.empty:
continue
if len(df) == 0:
continue
stock_df.loc[stock, 'ROE'] = df.iloc[-1, 3] / (df.iloc[-1, 1] - df.iloc[-1, 2]) # 净利润 / (资产总计-负债合计)
stock_df.loc[stock, 'PB'] = stock_close_se[stock] / df.iloc[-1, 0] # 股价 / 每股净资产
stock_df = stock_df[(stock_df['ROE'] > ContextInfo.ROE_rate) & (stock_df['PB'] > ContextInfo.PB_value)]
print(list(stock_df.index))
return list(stock_df.index)
def is_suspended_data(stocks_list):
"""
去除当前停牌的数据
return: list
"""
pass
return stocks_list
def transform_format(x):
"""
调整格式
"""
x['总市值'] = str(round(x['总市值'] * 0.00000001, 0)) + ' 亿'
x['流通市值'] = str(round(x['流通市值'] * 0.00000001, 0)) + ' 亿'
x['限售市值'] = str(round(x['限售市值'] * 0.0001, 2)) + ' 万'
return x
```