王教授策略 - 多头发散 V1.0
王教授策略 - 多头发散 V1.0
定义
描述
以沪深300(000300.XSHG)和中证1000(000852.XSHG)作为基准指数,采用指数20日和
10日的涨幅作为指数的开仓条件, 这里把沪深300和中证1000作为备选指数集合。
备选指数全部满足20日涨幅小于0且10日涨幅小于0时,清仓,否则保持持仓或者调仓。
当动量指标满足时,选择涨幅最大的作为指数的备选标的。
股票选取指标(在确定备选指数标的后,在指数内选取股票):
非st(如果在持仓过程中,被ST, 则在持仓中卖出)
非退市股票
PE 大于 0
EPS 大于0
ROE 大于0
非涨停股票(如果在持仓中,涨停票不会卖出,等不涨停后才会卖出)
非跌停股票
非停牌股票
10日涨幅大于0
在筛选股票时进行排序,选择排名靠前的股票买入,并进行平均持仓,每隔10天调一次仓,
调仓时间为下午14:30, 目前的排序逻辑只使用一年内最高点和最低点来计算盈亏比,按盈
亏比由大到小排序
调仓的过程中,如果股票不在指定排名中卖出,买入新排名中靠前的股票
持仓内涨停的股票,等不能连续涨停后才卖出。
取现价相对一年内最高点和最点的比例,按盈亏比大小逆序排列,5支票则取前5只进行购
买,10支票取前10只进行购买
10日的涨幅作为指数的开仓条件, 这里把沪深300和中证1000作为备选指数集合。
备选指数全部满足20日涨幅小于0且10日涨幅小于0时,清仓,否则保持持仓或者调仓。
当动量指标满足时,选择涨幅最大的作为指数的备选标的。
股票选取指标(在确定备选指数标的后,在指数内选取股票):
非st(如果在持仓过程中,被ST, 则在持仓中卖出)
非退市股票
PE 大于 0
EPS 大于0
ROE 大于0
非涨停股票(如果在持仓中,涨停票不会卖出,等不涨停后才会卖出)
非跌停股票
非停牌股票
10日涨幅大于0
在筛选股票时进行排序,选择排名靠前的股票买入,并进行平均持仓,每隔10天调一次仓,
调仓时间为下午14:30, 目前的排序逻辑只使用一年内最高点和最低点来计算盈亏比,按盈
亏比由大到小排序
调仓的过程中,如果股票不在指定排名中卖出,买入新排名中靠前的股票
持仓内涨停的股票,等不能连续涨停后才卖出。
取现价相对一年内最高点和最点的比例,按盈亏比大小逆序排列,5支票则取前5只进行购
买,10支票取前10只进行购买
示例
/// <summary>
/// 王教授策略
/// </summary>
public class MrWang_back : AlgorithmLogic
{
public List<string> SymbolEveryDay = [];
/// <summary>
/// 基准
/// </summary>
private Symbol _Symbol;
private Dictionary<Symbol, QuantConnect.Indicators.SimpleMovingAverage> day5 = new Dictionary<Symbol, QuantConnect.Indicators.SimpleMovingAverage>();
public override void Initialize()
{
///系统设置 必须设置 因为此方法会获取策略ID 且必须是第一句
SetConfig(1, 5);
Print("日志打印测试");
Print($"日志打印测试error ", EnumModel.PrintType.Error);
SetCash(1000000);
Console.WriteLine(BeginTime);
Console.WriteLine(EndTime);
SetStartDate(BeginTime); // 设置回测开始日期
SetEndDate(EndTime); // 设置回测结束日期
//设置手续费
// 先调用基类初始化
// SetBrokerageModel(QuantConnect.Brokerages.BrokerageName.AlphaStreams, AccountType.Margin);
//设置基准 300
var index300 = AddIndex("000300.XSHG", Resolution.Minute);
SetBenchmark(index300);
SymbolPoolIndex.Add(index300);
////设置基准 中证1000
var index1000 = AddIndex("000852.XSHG", Resolution.Minute);
SymbolPoolIndex.Add(index1000);
///加载所有股票
FillStocks(["000300.XSHG"], (symbol) =>
{
symbol.FeeModel = new CustomFeeService();//手续费
symbol.SlippageModel = new SlippageModel(0.02m);//滑点
}, Resolution.Minute);
//SetBrokerageModel(QuantConnect.Brokerages.BrokerageName.AlphaStreams, AccountType.Margin);
//SetSecurityInitializer(new MySecurityInitializer());
// day5.Add("000001.XSHE", SMA(SymbolPool.Find(o=>o.Value== "000001.XSHE"), 5, Resolution.Daily));
}
#region 选股数据处理
/// <summary>
/// 初选股后数据处理
/// </summary>
/// <param name="models"></param>
private void SetChooseStocks(DateTime dt, string index, List<ChooseStockResultModel> models, Slice slice)
{
try
{
var List = models.Select(n => new HandleResultModel { Code = n.Code, DaysMoreGainPrices = n.DaysMoreGainPrices, HighPrices = n.HighPrices }).ToList();
// var temp = "603297.XSHG,603197.XSHG,002906.XSHE";
Parallel.ForEach(List, new ParallelOptions { MaxDegreeOfParallelism = 2 }, model =>
{
var _stocks = SymbolPool.Find(a => a.Value == model.Code);
if (_stocks != null)
{
// Log($"股票数据有问题:{model.Code}");
model.Price = Math.Round(Securities[_stocks].Price, 2);
model.TodayHigh = Math.Round(Securities[_stocks].High, 2);
// model.FirstHigh = Securities[_stocks];
var _symbolData = ((QuantConnect.Algorithm.LocalData.DataModel.FillModel.Stock_Bars_Source)slice.AllData.Find(o => o.Symbol.Value == _stocks.Value));
if (slice != null && _symbolData != null)
{
model.UpperLimit = Math.Round(_symbolData.HighLimit, 2);
model.LowerLimit = Math.Round(_symbolData.LowLimit, 2);
}
if (!Securities.ContainsKey(_stocks))
{
var stock = AddData<Stock_Bars_Source>(model.Code, Resolution.Minute).Symbol;
SymbolPool.Add(stock);
}
}
});
// List= List.FindAll(a => temp.Contains(a.Code));
//过滤涨停跌停的股票数据
if (List.Count > 0 && List.First().LowerLimit > 0 && List.First().UpperLimit > 0)
{
List = FilterLimit(List, 2);
//Console.WriteLine($"涨停跌停结果:{List.Select(a => a.Code).ToJson()}");
}
//过滤符合涨幅条件的股票数据
if (List.Count > 0 && List.First().DaysMoreGainPrices != null && List.First().DaysMoreGainPrices.Count > 0)
{
//Console.WriteLine($"601963.XSHG涨幅数据,Price:{List.FirstOrDefault(x => x.Code == "601963.XSHG").Price},DaysMoreGainPrices:{List.FirstOrDefault(x => x.Code == "601963.XSHG").DaysMoreGainPrices.ToJson()}");
List = FilterGain(List, 0, 1);
//Console.WriteLine($"涨幅条件结果:{List.Select(a => a.Code).ToJson()}");
}
//过滤股票 股票评分
if (List.Count > 0 && List.First().HighPrices.Count == 0)
{
var resp = ServicesCore.quantApiServices.GetRangeHigh(new Chinahoo.Model.Extend.RequestModel.RangeHighParam { IndexCodes = index, BeginDateTime = dt, Day = 251 }).Result;
List.ForEach(o =>
{
o.HighPrices.Add(resp.FirstOrDefault(a => a.Code == o.Code)?.High ?? 0);
//Log($"{o.Code} {o.HighPrices.First()}");
});
// Log($"股票评分前:{List.Select(a => (a.Code, a.Price, a.HighPrices.First())).ToJson()}");
//接口获取最大值
List = FilterHighPercent(List, 0.95m, 1);
// Console.WriteLine($"股票评分结果:{List.Select(a => a.Code).ToJson()}");
}
///处理数据
if (List.Count > 0)
{
SymbolPoolEveryDay = List.Select(o => o.Code).ToList();
}
//Log(SymbolPoolEveryDay.ToJson());
}
catch (Exception ex)
{
Print("方法【SetChooseStocks】:" + ex.Message, PrintType.Error, ex.ToString());
}
}
#endregion
/// <summary>
/// 根据指数选股
/// </summary>
/// <param name="code"></param>
private void SumStocks(string code, Slice slice)
{
//满仓并且小于10日不进行筛选
if (CountDateTime != null && GetTrading(CountDateTime.ToDate(), Time).Result < 10 && Holdings.Count == 10)
{
SymbolEveryDay = [];
SymbolPoolEveryDay = [];
return;
}
////计算筛选股票池
//SetChooseStock(StrategyId.ToString(), Guid.NewGuid().ToString(), new StockPoll()
//{
// Code = [code],
// Type = 1 // 1-指数, 2-股票
//}, [
// CreateMethod(StockSelectionEnum.NoSt,0),//非ST股
// CreateMethod(StockSelectionEnum.NoPaused,1),//非退市股
// //CreateMethod(StockSelectionEnum.PE,0,CreateParameters(StockSelectionEnum.PE,[0, 1] )),//PE 大于零
// //CreateMethod(StockSelectionEnum.EPS,0,CreateParameters(StockSelectionEnum.EPS,[ 0,1] )),//EPS大于0
// //CreateMethod(StockSelectionEnum.NotPriceLimit,5,CreateParameters(StockSelectionEnum.NotPriceLimit,[ 1,2] )),//涨停
// CreateMethod(StockSelectionEnum.StockClose,21,CreateParameters(StockSelectionEnum.StockClose,[new List<int> {10,20 }] )),//10日内涨幅大于0
// //CreateMethod(StockSelectionEnum.RangeHigh,300,CreateParameters(StockSelectionEnum.RangeHigh,[new List<int> {255 }] ))//股票1年内评分
// ], slice
//);
//选股
List<ChooseStockResultModel> resp = InitChooseStock([code], 1)
.AddChooseStockMethod(StockSelectionEnum.NoSt, 0)
.AddChooseStockMethod(StockSelectionEnum.NoPaused, 1)
.AddChooseStockMethod(StockSelectionEnum.StockClose, 20, [new List<int> { 10, 20 }])
.RunChooseStock();
ChooseStockProxyParam chooseStockProxyParam = InitChooseStock(["000009.XSHG", "000906.XSHG"], 1);
SetChooseStocks(Time, code, resp, slice);
////选股示例
//List<ChooseStockResultModel> resp1 = InitChooseStock([code], 1)
// .AddChooseStockMethod(StockSelectionEnum.PriceLimit, 21, [21, 1])//20日内涨停的股票
// .AddChooseStockMethod(StockSelectionEnum.NotPriceLimit, 16, [16, 0])//15日内不涨停的股票
// .AddChooseStockMethod(StockSelectionEnum.StockClose, 20, [new List<int> { 10, 20 }])//返回第前10天、第前20天收盘价用于计算涨幅
// .AddChooseStockMethod(StockSelectionEnum.DividendYield, 0, [5.8, 1])//分红率大于5.8的股票
// .AddChooseStockMethod(StockSelectionEnum.EPS, 1, [12, 1])//EPS大于12的股票
// .AddChooseStockMethod(StockSelectionEnum.NoPaused, 3)//未停盘股票
// .AddChooseStockMethod(StockSelectionEnum.NoSt, 4)//不是ST股票
// .AddChooseStockMethod(StockSelectionEnum.PE, 5, [20, 0])//PE小于20的股票
// .AddChooseStockMethod(StockSelectionEnum.ROE, 6, [15, 1])//ROE大于15的股票
// .AddChooseStockMethod(StockSelectionEnum.SmallCap, 7, [10000, 1])//估值大于10000的股票
// .AddChooseStockMethod(StockSelectionEnum.Volume, 8, [6, 1265, 0])//6日内成交量小于1265的股票
// .RunChooseStock();
}
bool checkIsST = true;
bool CheckIsClearing = false;
/// <summary>
/// 每天数据到达时调用
/// </summary>
/// <param name="slice"></param>
public override void OnData(Slice slice)
{
try
{
//if (day5.ContainsKey("000001.XSHE"))
//{
// ;
// Print($"5 日均线 {day5["000001.XSHE"].ToString()}");
//}
//获取所有均值
//var avg= GetAvgAll(20, Time);
// Console.WriteLine("AVG6:" + DateTime.Now.ToString("HH:mm:ss:fff"));
//Print($"触发OnData {Time}");
//Print($"error {Time}", EnumModel.PrintType.Error);
// Console.WriteLine(Time);
CheckIsClearing = false;
//if (Time.Hour == 10 && Time.Minute == 0)
//{
// UpdateUniverse();
//}
// 10点以后 整点10分钟检测下涨停票
if (Time.Hour >= 10 && Time.Minute == 10)
{
SymbolLimitUp.ForEach(holding =>
{
//判断当前是否涨停,若没涨停 直接卖出
var _symbolData = slice.AllData.Find(o => o.Symbol == holding) as Stock_Bars_Source;
if (_symbolData.HighLimit > Securities[holding].Price)
{
//卖出
Sell(holding, 0, (status, msg, ticket) =>
{
if (ticket.Status == QuantConnect.Orders.OrderStatus.Filled)
{
//卖出成功
Log($"卖出股票:{holding} 成功");
}
else
{
//卖出失败
Log($"卖出股票:{holding} 失败,原因:{msg}");
}
});
}
});
//SetCountTime(0);
}
//每天2点半开始计算调仓
if (Time.Hour == 10 && Time.Minute == 10)
{
//每天只检测一次 ST
if (checkIsST)
{
var st = GetPausedStocks(Holdings.Select(a => a.Symbol.Value).ToList(), Time);
st.ForEach(item =>
{
//获取当前股票信息
var symbol = Holdings.FirstOrDefault(a => a.Symbol.Value == item);
//判断是否是ST股
if (symbol != null)//这里调用是否是ST股接口
{
//卖出
Sell(symbol.Symbol, symbol.Quantity, (status, msg, ticket) =>
{
if (status == 3)
{
//卖出成功
Log($"卖出ST股票:{symbol.Symbol} 成功");
}
else
{
//卖出失败
Log($"卖出ST股票:{symbol.Symbol} 失败,原因:{msg}");
}
});
}
});
// SetCountTime(0);
checkIsST = false; //只检测一次
}
}
//if (Time.Hour == 14 && Time.Minute == 25)
//{
// SortGuid = BeginRatioSort(Time.AddYears(-1), Time);
//}
//每天2点半开始计算调仓
if (Time.Hour == 14 && Time.Minute == 30)
{
#region 验证指数 20日 10日 是否涨幅都小于0 若小于0 清仓
//计算沪深300 涨幅
var rate300 = GetIncreaseRateByIndex(Time, [10, 20], Chinahoo.Model.Extend.EnumModel.IndexTypeEnum.沪深300);
Print($"{Time}沪深300: 10日涨幅【{rate300[0]}】 20日涨幅【{rate300[1]}】");
//中证1000 涨幅
var rate1000 = GetIncreaseRateByIndex(Time, [10, 20], Chinahoo.Model.Extend.EnumModel.IndexTypeEnum.中证1000);
//var rate1000_10 = GetIncreaseRateByIndex(Time, 10, Chinahoo.Model.Extend.EnumModel.IndexTypeEnum.中证1000);
Print($"{Time}中证1000: 10日涨幅【{rate1000[0]}】 20日涨幅【{rate1000[1]}】");
if (rate300[0] > 0 && rate300[1] > 0 && (rate1000[0] < 0 || rate1000[1] < 0))
{
//沪深300 10日 20日 都 涨幅大于0 且 中证1000 10日 20日其中一个 涨幅小于0 以沪深300为基准
SumStocks("000300.XSHG", slice);
}
else if ((rate300[0] < 0 || rate300[1] < 0) && (rate1000[0] > 0 && rate1000[1] > 0))
{
//沪深300 10日 20日其中一个 涨幅小于0 且 中证1000 10日 20日 涨幅大于0 以中证1000为基准
SumStocks("000852.XSHG", slice);
}
else if (rate300[0] > 0 && rate300[1] > 0 && rate1000[0] > 0 && rate1000[1] > 0 && (rate1000[1] > rate300[1]))
{
//以沪深300为基准
SumStocks("000852.XSHG", slice);
}
else if (rate300[0] > 0 && rate300[1] > 0 && rate1000[0] > 0 && rate1000[1] > 0 && (rate1000[1] < rate300[1]))
{
//以中证1000为基准
SumStocks("000300.XSHG", slice);
}
else
{
//两者都不满足条件 不进行筛股
SymbolPoolEveryDay = [];
}
if ((rate300[0] < 0 || rate300[1] < 0) && (rate1000[0] < 0 || rate1000[1] < 0) && Holdings.Count > 0)
{
//都小于0时 清仓指数下所有股票
ClearingStocks(Chinahoo.Model.Extend.EnumModel.IndexTypeEnum.所有股票);
CheckIsClearing = true; //标记已清仓 清仓后中止今天调仓
SetCountTime(0);
}
#endregion
if (!CheckIsClearing)
{
/*****
* 调仓业务逻辑
* 1.验证持有股票是否已达到10日 若达到10日 则卖出
* 2.验证是否已经满仓 若满仓直接跳过
* 3.若有仓位 则根据今天的排名 补仓
*/
//var day10= (Time-CountDateTime).Days>=10;
if (SymbolPoolEveryDay.Count > 10)
{
//一年250交易日 当天不算取251
SymbolEveryDay = ProfitRatioSort(true, 10, 251).ToList();
}
else
{
SymbolEveryDay = SymbolPoolEveryDay;
}
if (Holdings.Count > 0 && (CountDateTime == null || GetTrading(CountDateTime.ToDate(), Time).Result >= 10))
{
///达到10日 调仓 先与每日排名筛选的股票池进行对比 找出非排名里面前10的股票
//var selectTop10= ProfitRatioSort(true, Time.AddYears(-1), Time, SymbolEveryDay).Result.profitRatioSortLists.Take(10).Select(a=>a.Code).ToList();
// var temp = Holdings.Select(a => a.Symbol.Value).ToList().Except(selectTop10).ToList();
// var buyHoldings= Holdings.Where(a=> temp.Contains( a.Symbol.Value)).ToList();
var temp = SymbolEveryDay.Except(Holdings.Select(a => a.Symbol.Value).ToList());
var buyHoldings = Holdings.Where(a => !SymbolEveryDay.Contains(a.Symbol.Value)).ToList();
// var buyHoldings = Holdings.Find(a => a.Symbol==).ToList();
// var buyHoldings = Holdings.ToArray().ToList();
//卖出没有前10的股票
buyHoldings.ForEach(holding =>
{
//卖出
Sell(holding.Symbol, holding.Quantity, (status, msg, ticket) =>
{
if (status == 3)
{
//卖出成功
Log($"卖出股票:{holding} 成功");
}
else
{
//卖出失败
Log($"卖出股票:{holding} 失败,原因:{msg}");
}
});
});
CountDateTime = Time;
}
//判断当天是否允许买入
if ((rate1000[0] < 0 || rate1000[1] < 0) && (rate300[0] < 0 || rate300[1] < 0))
{
return;
}
//判断是否满仓
if (Holdings.Count < 10 && SymbolEveryDay.Count > 0)
{
var buy = SymbolEveryDay.Except(Holdings.Select(a => a.Symbol.Value).ToList()).Take(10 - Holdings.Count).ToList();
if (buy.Count > 0)
{
var money = GetAvailableInvestmentCapital() / (10 - Holdings.Count); //每只股票平均分配资金
buy.ForEach(a =>
{
var tempSymbol = SymbolPool.Find(o => o.Value == a);
if (tempSymbol != null)
{
///这需要计算购买金额
Buy(tempSymbol, money, (status, msg, ticket) =>
{
if (status == 1)
{
//买入成功
Log($"买入股票:{a} 成功");
}
else
{
//买入失败
Log($"买入股票:{a} 失败,原因:{msg}");
}
});
}
}
);
SetCountTime(1);
}
}
}
}
}
catch (AggregateException ex)
{
Console.WriteLine($"异步错误: {ex.InnerException.Message},{ex.InnerException.StackTrace}");
}
catch (Exception ex)
{
Console.WriteLine($"OnData error:{ex.Message},{ex.StackTrace}");
}
}
/// <summary>
/// 设置计数时间
/// </summary>
/// <param name="flag">0-卖 1-买 </param>
public void SetCountTime(int flag)
{
var day = Time - CountDateTime;
if (Holdings.Count == 0)
{
CountDateTime = null;
}
else
{
if (flag == 0)
{
if (day != null && day.Value.Days >= 10)
{
CountDateTime = Time.Date;
}
}
else
{
if (CountDateTime == null)
{
CountDateTime = Time.Date;
}
}
}
}
}