王教授策略 - 多头发散 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只进行购买

示例

/// <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;
                }
            }
        }

    }
}