//------------------------------------------------------------------------
// 简称: StockPick_IndustryLeader
// 名称: 选股-行业龙头
// 类别: 策略应用
// 类型: 内建应用
// 修改: 增加流通市值绝对值过滤(小于100亿、大于500亿,可分别启用)
//------------------------------------------------------------------------
/* 行业龙头,30天成交额前10%,流通市值前10%,主营收入前30%,盈利,取120日涨跌幅前5% */
Params
enum<String> IndustrytypeID(["F101022","F101021","F101026","F101003"]); //F101022:申万2级分类 F101021:申万1级分类F101026:ZJH分类 F101003:TB分类
String IndustryLeader ("行业龙头"); //推送到行情报价的三级板块名
Numeric MarkNumber(2); //标记的号码
Array<Numeric> TurnoverPCT([30,10]); //n天成交金额排名前m,n=TurnoverPCT[0],m=TurnoverPCT[1]
Numeric MarketCapPCT(10); //流通市值前10%
Numeric RevenuePCT(30); //营业收入前30%
Array<Numeric> zdfPCT([120,5]); //满足前面所有条件的按n日涨跌幅排序取前面m,n=zdfPCT[0],m=zdfPCT[1]
Numeric CrossDate(20260515); //选股的截面日期
Numeric writelog(0); //1写log,否则不写log
// ==== 新增参数:流通市值绝对值过滤 ====
Bool EnableMinMarketCap(True); // 是否启用最小市值过滤(True:启用, False:禁用)
Numeric MinMarketCap(100); // 最小流通市值(亿元),小于此值将被过滤
Bool EnableMaxMarketCap(True); // 是否启用最大市值过滤(True:启用, False:禁用)
Numeric MaxMarketCap(500); // 最大流通市值(亿元),大于此值将被过滤
Vars
//基础数据定义
Dic<Array<Numeric>> tbCapital("TB_CAPITAL_CIRCULATIONSHARES"); //数据中心》基础数据》股本结构》流通股份
Dic<Array<Numeric>> tbRevenue("TB_INCOME_REVENUE"); //数据中心》基础数据》利润分配表》营业收入
Dic<Array<Numeric>> tbprofit("TB_INCOME_PROFIT"); //数据中心》基础数据》利润分配表》利润汇总
//循环订阅所有板块,不需要重置
Global Numeric IdyNum(0); //当前板块在板块数组IdyNamearr的序号
Global Array<String> IdyNamearr; //板块数组
Global Integer i(0);
//需要重置区域
//推送选股和标记专用
Global String tmpSyms; //自定义合约的股票
Global String tmpIds;
Global String tmpDesps;
//其它变量
Global Array<Numeric> mv; //流通市值(亿元)
Global Array<Numeric> zdf120; //涨跌幅
Global Array<Numeric> turnover30; //成交金额
Global Array<Numeric> revenue; //营业收入
Global Array<Numeric> profit; //净利润
Global Array<Numeric> flag; //选股标识
Global Array<Integer> BarStatus2Flag(0); //数据源到最后一根的标识
Global Integer BarStatus2FlagCount(0);//数据源满足最后一根的个数
Defs
//写日志,方便代码调试
Integer msgbox(StringRef msg)
{
if(writelog==1)
FileAppend(".\\log\\"+FormulaName, "[" + DateTimeToString(SystemDateTime) + "] " + msg);
Return 0;
}
//从基础数据读取行业板块名称,依据板块名称从基础数据读取板块内品种名称,订阅品种bar数据
Bool SubscribeData()
{
//清空数组,准备新的板块选股
ArrayClear(mv);
ArrayClear(zdf120);
ArrayClear(turnover30);
ArrayClear(revenue);
ArrayClear(profit);
ArrayClear(flag);
ArrayClear(BarStatus2Flag);//清空标识
BarStatus2FlagCount=0;
//获取当前是第几个板块
if(IdyNum==0)
{
if(IndustrytypeID=="F101003")
IdyNum=1;//F101003第0个值的板块为全部,所以从1开始是行业板块
GetDicValue("TB_INDUSTRY",IndustrytypeID,CrossDate,IdyNamearr);
}
//写日志
msgbox("idnum="+Text(IdyNum)+"/"+Text(GetArraySize(IdyNamearr)-1));
//获取A股所有板块
//订阅数据,追加的数据源
Array<String> subSymbols;
//板块节点(科创板) 如果为空则不再添加数据
String IdyName=IdyNamearr[IdyNum];
PushStatusMsg("正在执行["+IdyName+"],"+Text(IdyNum)+"/"+Text(GetArraySize(IdyNamearr)-1));
if(IdyName<>"")
{
GetDicValue("TB_INDUSTRY",IdyName,SystemDateTime(),subSymbols);
}
msgbox(TextArray(subSymbols));
for i=0 to GetArraySize(subSymbols)-1
{
msgbox("起始日期"+text(DateAdd(CrossDate,-180)));
SubscribeBar(subSymbols[i],"1d",DateAdd(CrossDate,-180),CrossDate);
//msgbox("订阅"+subSymbols[i]+"成功");
//Else
//msgbox("订阅"+subSymbols[i]+"失败");
}
Range[0:DataCount-1]
{
AddDataFlag(Enum_Data_RolloverBackWard());//设置后复权
}
Return True;
}
//1、检查订阅数据的完整性,对于空数据的品种进行退订。2、设置后复权
Bool CheckData()
{
Integer datanum;
Array<Integer> BarCountarr;
//统计下各数据源的bar数
Range[i=0:DataCount-1]
{
BarCountarr[i]=BarCount;
}
msgbox(TextArray(BarCountarr));
msgbox("检查前数据源数量="+text(DataCount));
//倒序剔除无数据的数据源
datanum=DataCount;
for i=0 to DataCount-1
{
if(BarCountarr[datanum-1-i]==0 or BarCountarr[datanum-1-i]==InvalidInteger)
{
//msgbox("删除数据,因为bar数目="+Text(BarCountarr[datanum-1-i]));
UnsubscribeBar(datanum-1-i);
}
}
msgbox("检查后数据源数量="+text(DataCount));
if(DataCount==0)
{
IdyNum=IdyNum+1; //选过的板块数量加1
if(IdyNum<GetArraySize(IdyNamearr))
{
ReStart(false); //重启订阅下一个板块
}Else
{
PushStatusMsg("选股完成");
AddStrategyFlag(Enum_Strategy_Finished);//正常停止策略运行
}
}
Return True;
}
//计算指标
//计算指标
Bool countIndicators()
{
Range[i=0:DataCount-1]
{
// 无条件执行序列函数(每根Bar都会调用,避免警告)
Numeric tempTurnover30 = Summation(TurnOver, TurnoverPCT[0]);
if(((BarCount==1 && barstatus==0) ||barstatus==2) && BarStatus2Flag[i]==0)
{
if(close==InvalidNumeric)
{
mv[i]=-1;
zdf120[i]=-1;
turnover30[i]=-1;
}
Else
{
zdf120[i]=close/close[zdfPCT[0]];
mv[i]=close/Rollover*tbCapital[0][1]*0.0001*0.0001;
turnover30[i] = tempTurnover30; // 只在最后一根Bar记录
}
revenue[i]=tbRevenue[0][1];
profit[i]=tbprofit[0][11];
BarStatus2Flag[i]=1;
BarStatus2FlagCount=BarStatus2FlagCount+1;
msgbox(TextArray(BarStatus2Flag));
}
}
Return True;
}
//指标排序
Bool SortIndicators()
{
//站在data0上对所有品种进行排序,最后结果存为一个数组
msgbox("开始排序!");
msgbox(TextArray(BarStatus2Flag));
msgbox(TextArray(zdf120)); //确认数值都获取到
msgbox(TextArray(mv));
msgbox(TextArray(turnover30));
msgbox(TextArray(revenue));
msgbox(TextArray(profit));
// 1. 流通市值排名前MarketCapPCT%
Array<Integer> id1;
Array<Integer> id2;
for i=0 to DataCount-1 //下标数组的初始化赋值
{
id1[i]=i;
id2[i]=i;
}
SortIds(mv,id1,id2,0,DataCount-1,False);//对流通市值进行倒序排序
Array<Numeric> flagmv;
for i=0 to DataCount-1
{
if(id2[i]<DataCount*MarketCapPCT*0.01) //选择排名考前的标识为1
flagmv[i]=1;
Else
flagmv[i]=0;
}
// 2. 新增:流通市值绝对值过滤(小于MinMarketCap或大于MaxMarketCap)
Array<Numeric> flagMarketCapAbs;
for i=0 to DataCount-1
{
flagMarketCapAbs[i] = 1; // 默认通过
if(EnableMinMarketCap and mv[i] < MinMarketCap)
flagMarketCapAbs[i] = 0;
if(EnableMaxMarketCap and mv[i] > MaxMarketCap)
flagMarketCapAbs[i] = 0;
}
// 日志输出市值过滤情况
if(EnableMinMarketCap or EnableMaxMarketCap)
{
Integer passCount = 0;
for i=0 to DataCount-1
if(flagMarketCapAbs[i]==1) passCount = passCount + 1;
msgbox("市值绝对值过滤:启用最小"+IIFString(EnableMinMarketCap,Text(MinMarketCap)+"亿","否")+
",启用最大"+IIFString(EnableMaxMarketCap,Text(MaxMarketCap)+"亿","否")+
",通过数量="+Text(passCount)+"/"+Text(DataCount));
}
// 3. 30天成交额排名前TurnoverPCT[1]%
ArrayClear(id1);
ArrayClear(id2);
for i=0 to DataCount-1
{
id1[i]=i;
id2[i]=i;
}
SortIds(turnover30,id1,id2,0,DataCount-1,False);
Array<Numeric> flagturnover30;
for i=0 to DataCount-1
{
if(id2[i]<DataCount*TurnoverPCT[1]*0.01)
flagturnover30[i]=1;
Else
flagturnover30[i]=0;
}
// 4. 营业收入排名前RevenuePCT%
ArrayClear(id1);
ArrayClear(id2);
for i=0 to DataCount-1
{
id1[i]=i;
id2[i]=i;
}
SortIds(revenue,id1,id2,0,DataCount-1,False);
Array<Numeric> flagrevenue;
for i=0 to DataCount-1
{
if(id2[i]<DataCount*RevenuePCT*0.01)
flagrevenue[i]=1;
Else
flagrevenue[i]=0;
}
// 5. 盈利过滤(净利润>0)
Array<Numeric> flagprofit;
for i=0 to DataCount-1
{
if(profit[i]>0)
flagprofit[i]=1;
Else
flagprofit[i]=0;
}
// 日志输出各条件计数
msgbox(TextArray(flagmv));
msgbox(TextArray(flagturnover30));
msgbox(TextArray(flagrevenue));
msgbox(TextArray(flagprofit));
// 6. 汇总所有条件(原4项 + 新增市值绝对值过滤)
Array<Numeric> flag1;
for i=0 to DataCount-1
{
flag1[i]=flagmv[i]*flagturnover30[i]*flagrevenue[i]*flagprofit[i]*flagMarketCapAbs[i];
}
// 7. 按涨跌幅排序,取前zdfPCT[1]%
ArrayClear(id1);
ArrayClear(id2);
for i=0 to DataCount-1
{
id1[i]=i;
id2[i]=i;
}
SortIds(zdf120,id1,id2,0,DataCount-1,False);
Numeric k=0;
Numeric targetCount = RoundUp(zdfPCT[1]*0.01*DataCount,0);
for i=0 to DataCount-1 //循环选取股票,当达到数量上限则停止选股
{
if(flag1[id1[i]]==1 and k<targetCount)
{
flag[id1[i]]=1;
k=k+1;
}
Else
flag[id1[i]]=0;
}
msgbox("最终入选股票数量="+Text(k));
Return True;
}
//选股并推送到板块
Bool PickStock_Send()
{
if(GetArraySize(flag)>0)
{
msgbox(TextArray(flag));
}
Range[i=0:DataCount-1]
{
if(flag[i]==1)
{
tmpSyms = tmpSyms+Symbol+",";
tmpIds=tmpIds+Text(MarkNumber)+","; //写标记
tmpDesps=tmpDesps+Text(SystemDateTime,9)+",";
}
}
Map<String,String> context; //写标记用
Map<String,String> mySyms; //推送自定义合约的MAP
mySyms["合约集合"]=tmpSyms;//选股合约
mySyms["板块名称"]="策略选择_"+IndustryLeader ;//自定义行情设置,格式是:一级板块_二级板块
If(IdyNum==1)
mySyms["添加方式"]="override";//更新方式:override,append
Else
mySyms["添加方式"]="append";
context["合约集合"]=tmpSyms;//选股合约
context["标记编号集合"]=tmpIds;//合约标记ID【1-10】
context["标记内容集合"]=tmpDesps;//标记内容
if(tmpSyms<>"")
{
PublishEvent("系统-选股事件",mySyms,"行情报价");//发送选股事件到行情报价
PublishEvent("系统-标记事件",context,"行情报价"); //写标记
}
tmpSyms="";
tmpIds="";
tmpDesps="";
IdyNum=IdyNum+1; //选过的板块数量加1
if(IdyNum<GetArraySize(IdyNamearr))
{
ReStart(false); //重启订阅下一个板块
}Else
{
PushStatusMsg("选股完成");
AddStrategyFlag(Enum_Strategy_Finished);//正常停止策略运行
}
Return True;
}
Events
OnInit()
{
//订阅数据
SubscribeData();
}
OnReady()
{
//检查数据
CheckData();
}
OnBar(ArrayRef<Integer> indexs)
{
//计算指标
countIndicators();
//指标排序
//所有数据源都结束再选股
if(BarStatus2FlagCount==DataCount)
{
SortIndicators();
//选股并推送到板块
PickStock_Send();
}
}
//------------------------------------------------------------------------
// 编译版本 GS2010.12.08
// 版权所有 TradeBlazer Software 2003-2045
// 更改声明 TradeBlazer Software保留对TradeBlazer平
// 台每一版本的TradeBlazer公式修改和重写的权利
//------------------------------------------------------------------------TB3流通市值过滤问题,无法获得流通市值并做上下限过滤,比如过滤掉流通市值小于100亿,大于500亿的股票。麻烦老师指点一下,谢谢!
有没有函数可以直接查询流值和PE?
是不能用了吗?有没有其他替代的过滤方法?