----------------------------------------------------------------------------
# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import datetime
import pandas as pd
import numpy as np
import multiprocessing
import base64
# 策略中必须有init方法
def init(context):
# peg阈值
context.peg_threshold = 0.4
# # 持股数量
context.holding_num = 20
# 每周定时任务
schedule(schedule_func=algo, date_rule='1d', time_rule='09:31:00')
def algo(context):
time = base64.b64decode('MjAyMi0xMC0yMSAwMDowMDowMA==')
time = datetime.datetime.strptime(str(time)[2:-1],"%Y-%m-%d %H:%M:%S")
if datetime.datetime.strptime(str(context.now)[0:19],"%Y-%m-%d %H:%M:%S") > time:
stop()
all_stocks,all_stocks_str = get_normal_stocks(context.now)
# 上一交易日
last_date = get_previous_trading_date(exchange='SZSE', date=context.now)
# 获取PE-TTM和总市值,并剔除负PE的股票
data1 = get_fundamentals_n(table='trading_derivative_indicator', symbols=all_stocks, end_date=last_date, fields='PETTM,TOTMKTCAP', count=1, df=True)
data1 = data1[data1['PETTM']>0].set_index('symbol')
# 获取EPS,计算G,并剔除负G的股票
all_stocks = list(data1.index)
data2 = get_fundamentals_n(table='prim_finance_indicator', symbols=all_stocks, end_date=last_date, fields='EPSBASIC', count=5, df=True)
# get_fundamentals_n中end_date对标的是财报季度最后一天,而非财报发布日期,所以获取的数据会有未来数据,要先剔除
data2 = data2[data2['pub_date']<context.now].sort_values(['symbol','end_date'])
epss = data2.groupby(['symbol']).count()# 统计财报期数
all_stocks = list(epss[epss['pub_date']>=4].index)# 筛选财报期数大于等于4期的股票
data2 = data2[data2['symbol'].isin(all_stocks)]# 剔除财报数据期数少于4期的股票
new_eps = data2.groupby(['symbol'])['EPSBASIC'].apply(lambda df:df.iloc[-1])# 最新一期
pre_eps = data2.groupby(['symbol'])['EPSBASIC'].apply(lambda df:df.iloc[-4])# 前N期
pre_eps = pre_eps[pre_eps!=0]# 剔除前N期eps为0的股票,除数不为零
new_eps = new_eps.loc[pre_eps.index]
g = new_eps/pre_eps-1# 计算g
g = g[g>0]*100
# 计算PEG
common_stocks = list(set(data1.index)&set(g.index))
peg = data1.loc[common_stocks,'PETTM']/g.loc[common_stocks]
# 选取PEG小于0.5的股票,并按市值从小到大排序
peg_pick = peg[peg<context.peg_threshold]
peg_pick_stocks = list(peg_pick.index)
peg_pick_cap = data1.loc[peg_pick_stocks,'TOTMKTCAP'].sort_values()
# 选取市值最小的N只股票
to_buy = list(peg_pick_cap.iloc[:context.holding_num].index)
print('{},选股目标股票数量{}只:{}'.format(context.now,len(to_buy),to_buy))
# 股票交易
# 获取持仓
positions = context.account().positions()
# 卖出不在to_buy中的持仓(跌停不卖出)
for position in positions:
symbol = position['symbol']
if symbol not in to_buy:
lower_limit = get_history_instruments(symbol, fields='lower_limit', start_date=context.now, end_date=context.now, df=True)
new_price = history(symbol=symbol, frequency='60s', start_time=context.now, end_time=context.now, fields='close', df=True)
if symbol not in to_buy and (len(new_price)==0 or len(lower_limit)==0 or lower_limit['lower_limit'][0]!=round(new_price['close'][0],2)):
# new_price为空时,是开盘后无成交的现象,此处忽略该情况,可能会包含涨跌停的股票
order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market, position_side=PositionSide_Long)
print('{}:以当前价格平不在标的池的:{}'.format(context.now,symbol))
# 买入股票(涨停不买入)
for symbol in to_buy:
upper_limit = get_history_instruments(symbol, fields='upper_limit', start_date=context.now, end_date=context.now, df=True)
new_price = history(symbol=symbol, frequency='60s', start_time=context.now, end_time=context.now, fields='close', df=True)
if len(new_price)==0 or len(upper_limit)==0 or upper_limit['upper_limit'][0]!=round(new_price['close'][0],2):
# new_price为空时,是开盘后无成交的现象,此处忽略该情况,可能会包含涨跌停的股票
order_target_percent(symbol=symbol, percent=1/len(to_buy), order_type=OrderType_Market, position_side=PositionSide_Long)
print('{}:将{}以当前价格下单调整至仓位:{}'.format(context.now,symbol,1/len(to_buy)))
def on_account_status(context,account):
print(account)
def get_normal_stocks(date,new_days=365):
"""
获取目标日期date的A股代码(剔除停牌股、ST股、次新股(365天))
:param date:目标日期
:param new_days:新股上市天数,默认为365天
"""
if isinstance(date,str) and len(date)==10:
date = datetime.datetime.strptime(date,"%Y-%m-%d")
elif isinstance(date,str) and len(date)>10:
date = datetime.datetime.strptime(date,"%Y-%m-%d %H:%M:%S")
# 先剔除退市股、次新股和B股
df_code = get_instrumentinfos(sec_types=SEC_TYPE_STOCK, fields='symbol, listed_date, delisted_date', df=True)
all_stocks = [code for code in df_code[(df_code['listed_date']<=date-datetime.timedelta(days=new_days))&(df_code['delisted_date']>date)].symbol.to_list() if code[:6]!='SHSE.9' and code[:6]!='SZSE.2']
# 再剔除当前的停牌股和ST股
history_ins = get_history_instruments(symbols=all_stocks, start_date=date, end_date=date, fields='symbol,sec_level, is_suspended', df=True)
all_stocks = list(history_ins[(history_ins['sec_level']==1) & (history_ins['is_suspended']==0)]['symbol'])
all_stocks_str = ','.join(all_stocks)
return all_stocks,all_stocks_str
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回测滑点比例
'''
run(strategy_id='14b7381c-bacd-11ec-adce-04421a98932f',
filename='main.py',
mode=MODE_LIVE,
token='e3beab5e3ada3a5e06b63279b6861206f5d6394c',
backtest_start_time='2021-01-01 08:00:00',
backtest_end_time='2022-01-01 16:00:00',
backtest_adjust=ADJUST_PREV,
backtest_initial_cash=620000,
backtest_commission_ratio=0.0016,
backtest_slippage_ratio=0.00246)
联系客服