public static async Task<Tuple<IList<List<DailyData>>,TimeSpan, TimeSpan>> GetHistoricalAndRealtimesQuotesAsync(GeneralStrategyParameters p_generalParams, List<string> p_tickers, CancellationToken p_canc = default(CancellationToken))
        {
            //- SPY 300K CSV SQLqueryTime (local server), msecond times for  (for Azure in-house datacenter, these will be less)
            //All data: Open, High, Low, Close, Volume : 886, 706, 1237, 761, 727, Avg = 863
            //Only ClosePrice: 662, 680, 702, 820, 663, 692, Avg = 703
            // if the Website is not local, but it is in the same Azure datacenter as the SQL center
            //SQL query time (All OHLCV data): msec: 612, 614, 667, 772, 632, 613, 665, 662, Avg = 654
            //SQL query time (only Close data): msec: 623, 624, 704, 614, 615, 621, 621, 722, 636, Avg = 642
            //Conclusion:downloading only Closeprice from SQL, we can save 100msec (LocalServer website (non-datacenter website)) or 15msec (Azure server website when SQL server is very close), still worth it
            ushort sqlReturnedColumns = QuoteRequest.TDC;       // QuoteRequest.All or QuoteRequest.TDOHLCVS

            //var sqlReturnTask = GetHistQuotesAsync(p_tickers.Select(r => new QuoteRequest { Ticker = r, nQuotes = Int32.MaxValue, NonAdjusted = false, ReturnedColumns = sqlReturnedColumns }), HQCommon.AssetType.Stock, true); // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers
            var sqlReturnTask = GetHistQuotesAsync(p_generalParams, p_tickers, sqlReturnedColumns);

            Task<Tuple<IList<double?>, TimeSpan>> realtimeReturnTask = null;
            if (p_generalParams.endDateUtc >= DateTime.UtcNow)
                realtimeReturnTask = GetRealtimesQuotesAsync(p_tickers);

            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");

            // Wait for the tasks to complete.            // ... Display its results.
            //var combinedAsyncTasksResults = await Task.WhenAll(sqlReturnTask, realtimeReturnTask); this cannot be done now, because the return values are different
            if (realtimeReturnTask != null)
                await Task.WhenAll(sqlReturnTask, realtimeReturnTask);  // otherwise, the next await will wait the historical data
            
            var sqlReturnData = await sqlReturnTask;  //as they have all definitely finished, you could also use Task.Value, "However, I recommend using await because it's clearly correct, while Result can cause problems in other scenarios."
            Tuple<IList<double?>, TimeSpan> realtimeReturnData = null;
            if (realtimeReturnTask != null)
                realtimeReturnData = await realtimeReturnTask; //as they have all definitely finished, you could also use Task.Value, "However, I recommend using await because it's clearly correct, while Result can cause problems in other scenarios."

            var sqlReturn = sqlReturnData.Item1;
            List<List<DailyData>> returnQuotes = null;
            // sql query of "VXX.SQ" gives back tickers of VXX and also tickers of "VXX.SQ"
            int closePriceIndex = -1;
            if (sqlReturnedColumns == QuoteRequest.TDOHLCVS)
                closePriceIndex = 5;
            else if (sqlReturnedColumns == QuoteRequest.TDC)
                closePriceIndex = 2;
            else
                throw new NotImplementedException();

            returnQuotes = p_tickers.Select(ticker =>
            {
                string tickerWithoutDotSQ = "";
                if (ticker.EndsWith(".SQ"))
                    tickerWithoutDotSQ = ticker.Substring(0, ticker.Length - ".SQ".Length);
                return sqlReturn.Where(row => (string)row[0] == ticker || (string)row[0] == tickerWithoutDotSQ).Select(
                    row => new DailyData()
                        {
                            Date = ((DateTime)row[1]),
                            ClosePrice = (double)Convert.ToDecimal(row[closePriceIndex])  // row[2] is object(double) if it is a stock (because Adjustment multiplier), and object(float) if it is Indices. However Convert.ToDouble(row[2]) would convert 16.66 to 16.6599999
                        }).ToList();
            }).ToList();

            if (realtimeReturnData != null)
            {
                var todayDate = DateTime.UtcNow.Date;
                var realtimeReturn = realtimeReturnData.Item1;
                for (int i = 0; i < p_tickers.Count(); i++)
                {
                    if (realtimeReturn[i] != null)
                    {
                        int todayInd = returnQuotes[i].FindLastIndex(r => r.Date == todayDate);
                        if (todayInd == -1) // if it is missing
                        {
                            returnQuotes[i].Add(new DailyData() { Date = todayDate, ClosePrice = (double)realtimeReturn[i] });
                        }
                        else // if it is already in the array, overwrite it
                        {
                            returnQuotes[i][todayInd].ClosePrice = (double)realtimeReturn[i];
                        }
                    }
                }
            }

            return new Tuple<IList<List<DailyData>>, TimeSpan, TimeSpan>(returnQuotes, sqlReturnData.Item2, (realtimeReturnData != null)?realtimeReturnData.Item2:TimeSpan.Zero);
        }
        public static async Task <string> GenerateQuickTesterResponse(GeneralStrategyParameters p_generalParams, string p_strategyName, string p_params)
        {
            Stopwatch stopWatchTotalResponse = Stopwatch.StartNew();

            if (p_strategyName != "LETFDiscrepancy1" && p_strategyName != "LETFDiscrepancy2" && p_strategyName != "LETFDiscrepancy3")
            {
                return(null);
            }

            string strategyParams = p_params;
            int    ind            = -1;

            string etfPairs = null;

            if (strategyParams.StartsWith("ETFPairs=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("ETFPairs=".Length);
                ind            = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    return(@"{ ""errorMessage"":  ""Error: uriQuery.IndexOf('&') 2. Uri: " + strategyParams + @""" }");
                }
                etfPairs       = strategyParams.Substring(0, ind);
                strategyParams = strategyParams.Substring(ind + 1);
            }

            string rebalancingFrequency = null;

            if (strategyParams.StartsWith("rebalancingFrequency=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("rebalancingFrequency=".Length);
                ind            = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                rebalancingFrequency = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                {
                    strategyParams = strategyParams.Substring(ind + 1);
                }
                else
                {
                    strategyParams = "";
                }
            }

            ind = etfPairs.IndexOf('-');
            if (ind == -1)
            {
                return(@"{ ""errorMessage"":  ""Error: cannot find tickers in : " + etfPairs + @""" }");
            }

            string bullishTicker = etfPairs.Substring(0, ind);
            string bearishTicker = etfPairs.Substring(ind + 1);

            // startDates
            // URE: Feb 2, 2007
            // SRS: Feb 1, 2007
            // XIV: Nov 30, 2010
            // VXX: Jan 30, 2009
            // FAS: Nov 19, 2008
            // FAZ: Nov 19, 2008
            Stopwatch stopWatch        = Stopwatch.StartNew();
            var       getAllQuotesTask = StrategiesCommon.GetHistoricalAndRealtimesQuotesAsync(p_generalParams, (new string[] { bullishTicker, bearishTicker }).ToList());

            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");
            var getAllQuotesData = await getAllQuotesTask;

            stopWatch.Stop();


            string htmlNoteFromStrategy = "", noteToUserCheckData = "", noteToUserBacktest = "", debugMessage = "", errorMessage = "";

            List <DailyData> pv = null;

            if (String.Equals(p_strategyName, "LETFDiscrepancy1", StringComparison.InvariantCultureIgnoreCase))
            {
                pv = DoBacktestExample(getAllQuotesData.Item1, bullishTicker, bearishTicker, rebalancingFrequency);
            }
            else
            {
                pv = DoBacktestBasic(getAllQuotesData.Item1, bullishTicker, bearishTicker, p_strategyName, rebalancingFrequency, ref noteToUserCheckData, ref htmlNoteFromStrategy);
            }


            stopWatchTotalResponse.Stop();
            StrategyResult strategyResult = StrategiesCommon.CreateStrategyResultFromPV(pv,
                                                                                        htmlNoteFromStrategy + ". " + noteToUserCheckData + "***" + noteToUserBacktest, errorMessage,
                                                                                        debugMessage + String.Format("SQL query time: {0:000}ms", getAllQuotesData.Item2.TotalMilliseconds) + String.Format(", RT query time: {0:000}ms", getAllQuotesData.Item3.TotalMilliseconds) + String.Format(", All query time: {0:000}ms", stopWatch.Elapsed.TotalMilliseconds) + String.Format(", TotalC#Response: {0:000}ms", stopWatchTotalResponse.Elapsed.TotalMilliseconds));
            string jsonReturn = JsonConvert.SerializeObject(strategyResult);

            return(jsonReturn);
            //{
            //  "Name": "Apple",
            //  "Expiry": "2008-12-28T00:00:00",
            //  "Sizes": [
            //    "Small"
            //  ]
            //}

            //returnStr = "[" + String.Join(Environment.NewLine,
            //    (await Tools.GetHistoricalQuotesAsync(new[] {
            //        new QuoteRequest { Ticker = "VXX", nQuotes = 2, StartDate = new DateTime(2011,1,1), NonAdjusted = true },
            //        new QuoteRequest { Ticker = "SPY", nQuotes = 3 }
            //    }, HQCommon.AssetType.Stock))
            //    .Select(row => String.Join(",", row))) + "]";

            //returnStr = returnStr.Replace(" 00:00:00", "");
            //returnStr = returnStr.Replace("\n", ",");

            //return @"[{""Symbol"":""VXX""},{""Symbol"":""^VIX"",""LastUtc"":""2015-01-08T19:25:48"",""Last"":17.45,""UtcTimeType"":""LastChangedTime""}]";
        }
Exemple #3
0
        // if I have Get(), I cannot have GetAllRtp(), as it will be Duplicate resolve possibility and I got an exception.
        //// IIS can handle if the return is a Task lst, not a HttpActionResult. It is needed for async SQL examples from Robert
        public async Task<HttpResponseMessage> Get()
        {
            string jsonpCallback = null;
            var response = this.Request.CreateResponse(HttpStatusCode.OK);
            try
            {
                string uriQuery = this.Url.Request.RequestUri.Query;    // "?s=VXX,XIV,^vix&f=ab&o=csv" from the URL http://localhost:58213/api/rtp?s=VXX,XIV,^vix&f=ab&o=csv

                if (uriQuery.Length > 8192)
                {//When you try to pass a string longer than 8192 charachters, a faultException will be thrown. There is a solution, but I don't want
                    throw new Exception("Error caught by WebApi Get():: uriQuery is longer than 8192: we don't process that. Uri: " + uriQuery);
                }

                uriQuery = uriQuery.Substring(1);   // remove '?'
                uriQuery = uriQuery.Replace("%20", " ").Replace("%5E", "^");    // de-coding from URL to normal things

                int ind = -1;
                if (uriQuery.StartsWith("jsonp=", StringComparison.InvariantCultureIgnoreCase))
                {
                    uriQuery = uriQuery.Substring("jsonp=".Length);
                    ind = uriQuery.IndexOf('&');
                    if (ind == -1)
                    {
                        throw new Exception("Error: uriQuery.IndexOf('&') 2. Uri: " + uriQuery);
                    }
                    jsonpCallback = uriQuery.Substring(0, ind);
                    uriQuery = uriQuery.Substring(ind + 1);
                }

                if (!uriQuery.StartsWith("StartDate=", StringComparison.InvariantCultureIgnoreCase))
                {
                    throw new Exception("Error: StartDate= was not found. Uri: " + uriQuery);
                }
                uriQuery = uriQuery.Substring("StartDate=".Length);
                ind = uriQuery.IndexOf('&');
                if (ind == -1)
                {
                    ind = uriQuery.Length;
                }
                string startDateStr = uriQuery.Substring(0, ind);
                if (ind < uriQuery.Length)  // if we are not at the end of the string
                    uriQuery = uriQuery.Substring(ind + 1);
                else
                    uriQuery = "";

                if (!uriQuery.StartsWith("EndDate=", StringComparison.InvariantCultureIgnoreCase))
                {
                    throw new Exception("Error: EndDate= was not found. Uri: " + uriQuery);
                }
                uriQuery = uriQuery.Substring("EndDate=".Length);
                ind = uriQuery.IndexOf('&');
                if (ind == -1)
                {
                    ind = uriQuery.Length;
                }
                string endDateStr = uriQuery.Substring(0, ind);
                if (ind < uriQuery.Length)  // if we are not at the end of the string
                    uriQuery = uriQuery.Substring(ind + 1);
                else
                    uriQuery = "";


                if (!uriQuery.StartsWith("strategy=", StringComparison.InvariantCultureIgnoreCase))
                {
                    throw new Exception("Error: strategy= was not found. Uri: " + uriQuery);
                }
                uriQuery = uriQuery.Substring("strategy=".Length);
                ind = uriQuery.IndexOf('&');
                if (ind == -1)
                {
                    ind = uriQuery.Length;
                }
                string strategyName = uriQuery.Substring(0, ind);
                if (ind < uriQuery.Length)  // if we are not at the end of the string
                    uriQuery = uriQuery.Substring(ind + 1);
                else
                    uriQuery = "";


                string strategyParams = uriQuery;

                DateTime startDate = DateTime.MinValue;
                if (startDateStr.Length != 0)
                {
                    if (!DateTime.TryParse(startDateStr, out startDate))
                        throw new Exception("Error: startDateStr couldn't be converted: " + uriQuery);
                }
                DateTime endDate = DateTime.MaxValue;
                if (endDateStr.Length != 0)
                {
                    if (!DateTime.TryParse(endDateStr, out endDate))
                        throw new Exception("Error: endDateStr couldn't be converted: " + uriQuery);
                }

                GeneralStrategyParameters generalParams = new GeneralStrategyParameters() { startDateUtc = startDate, endDateUtc = endDate };

                string jsonString = (await VXX_SPY_Controversial.GenerateQuickTesterResponse(generalParams, strategyName, strategyParams));
                if (jsonString == null)
                    jsonString = (await LEtfDistcrepancy.GenerateQuickTesterResponse(generalParams, strategyName, strategyParams));
                if (jsonString == null)
                    jsonString = (await TotM.GenerateQuickTesterResponse(generalParams, strategyName, strategyParams));

                if (jsonString == null)
                    throw new Exception("Strategy was not found in the WebApi: " + strategyName);

                return ResponseBuilder(jsonpCallback, jsonString);
            }
            catch (Exception e)
            {
                return ResponseBuilder(jsonpCallback, @"{ ""errorMessage"":  ""Exception caught by WebApi Get(): " + e.Message + @""" }");
            }
        }
        //public static async Task<Tuple<IList<object[]>,TimeSpan>> GetHistQuotesAsync(IEnumerable<QuoteRequest> p_req, HQCommon.AssetType p_at, bool? p_isAscendingDates = null, CancellationToken p_canc = default(CancellationToken))
        public static async Task<Tuple<List<object[]>, TimeSpan>> GetHistQuotesAsync(GeneralStrategyParameters p_generalParams, List<string> p_tickers, ushort p_sqlReturnedColumns)
        {
            List<string> stockTickers = p_tickers.Where(r => !r.StartsWith("^")).ToList();
            List<string> indicesTickers = p_tickers.Where(r => r.StartsWith("^")).ToList();

            TimeZoneInfo etZone = null;

            int requestNQuotes = Int32.MaxValue;
            DateTime? requestStartDateExcgLocal = null, requestEndDateExcgLocal = null;
            if (p_generalParams.startDateUtc != DateTime.MinValue)
            {
                ConvertUtcToExchangeLocal(p_generalParams.startDateUtc, ref etZone, ref requestStartDateExcgLocal);
            }
            if (p_generalParams.endDateUtc != DateTime.MaxValue)
            {
                ConvertUtcToExchangeLocal(p_generalParams.endDateUtc, ref etZone, ref requestEndDateExcgLocal);
            }

            Stopwatch stopWatch = Stopwatch.StartNew();

            Task<IList<object[]>> stocksSqlReturnTask = null, indicesSqlReturnTask = null; ;
            IList<object[]> stocksSqlReturn = null, indicesSqlReturn = null;
            if (stockTickers.Count != 0)
                stocksSqlReturnTask = Tools.GetHistoricalQuotesAsync(stockTickers.Select(r => new QuoteRequest { Ticker = r, nQuotes = requestNQuotes, StartDate = requestStartDateExcgLocal, EndDate = requestEndDateExcgLocal, NonAdjusted = false, ReturnedColumns = p_sqlReturnedColumns }), HQCommon.AssetType.Stock, true); // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers
            if (indicesTickers.Count != 0)
                indicesSqlReturnTask = Tools.GetHistoricalQuotesAsync(indicesTickers.Select(r => new QuoteRequest { Ticker = r, nQuotes = requestNQuotes, StartDate = requestStartDateExcgLocal, EndDate = requestEndDateExcgLocal, NonAdjusted = false, ReturnedColumns = p_sqlReturnedColumns }), HQCommon.AssetType.BenchmarkIndex, true); // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers

            if (stockTickers.Count != 0)
                stocksSqlReturn = await stocksSqlReturnTask;
            if (indicesTickers.Count != 0)
                indicesSqlReturn = await indicesSqlReturnTask;
            stopWatch.Stop();
            TimeSpan historicalQueryTimeSpan = stopWatch.Elapsed;

            List<object[]> sqlReturn = null;
            if (stocksSqlReturn != null)
                sqlReturn = stocksSqlReturn.ToList();   // the return is a List() already if you look deeper in the implementation
            if (indicesSqlReturn != null)
            {
                if (sqlReturn == null)
                    sqlReturn = indicesSqlReturn.ToList();
                else
                    sqlReturn.AddRange(indicesSqlReturn.ToList());
            }

            return new Tuple<List<object[]>, TimeSpan>(sqlReturn, historicalQueryTimeSpan);
        }
Exemple #5
0
        public static async Task<string> GenerateQuickTesterResponse(GeneralStrategyParameters p_generalParams, string p_strategyName, string p_params)
        {
            Stopwatch stopWatchTotalResponse = Stopwatch.StartNew();

            if (p_strategyName != "TotM")
                return null;

            string strategyParams = p_params;
            int ind = -1;

            string bullishTradingInstrument = null;
            if (strategyParams.StartsWith("BullishTradingInstrument=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("BullishTradingInstrument=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                bullishTradingInstrument = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string dailyMarketDirectionMaskSummerTotM = null;
            if (strategyParams.StartsWith("DailyMarketDirectionMaskSummerTotM=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("DailyMarketDirectionMaskSummerTotM=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                dailyMarketDirectionMaskSummerTotM = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string dailyMarketDirectionMaskSummerTotMM = null;
            if (strategyParams.StartsWith("DailyMarketDirectionMaskSummerTotMM=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("DailyMarketDirectionMaskSummerTotMM=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                dailyMarketDirectionMaskSummerTotMM = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string dailyMarketDirectionMaskWinterTotM = null;
            if (strategyParams.StartsWith("DailyMarketDirectionMaskWinterTotM=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("DailyMarketDirectionMaskWinterTotM=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                dailyMarketDirectionMaskWinterTotM = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string dailyMarketDirectionMaskWinterTotMM = null;
            if (strategyParams.StartsWith("DailyMarketDirectionMaskWinterTotMM=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("DailyMarketDirectionMaskWinterTotMM=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                dailyMarketDirectionMaskWinterTotMM = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }

            //bullishTradingInstrument = bullishTradingInstrument.Replace("%20", " ");
            ind = bullishTradingInstrument.IndexOf(' ');        // "long SPY", "long QQQ", "short VXX"
            Utils.StrongAssert(ind != -1 && ind != (bullishTradingInstrument.Length - 1), "bullishTradingInstrument parameter cannot be interpreted: " + bullishTradingInstrument);
            string stock = bullishTradingInstrument.Substring(ind + 1);
            string longOrShortOnBullish = bullishTradingInstrument.Substring(0, ind);


            Stopwatch stopWatch = Stopwatch.StartNew();
            var getAllQuotesTask = StrategiesCommon.GetHistoricalAndRealtimesQuotesAsync(p_generalParams, (new string[] { stock }).ToList());
            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");
            var getAllQuotesData = await getAllQuotesTask;
            stopWatch.Stop();

            var stockQoutes = getAllQuotesData.Item1[0];
            
            string noteToUserCheckData = "", noteToUserBacktest = "", debugMessage = "", errorMessage = "";
            List<DailyData> pv = StrategiesCommon.DetermineBacktestPeriodCheckDataCorrectness(stockQoutes, ref noteToUserCheckData);


            if (String.Equals(p_strategyName, "TotM", StringComparison.InvariantCultureIgnoreCase))
            {
                DoBacktestInTheTimeInterval_TotM(stockQoutes, longOrShortOnBullish, dailyMarketDirectionMaskSummerTotM, dailyMarketDirectionMaskSummerTotMM, dailyMarketDirectionMaskWinterTotM, dailyMarketDirectionMaskWinterTotMM, pv, ref noteToUserBacktest);
            }
            //else if (String.Equals(p_strategyName, "LETFDiscrepancy3", StringComparison.InvariantCultureIgnoreCase))
            //{
            //    //DoBacktestInTheTimeInterval_AddToTheWinningSideWithLeverage(bullishQoutes, bearishQoutes, p_rebalancingFrequency, pv, ref noteToUserBacktest);
            //}
            else
            {
                
            }

            stopWatchTotalResponse.Stop();
            StrategyResult strategyResult = StrategiesCommon.CreateStrategyResultFromPV(pv,
                //"Number of positions: <span> XXXX </span><br><br>test",
                //"Number of positions: <span> {{nPositions}} </span><br><br>test",
                "<b>Bullish</b> (Bearish) on days when mask is Up (Down).<br>" + noteToUserCheckData
                + ((!String.IsNullOrEmpty(noteToUserCheckData) && !String.IsNullOrEmpty(noteToUserBacktest)) ? "<br>" : "")
                + noteToUserBacktest, 
                errorMessage, debugMessage + String.Format("SQL query time: {0:000}ms", getAllQuotesData.Item2.TotalMilliseconds) + String.Format(", RT query time: {0:000}ms", getAllQuotesData.Item3.TotalMilliseconds) + String.Format(", All query time: {0:000}ms", stopWatch.Elapsed.TotalMilliseconds) + String.Format(", TotalC#Response: {0:000}ms", stopWatchTotalResponse.Elapsed.TotalMilliseconds));
            string jsonReturn = JsonConvert.SerializeObject(strategyResult);
            return jsonReturn;
        }
        public static async Task<string> GenerateQuickTesterResponse(GeneralStrategyParameters p_generalParams, string p_strategyName, string p_params)
        {
            Stopwatch stopWatchTotalResponse = Stopwatch.StartNew();

            if (p_strategyName != "LETFDiscrepancy1" && p_strategyName != "LETFDiscrepancy2" && p_strategyName != "LETFDiscrepancy3")
                return null;

            string strategyParams = p_params;
            int ind = -1;

            string etfPairs = null;
            if (strategyParams.StartsWith("ETFPairs=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("ETFPairs=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    return @"{ ""errorMessage"":  ""Error: uriQuery.IndexOf('&') 2. Uri: " + strategyParams + @""" }";
                }
                etfPairs = strategyParams.Substring(0, ind);
                strategyParams = strategyParams.Substring(ind + 1);
            }

            string rebalancingFrequency = null;
            if (strategyParams.StartsWith("rebalancingFrequency=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("rebalancingFrequency=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                rebalancingFrequency = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }

            ind = etfPairs.IndexOf('-');
            if (ind == -1)
            {
                return @"{ ""errorMessage"":  ""Error: cannot find tickers in : " + etfPairs + @""" }";
            }

            string bullishTicker = etfPairs.Substring(0, ind);
            string bearishTicker = etfPairs.Substring(ind +1);

            // startDates
            // URE: Feb 2, 2007
            // SRS: Feb 1, 2007
            // XIV: Nov 30, 2010
            // VXX: Jan 30, 2009
            // FAS: Nov 19, 2008
            // FAZ: Nov 19, 2008
            Stopwatch stopWatch = Stopwatch.StartNew();
            var getAllQuotesTask = StrategiesCommon.GetHistoricalAndRealtimesQuotesAsync(p_generalParams, (new string[] { bullishTicker, bearishTicker }).ToList());
            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");
            var getAllQuotesData = await getAllQuotesTask;
            stopWatch.Stop();


            string htmlNoteFromStrategy = "", noteToUserCheckData = "", noteToUserBacktest = "", debugMessage = "", errorMessage = "";

            List<DailyData> pv = null;
            if (String.Equals(p_strategyName, "LETFDiscrepancy1", StringComparison.InvariantCultureIgnoreCase))
            {
                pv = DoBacktestExample(getAllQuotesData.Item1, bullishTicker, bearishTicker, rebalancingFrequency);
            }
            else
            {
                pv = DoBacktestBasic(getAllQuotesData.Item1, bullishTicker, bearishTicker, p_strategyName, rebalancingFrequency, ref noteToUserCheckData, ref htmlNoteFromStrategy);
            }


            stopWatchTotalResponse.Stop();
            StrategyResult strategyResult = StrategiesCommon.CreateStrategyResultFromPV(pv,
                htmlNoteFromStrategy + ". " + noteToUserCheckData + "***" + noteToUserBacktest, errorMessage,
                debugMessage + String.Format("SQL query time: {0:000}ms", getAllQuotesData.Item2.TotalMilliseconds) + String.Format(", RT query time: {0:000}ms", getAllQuotesData.Item3.TotalMilliseconds) + String.Format(", All query time: {0:000}ms", stopWatch.Elapsed.TotalMilliseconds) + String.Format(", TotalC#Response: {0:000}ms", stopWatchTotalResponse.Elapsed.TotalMilliseconds));
            string jsonReturn = JsonConvert.SerializeObject(strategyResult);
            return jsonReturn;
            //{
            //  "Name": "Apple",
            //  "Expiry": "2008-12-28T00:00:00",
            //  "Sizes": [
            //    "Small"
            //  ]
            //}

            //returnStr = "[" + String.Join(Environment.NewLine,
            //    (await Tools.GetHistoricalQuotesAsync(new[] {
            //        new QuoteRequest { Ticker = "VXX", nQuotes = 2, StartDate = new DateTime(2011,1,1), NonAdjusted = true },
            //        new QuoteRequest { Ticker = "SPY", nQuotes = 3 }
            //    }, HQCommon.AssetType.Stock))
            //    .Select(row => String.Join(",", row))) + "]";

            //returnStr = returnStr.Replace(" 00:00:00", "");
            //returnStr = returnStr.Replace("\n", ",");

            //return @"[{""Symbol"":""VXX""},{""Symbol"":""^VIX"",""LastUtc"":""2015-01-08T19:25:48"",""Last"":17.45,""UtcTimeType"":""LastChangedTime""}]";
        }
        public static async Task<string> GenerateQuickTesterResponse(GeneralStrategyParameters p_generalParams, string p_strategyName, string p_params)
        {
            Stopwatch stopWatchTotalResponse = Stopwatch.StartNew();

            if (p_strategyName != "VXX_SPY_Controversial")
                return null;

            string strategyParams = p_params;
            int ind = -1;

            string spyMinPctMoveStr = null;
            if (strategyParams.StartsWith("SpyMinPctMove=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("SpyMinPctMove=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                spyMinPctMoveStr = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string vxxMinPctMoveStr = null;
            if (strategyParams.StartsWith("VxxMinPctMove=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("VxxMinPctMove=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                vxxMinPctMoveStr = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }
            string longOrShortTrade = null;
            if (strategyParams.StartsWith("LongOrShortTrade=", StringComparison.InvariantCultureIgnoreCase))
            {
                strategyParams = strategyParams.Substring("LongOrShortTrade=".Length);
                ind = strategyParams.IndexOf('&');
                if (ind == -1)
                {
                    ind = strategyParams.Length;
                }
                longOrShortTrade = strategyParams.Substring(0, ind);
                if (ind < strategyParams.Length)
                    strategyParams = strategyParams.Substring(ind + 1);
                else
                    strategyParams = "";
            }

            double spyMinPctMove;
            bool isParseSuccess = Double.TryParse(spyMinPctMoveStr, out spyMinPctMove);
            if (!isParseSuccess)
            {
                throw new Exception("Error: spyMinPctMoveStr as " + spyMinPctMoveStr + " cannot be converted to number.");
            }

            double vxxMinPctMove;
            isParseSuccess = Double.TryParse(vxxMinPctMoveStr, out vxxMinPctMove);
            if (!isParseSuccess)
            {
                throw new Exception("Error: vxxMinPctMoveStr as " + vxxMinPctMoveStr + " cannot be converted to number.");
            }


            Stopwatch stopWatch = Stopwatch.StartNew();
            var getAllQuotesTask = StrategiesCommon.GetHistoricalAndRealtimesQuotesAsync(p_generalParams, (new string[] { "VXX", "SPY" }).ToList());
            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");
            var getAllQuotesData = await getAllQuotesTask;
            stopWatch.Stop();

            var vxxQoutes = getAllQuotesData.Item1[0];
            var spyQoutes = getAllQuotesData.Item1[1];

            string noteToUserCheckData = "", noteToUserBacktest = "", debugMessage = "", errorMessage = "";
            List<DailyData> pv = StrategiesCommon.DetermineBacktestPeriodCheckDataCorrectness(vxxQoutes, spyQoutes, ref noteToUserCheckData);


            if (String.Equals(p_strategyName, "VXX_SPY_Controversial", StringComparison.InvariantCultureIgnoreCase))
            {
                DoBacktestInTheTimeInterval_VXX_SPY_Controversial(vxxQoutes, spyQoutes, spyMinPctMove, vxxMinPctMove, longOrShortTrade, pv, ref noteToUserBacktest);
            }
            //else if (String.Equals(p_strategyName, "LETFDiscrepancy3", StringComparison.InvariantCultureIgnoreCase))
            //{
            //    //DoBacktestInTheTimeInterval_AddToTheWinningSideWithLeverage(bullishQoutes, bearishQoutes, p_rebalancingFrequency, pv, ref noteToUserBacktest);
            //}
            else
            {

            }

            stopWatchTotalResponse.Stop();
            StrategyResult strategyResult = StrategiesCommon.CreateStrategyResultFromPV(pv,
               noteToUserCheckData + "***" + noteToUserBacktest, errorMessage,
               debugMessage + String.Format("SQL query time: {0:000}ms", getAllQuotesData.Item2.TotalMilliseconds) + String.Format(", RT query time: {0:000}ms", getAllQuotesData.Item3.TotalMilliseconds) + String.Format(", All query time: {0:000}ms", stopWatch.Elapsed.TotalMilliseconds) + String.Format(", TotalC#Response: {0:000}ms", stopWatchTotalResponse.Elapsed.TotalMilliseconds));
            string jsonReturn = JsonConvert.SerializeObject(strategyResult);
            return jsonReturn;
        }
        public static async Task <Tuple <IList <List <DailyData> >, TimeSpan, TimeSpan> > GetHistoricalAndRealtimesQuotesAsync(GeneralStrategyParameters p_generalParams, List <string> p_tickers, CancellationToken p_canc = default(CancellationToken))
        {
            //- SPY 300K CSV SQLqueryTime (local server), msecond times for  (for Azure in-house datacenter, these will be less)
            //All data: Open, High, Low, Close, Volume : 886, 706, 1237, 761, 727, Avg = 863
            //Only ClosePrice: 662, 680, 702, 820, 663, 692, Avg = 703
            // if the Website is not local, but it is in the same Azure datacenter as the SQL center
            //SQL query time (All OHLCV data): msec: 612, 614, 667, 772, 632, 613, 665, 662, Avg = 654
            //SQL query time (only Close data): msec: 623, 624, 704, 614, 615, 621, 621, 722, 636, Avg = 642
            //Conclusion:downloading only Closeprice from SQL, we can save 100msec (LocalServer website (non-datacenter website)) or 15msec (Azure server website when SQL server is very close), still worth it
            ushort sqlReturnedColumns = QuoteRequest.TDC;       // QuoteRequest.All or QuoteRequest.TDOHLCVS

            //var sqlReturnTask = GetHistQuotesAsync(p_tickers.Select(r => new QuoteRequest { Ticker = r, nQuotes = Int32.MaxValue, NonAdjusted = false, ReturnedColumns = sqlReturnedColumns }), HQCommon.AssetType.Stock, true); // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers
            var sqlReturnTask = GetHistQuotesAsync(p_generalParams, p_tickers, sqlReturnedColumns);

            Task <Tuple <IList <double?>, TimeSpan> > realtimeReturnTask = null;

            if (p_generalParams.endDateUtc >= DateTime.UtcNow)
            {
                realtimeReturnTask = GetRealtimesQuotesAsync(p_tickers);
            }

            // Control returns here before GetHistoricalQuotesAsync() returns.  // ... Prompt the user.
            Console.WriteLine("Please wait patiently while I do SQL and realtime price queries.");

            // Wait for the tasks to complete.            // ... Display its results.
            //var combinedAsyncTasksResults = await Task.WhenAll(sqlReturnTask, realtimeReturnTask); this cannot be done now, because the return values are different
            if (realtimeReturnTask != null)
            {
                await Task.WhenAll(sqlReturnTask, realtimeReturnTask); // otherwise, the next await will wait the historical data
            }
            var sqlReturnData = await sqlReturnTask;                   //as they have all definitely finished, you could also use Task.Value, "However, I recommend using await because it's clearly correct, while Result can cause problems in other scenarios."
            Tuple <IList <double?>, TimeSpan> realtimeReturnData = null;

            if (realtimeReturnTask != null)
            {
                realtimeReturnData = await realtimeReturnTask; //as they have all definitely finished, you could also use Task.Value, "However, I recommend using await because it's clearly correct, while Result can cause problems in other scenarios."
            }
            var sqlReturn = sqlReturnData.Item1;
            List <List <DailyData> > returnQuotes = null;
            // sql query of "VXX.SQ" gives back tickers of VXX and also tickers of "VXX.SQ"
            int closePriceIndex = -1;

            if (sqlReturnedColumns == QuoteRequest.TDOHLCVS)
            {
                closePriceIndex = 5;
            }
            else if (sqlReturnedColumns == QuoteRequest.TDC)
            {
                closePriceIndex = 2;
            }
            else
            {
                throw new NotImplementedException();
            }

            returnQuotes = p_tickers.Select(ticker =>
            {
                string tickerWithoutDotSQ = "";
                if (ticker.EndsWith(".SQ"))
                {
                    tickerWithoutDotSQ = ticker.Substring(0, ticker.Length - ".SQ".Length);
                }
                return(sqlReturn.Where(row => (string)row[0] == ticker || (string)row[0] == tickerWithoutDotSQ).Select(
                           row => new DailyData()
                {
                    Date = ((DateTime)row[1]),
                    ClosePrice = (double)Convert.ToDecimal(row[closePriceIndex])          // row[2] is object(double) if it is a stock (because Adjustment multiplier), and object(float) if it is Indices. However Convert.ToDouble(row[2]) would convert 16.66 to 16.6599999
                }).ToList());
            }).ToList();

            if (realtimeReturnData != null)
            {
                var todayDate      = DateTime.UtcNow.Date;
                var realtimeReturn = realtimeReturnData.Item1;
                for (int i = 0; i < p_tickers.Count(); i++)
                {
                    if (realtimeReturn[i] != null)
                    {
                        int todayInd = returnQuotes[i].FindLastIndex(r => r.Date == todayDate);
                        if (todayInd == -1) // if it is missing
                        {
                            returnQuotes[i].Add(new DailyData()
                            {
                                Date = todayDate, ClosePrice = (double)realtimeReturn[i]
                            });
                        }
                        else // if it is already in the array, overwrite it
                        {
                            returnQuotes[i][todayInd].ClosePrice = (double)realtimeReturn[i];
                        }
                    }
                }
            }

            return(new Tuple <IList <List <DailyData> >, TimeSpan, TimeSpan>(returnQuotes, sqlReturnData.Item2, (realtimeReturnData != null)?realtimeReturnData.Item2:TimeSpan.Zero));
        }
        //public static async Task<Tuple<IList<object[]>,TimeSpan>> GetHistQuotesAsync(IEnumerable<QuoteRequest> p_req, HQCommon.AssetType p_at, bool? p_isAscendingDates = null, CancellationToken p_canc = default(CancellationToken))
        public static async Task <Tuple <List <object[]>, TimeSpan> > GetHistQuotesAsync(GeneralStrategyParameters p_generalParams, List <string> p_tickers, ushort p_sqlReturnedColumns)
        {
            List <string> stockTickers   = p_tickers.Where(r => !r.StartsWith("^")).ToList();
            List <string> indicesTickers = p_tickers.Where(r => r.StartsWith("^")).ToList();

            TimeZoneInfo etZone = null;

            int      requestNQuotes = Int32.MaxValue;
            DateTime?requestStartDateExcgLocal = null, requestEndDateExcgLocal = null;

            if (p_generalParams.startDateUtc != DateTime.MinValue)
            {
                ConvertUtcToExchangeLocal(p_generalParams.startDateUtc, ref etZone, ref requestStartDateExcgLocal);
            }
            if (p_generalParams.endDateUtc != DateTime.MaxValue)
            {
                ConvertUtcToExchangeLocal(p_generalParams.endDateUtc, ref etZone, ref requestEndDateExcgLocal);
            }

            Stopwatch stopWatch = Stopwatch.StartNew();

            Task <IList <object[]> > stocksSqlReturnTask = null, indicesSqlReturnTask = null;;
            IList <object[]>         stocksSqlReturn = null, indicesSqlReturn = null;

            if (stockTickers.Count != 0)
            {
                stocksSqlReturnTask = Tools.GetHistoricalQuotesAsync(stockTickers.Select(r => new QuoteRequest {
                    Ticker = r, nQuotes = requestNQuotes, StartDate = requestStartDateExcgLocal, EndDate = requestEndDateExcgLocal, NonAdjusted = false, ReturnedColumns = p_sqlReturnedColumns
                }), HQCommon.AssetType.Stock, true);                                                                                                                                                                                                                                                                              // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers
            }
            if (indicesTickers.Count != 0)
            {
                indicesSqlReturnTask = Tools.GetHistoricalQuotesAsync(indicesTickers.Select(r => new QuoteRequest {
                    Ticker = r, nQuotes = requestNQuotes, StartDate = requestStartDateExcgLocal, EndDate = requestEndDateExcgLocal, NonAdjusted = false, ReturnedColumns = p_sqlReturnedColumns
                }), HQCommon.AssetType.BenchmarkIndex, true);                                                                                                                                                                                                                                                                                 // Ascending date order: TRUE, better to order it at the SQL server than locally. SQL has indexers
            }
            if (stockTickers.Count != 0)
            {
                stocksSqlReturn = await stocksSqlReturnTask;
            }
            if (indicesTickers.Count != 0)
            {
                indicesSqlReturn = await indicesSqlReturnTask;
            }
            stopWatch.Stop();
            TimeSpan historicalQueryTimeSpan = stopWatch.Elapsed;

            List <object[]> sqlReturn = null;

            if (stocksSqlReturn != null)
            {
                sqlReturn = stocksSqlReturn.ToList();   // the return is a List() already if you look deeper in the implementation
            }
            if (indicesSqlReturn != null)
            {
                if (sqlReturn == null)
                {
                    sqlReturn = indicesSqlReturn.ToList();
                }
                else
                {
                    sqlReturn.AddRange(indicesSqlReturn.ToList());
                }
            }

            return(new Tuple <List <object[]>, TimeSpan>(sqlReturn, historicalQueryTimeSpan));
        }