Beispiel #1
0
        public static TipRanksResult GetTipRanksResult(string symbol)
        {
            //Return a data object that contains:
            //Aggregated ratings data and ratings providers
            //Aggregated insider transactions data, insider names, insider ratings
            //Aggregated insitutional transactions data, hedge fund ratings, hedge fund names
            //Composite score of the above 3 items

            string         responseString = String.Empty;
            HttpWebRequest request        = (HttpWebRequest)HttpWebRequest.Create(TipRanksBaseUrl + "getData/" + symbol);

            request.Method = "GET";

            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                    responseString = reader.ReadToEnd();
                    response.Close();
                }
                TipRanksDataResponse trResponse = JsonConvert.DeserializeObject <TipRanksDataResponse>(responseString);

                //filter results to the last 3 months
                DateTime       startDate = DateTime.Now.AddMonths(-3);
                List <Insider> insiders  = trResponse.insiders
                                           .Where(x => DateTime.Compare(x.rDate, startDate) > 0)
                                           .ToList();
                List <ConsensusOverTime> bsns = trResponse.consensusOverTime
                                                .Where(x => DateTime.Compare(x.date, startDate) > 0)
                                                .ToList();
                List <Expert> ratings = trResponse.experts
                                        .Where(x => DateTime.Compare(x.ratings.FirstOrDefault().date, startDate) > 0 && x.rankings.FirstOrDefault().stars > 0)
                                        .ToList();


                //find the slope of the buy and sell consensuses (net)
                List <decimal> bsnYList = bsns
                                          .Select(x => Convert.ToDecimal(x.consensus - 3 + x.buy - x.sell))
                                          .ToList();

                List <decimal> bsnXList = new List <decimal>();
                int            counter  = 1;
                foreach (decimal bsn in bsnYList)
                {
                    bsnXList.Add(counter);
                    counter++;
                }

                decimal bsnSlope           = Indicators.GetSlope(bsnXList, bsnYList);
                decimal bsnSlopeMultiplier = 10.0M; //TwelveData.GetSlopeMultiplier(bsnSlope);

                //find the average rating
                List <decimal> ratingNums = ratings
                                            .Select(x => Convert.ToDecimal(x.rankings.FirstOrDefault().stars))
                                            .ToList();

                decimal averageRating = ratingNums.Sum() / ratingNums.Count;

                decimal priceTarget = Convert.ToDecimal(trResponse.portfolioHoldingData.priceTarget);
                decimal lastPrice   = Convert.ToDecimal(trResponse.prices[trResponse.prices.Length - 1].p);

                decimal ratingsComposite = 0;
                ratingsComposite += (averageRating / 6.0M) * 100;       //Get score using the average rating as a percentage of (max rating + 1)
                ratingsComposite += bsnSlope > 0 ? (bsnSlope * bsnSlopeMultiplier) + 5 : -(bsnSlope * bsnSlopeMultiplier) - 5;
                ratingsComposite += priceTarget > lastPrice ? 10 : -10; //Adjust with price target
                ratingsComposite  = Math.Min(ratingsComposite, 100);

                return(new TipRanksResult
                {
                    Insiders = insiders,
                    Institutions = trResponse.hedgeFundData,
                    ThirdPartyRatings = ratings,
                    ConsensusOverTime = bsns,
                    InsiderComposite = 0.0M,
                    InstitutionalComposite = 0.0M,
                    RatingsComposite = ratingsComposite,
                    PriceTarget = priceTarget
                });
            }
            catch (Exception e)
            {
                Debug.WriteLine("EXCEPTION CAUGHT: TipRanks.cs GetTipRanksResult for symbol " + symbol + ", message: " + e.Message);
                return(new TipRanksResult
                {
                    Insiders = null,
                    Institutions = null,
                    ThirdPartyRatings = null,
                    ConsensusOverTime = null,
                    InsiderComposite = 0.0M,
                    InstitutionalComposite = 0.0M,
                    RatingsComposite = 50.0M,
                    PriceTarget = 0.0M
                });
            }
        }
Beispiel #2
0
        public static ShortInterestResult GetShortInterest(string quandlResponse, int daysToCalculate)
        {
            decimal compositeScore = 0;
            JObject response       = JObject.Parse(quandlResponse);
            JObject dataset        = (JObject)response.GetValue("dataset");
            JArray  data           = (JArray)dataset.GetValue("data");

            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();
            Stack <decimal>  shortInterestYList = new Stack <decimal>();

            decimal shortInterestToday = 0;
            decimal totalVolume        = 0;
            decimal totalVolumeShort   = 0;

            foreach (JArray result in data)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal shortVal       = decimal.Parse(result[1].Value <string>());
                    decimal shortExemptVal = decimal.Parse(result[2].Value <string>());
                    decimal shortVolume    = shortVal + shortExemptVal;
                    decimal volume         = decimal.Parse(result[3].Value <string>());
                    decimal shortInterest  = (shortVolume / volume) * 100;

                    if (numberOfResults == 0)
                    {
                        shortInterestToday = shortInterest;
                    }

                    shortInterestYList.Push(shortInterest);
                    totalVolume      += volume;
                    totalVolumeShort += shortVolume;
                    numberOfResults++;

                    string shortDate = DateTime.Parse(result[0].Value <string>()).ToString("yyyy-MM-dd");
                    if (!dates.Contains(shortDate))
                    {
                        dates.Add(shortDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> shortXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                shortXList.Add(i);
            }

            List <decimal> shortYList           = shortInterestYList.ToList();
            decimal        shortSlope           = Indicators.GetSlope(shortXList, shortYList);
            decimal        shortSlopeMultiplier = Indicators.GetSlopeMultiplier(shortSlope);
            decimal        shortInterestAverage = (totalVolumeShort / totalVolume) * 100;

            //Add these bonuses to account for normal short interest fluctuations
            bool slightlyBearish   = (0.0M <= shortSlope && shortSlope <= 0.5M);
            bool moderatelyBearish = (0.5M <= shortSlope && shortSlope <= 1.0M);

            //calculate composite score based on the following values and weighted multipliers
            compositeScore += 100 - shortInterestAverage; //get score as 100 - short interest
            compositeScore += (shortSlope < 0) ? (shortSlope * shortSlopeMultiplier) + 20 : -5;
            compositeScore += (shortSlope > 0 && slightlyBearish) ? 15 : 0;
            compositeScore += (shortSlope > 0 && moderatelyBearish) ? 10 : 0;

            //Return ShortInterestResult
            return(new ShortInterestResult
            {
                TotalVolume = totalVolume,
                TotalVolumeShort = totalVolumeShort,
                ShortInterestPercentToday = shortInterestToday,
                ShortInterestPercentAverage = shortInterestAverage,
                ShortInterestSlope = shortSlope,
                ShortInterestCompositeScore = compositeScore
            });
        }
Beispiel #3
0
        //unused for now
        public static decimal GetOBVComposite(JArray resultSet, int daysToCalculate)
        {
            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();

            Stack <decimal> obvValueYList = new Stack <decimal>();
            decimal         obvSum        = 0;

            foreach (var result in resultSet)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal obvValue = decimal.Parse(result.Value <string>("obv"));

                    obvValueYList.Push(obvValue);
                    obvSum += obvValue;
                    numberOfResults++;

                    string resultDate = result.Value <string>("datetime");
                    string obvDate    = DateTime.Parse(resultDate).ToString("yyyy-MM-dd");
                    if (!dates.Contains(obvDate))
                    {
                        dates.Add(obvDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> obvXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                obvXList.Add(i);
            }

            List <decimal> obvYList           = obvValueYList.ToList();
            decimal        obvSlope           = Indicators.GetSlope(obvXList, obvYList);
            decimal        obvSlopeMultiplier = Indicators.GetSlopeMultiplier(obvSlope);

            List <decimal> zScores               = Indicators.GetZScores(obvYList);
            decimal        zScoreSlope           = Indicators.GetSlope(zScores, obvXList);
            decimal        zScoreSlopeMultiplier = Indicators.GetSlopeMultiplier(zScoreSlope);

            List <decimal> normalizedScores          = Indicators.GetNormalizedData(obvYList);
            decimal        normalizedSlope           = Indicators.GetSlope(normalizedScores, obvXList);
            decimal        normalizedSlopeMultiplier = Indicators.GetSlopeMultiplier(normalizedSlope);

            decimal obvAverage = obvSum / numberOfResults;

            //look for buy and sell signals
            bool obvHasBuySignal  = false;
            bool obvHasSellSignal = false;

            decimal obvPrev           = obvYList[0];
            bool    obvPrevIsNegative = obvPrev < 0;

            for (int i = 1; i < obvYList.Count(); i++)
            {
                decimal current           = obvYList[i];
                bool    currentIsNegative = current < 0;
                if (!currentIsNegative && obvPrevIsNegative)
                {
                    obvHasBuySignal = true;
                    if (obvHasSellSignal)
                    {
                        obvHasSellSignal = false; //cancel the previous sell signal if buy signal is most recent
                    }
                }
                else if (currentIsNegative && !obvPrevIsNegative)
                {
                    obvHasSellSignal = true;
                    if (obvHasBuySignal)
                    {
                        obvHasBuySignal = false; //cancel the previous buy signal if sell signal is most recent
                    }
                }
                obvPrev           = current;
                obvPrevIsNegative = obvPrev < 0;
            }

            //Start with the average of the 2 most recent OBV Normalized Scores
            //Only use the normalized scores if average OBV is greater than 0
            decimal baseValue;

            if (obvAverage > 0)
            {
                baseValue = ((normalizedScores[normalizedScores.Count - 1] + normalizedScores[normalizedScores.Count - 2]) / 2) * 100;
            }
            else
            {
                //ZScore bonus helps us score based on derivatives
                //Only add ZScoreBonus if it is positive, divide by 4 instead of 2 (which would be classic mean)
                decimal zScoreBonus = ((zScores[zScores.Count - 1] + zScores[zScores.Count - 2]) / 4) * 100;
                if (zScoreBonus < 0)
                {
                    zScoreBonus = 15; //pity points
                }
                baseValue = zScoreBonus;
            }

            //Add bonus if average OBV is greater than 0
            decimal obvAverageBonus = obvAverage > 0 ? 15 : 0;

            //Add bonus if OBV slope positive
            decimal obvSlopeBonus = (obvSlope > 0) ? 10 : 0;

            //calculate composite score based on the following values and weighted multipliers
            decimal composite = 0;

            composite += baseValue;
            composite += obvAverageBonus;
            composite += obvSlopeBonus;
            composite += (zScoreSlope > 0) ? (zScoreSlope * zScoreSlopeMultiplier) : 0;
            composite += (normalizedSlope > 0) ? (normalizedSlope * normalizedSlopeMultiplier) : 0;
            composite += (obvHasBuySignal) ? 15 : 0;
            composite += (obvHasSellSignal) ? -15 : 0;

            return(Math.Min(composite, 100)); //cap OBV composite at 100, no extra weight
        }
Beispiel #4
0
        public static decimal GetMACDComposite(JArray resultSet, int daysToCalculate)
        {
            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();

            Stack <decimal> macdHistYList   = new Stack <decimal>();
            Stack <decimal> macdBaseYList   = new Stack <decimal>();
            Stack <decimal> macdSignalYList = new Stack <decimal>();
            decimal         macdTotalHist   = 0;

            foreach (var result in resultSet)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal macdBaseValue      = decimal.Parse(result.Value <string>("macd"));
                    decimal macdSignalValue    = decimal.Parse(result.Value <string>("macd_signal"));
                    decimal macdHistogramValue = decimal.Parse(result.Value <string>("macd_hist"));

                    macdHistYList.Push(macdHistogramValue);
                    macdBaseYList.Push(macdBaseValue);
                    macdSignalYList.Push(macdSignalValue);
                    macdTotalHist += macdHistogramValue;
                    numberOfResults++;

                    string resultDate = result.Value <string>("datetime");
                    string macdDate   = DateTime.Parse(resultDate).ToString("yyyy-MM-dd");
                    if (!dates.Contains(macdDate))
                    {
                        dates.Add(macdDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> macdXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                macdXList.Add(i);
            }

            List <decimal> baseYList   = macdBaseYList.ToList();
            decimal        baseSlope   = Indicators.GetSlope(macdXList, baseYList);
            List <decimal> signalYList = macdSignalYList.ToList();
            decimal        signalSlope = Indicators.GetSlope(macdXList, signalYList);
            List <decimal> histYList   = macdHistYList.ToList();
            decimal        histSlope   = Indicators.GetSlope(macdXList, histYList);

            //look for buy and sell signals
            bool macdHasBuySignal  = false;
            bool macdHasSellSignal = false;

            decimal macdPrev           = histYList[0];
            bool    macdPrevIsNegative = macdPrev < 0;

            for (int i = 1; i < histYList.Count(); i++)
            {
                decimal current           = histYList[i];
                bool    currentIsNegative = current < 0;
                if (!currentIsNegative && macdPrevIsNegative)
                {
                    macdHasBuySignal = true;
                }
                else if (currentIsNegative && !macdPrevIsNegative)
                {
                    macdHasSellSignal = true;
                }
                macdPrev           = current;
                macdPrevIsNegative = macdPrev < 0;
            }
            decimal histSlopeMultiplier   = Indicators.GetSlopeMultiplier(histSlope);
            decimal baseSlopeMultiplier   = Indicators.GetSlopeMultiplier(baseSlope);
            decimal signalSlopeMultiplier = Indicators.GetSlopeMultiplier(signalSlope);

            decimal histBonus = Math.Min((macdTotalHist * 5) + 5, 20); //cap histBonus at 20

            //calculate composite score based on the following values and weighted multipliers
            decimal composite = 0;

            composite += (histSlope > -0.05M) ? (histSlope * histSlopeMultiplier) + 25 : 0;
            composite += (baseSlope > -0.05M) ? (baseSlope * baseSlopeMultiplier) + 10 : 0;
            composite += (signalSlope > -0.05M) ? (signalSlope * signalSlopeMultiplier) + 10 : 0;
            composite += (histBonus > 0) ? histBonus : 0; //Add histBonus if macdTotalHist is not negative
            composite += (macdHasBuySignal) ? 40 : 0;
            composite += (macdHasSellSignal) ? -40 : 0;

            return(composite);
        }
Beispiel #5
0
        public static decimal GetAROONComposite(JArray resultSet, int daysToCalculate)
        {
            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();

            Stack <decimal> aroonUpYList         = new Stack <decimal>();
            Stack <decimal> aroonDownYList       = new Stack <decimal>();
            Stack <decimal> aroonOscillatorYList = new Stack <decimal>();
            decimal         aroonUpTotal         = 0;
            decimal         aroonDownTotal       = 0;

            foreach (var result in resultSet)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal aroonUpVal   = decimal.Parse(result.Value <string>("aroon_up"));
                    decimal aroonDownVal = decimal.Parse(result.Value <string>("aroon_down"));
                    aroonUpYList.Push(aroonUpVal);
                    aroonDownYList.Push(aroonDownVal);
                    aroonOscillatorYList.Push(aroonUpVal - aroonDownVal);
                    aroonUpTotal   += aroonUpVal;
                    aroonDownTotal += aroonDownVal;
                    numberOfResults++;

                    string resultDate = result.Value <string>("datetime");
                    string aroonDate  = DateTime.Parse(resultDate).ToString("yyyy-MM-dd");
                    if (!dates.Contains(aroonDate))
                    {
                        dates.Add(aroonDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> aroonXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                aroonXList.Add(i);
            }

            List <decimal> upYList         = aroonUpYList.ToList();
            decimal        upSlope         = Indicators.GetSlope(aroonXList, upYList);
            List <decimal> downYList       = aroonDownYList.ToList();
            decimal        downSlope       = Indicators.GetSlope(aroonXList, downYList);
            List <decimal> oscillatorYList = aroonOscillatorYList.ToList();
            //decimal oscillatorSlope = GetSlope(aroonXList, oscillatorYList);

            decimal upSlopeMultiplier   = Indicators.GetSlopeMultiplier(upSlope);
            decimal downSlopeMultiplier = Indicators.GetSlopeMultiplier(downSlope);
            //decimal oscillatorSlopeMultiplier = GetSlopeMultiplier(oscillatorSlope);

            //look for buy and sell signals
            bool    aroonHasBuySignal       = false;
            bool    aroonHasSellSignal      = false;
            decimal previousOscillatorValue = oscillatorYList[0];
            bool    previousIsNegative      = previousOscillatorValue <= 0;

            for (int i = 1; i < oscillatorYList.Count(); i++)
            {
                decimal currentOscillatorValue = oscillatorYList[i];
                bool    currentIsNegative      = currentOscillatorValue <= 0;
                if (!currentIsNegative && previousIsNegative && (upYList[i] >= 30 && downYList[i] <= 70))
                {
                    aroonHasBuySignal = true;
                }
                else if (currentIsNegative && !previousIsNegative && (upYList[i] <= 30 && downYList[i] >= 70))
                {
                    aroonHasSellSignal = true;
                }
                previousOscillatorValue = currentOscillatorValue;
                previousIsNegative      = previousOscillatorValue <= 0;
            }

            decimal aroonAvgUp      = Math.Max(aroonUpTotal / numberOfResults, 1.0M);
            decimal aroonAvgDown    = Math.Max(aroonDownTotal / numberOfResults, 1.0M);
            decimal percentDiffDown = (aroonAvgDown / aroonAvgUp) * 100;
            decimal percentDiffUp   = (aroonAvgUp / aroonAvgDown) * 100;

            decimal bullResult = Math.Min(100 - percentDiffDown, 50); //bull result caps at 50
            decimal bearResult = Math.Min(percentDiffUp + 15, 20);    //bear result caps at 20

            //Add bull bonus if last AROON UP >= 70 per investopedia recommendation
            //This is the same as when last AROON OSC >= 50
            //Cap bullBonus at 15
            decimal bullBonus = (aroonAvgUp > aroonAvgDown && previousOscillatorValue >= 50) ? Math.Min(previousOscillatorValue / 5, 15) : 0;

            //Add bear bonus if last AROON UP > last AROON DOWN per investopedia recommendation
            //This is the same as when last AROON OSC > 0
            //Cap bearBonus at 10
            decimal bearBonus = (aroonAvgDown > aroonAvgUp && previousOscillatorValue > 0) ? Math.Min(previousOscillatorValue / 5, 10) : 0;

            //calculate composite score based on the following values and weighted multipliers
            //if AROON avg up > AROON avg down, start score with 100 - (down as % of up)
            //if AROON avg up < AROON avg down, start score with 100 - (up as % of down)
            decimal composite = 0;

            composite += (aroonAvgUp > aroonAvgDown) ? bullResult : bearResult;
            composite += (bullBonus > bearBonus) ? bullBonus : bearBonus;
            composite += (upSlope > -0.05M) ? (upSlope * upSlopeMultiplier) + 5 : 0;
            composite += (downSlope < 0.05M) ? (downSlope * downSlopeMultiplier) + 5 : -(downSlope * downSlopeMultiplier);
            composite += (aroonHasBuySignal) ? 25 : 0;
            composite += (aroonHasSellSignal) ? -25 : 0;

            return(composite);
        }
Beispiel #6
0
        public static decimal GetADXComposite(JArray resultSet, int daysToCalculate)
        {
            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();

            Stack <decimal> adxValueYList = new Stack <decimal>();
            decimal         adxTotal      = 0;

            foreach (var result in resultSet)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal adxVal = decimal.Parse(result.Value <string>("adx"));
                    adxValueYList.Push(adxVal);
                    adxTotal += adxVal;
                    numberOfResults++;

                    string resultDate = result.Value <string>("datetime");
                    string adxDate    = DateTime.Parse(resultDate).ToString("yyyy-MM-dd");
                    if (!dates.Contains(adxDate))
                    {
                        dates.Add(adxDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> adxXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                adxXList.Add(i);
            }

            List <decimal> adxYList           = adxValueYList.ToList();
            decimal        adxSlope           = Indicators.GetSlope(adxXList, adxYList);
            decimal        adxSlopeMultiplier = Indicators.GetSlopeMultiplier(adxSlope);
            decimal        adxAvg             = adxTotal / numberOfResults;

            List <decimal> adxZScores            = Indicators.GetZScores(adxYList);
            decimal        zScoreSlope           = Indicators.GetSlope(adxXList, adxZScores);
            decimal        zScoreSlopeMultiplier = Indicators.GetSlopeMultiplier(zScoreSlope);

            //Start with the average of the 2 most recent ADX values
            decimal baseValue = (adxYList[adxYList.Count - 1] + adxYList[adxYList.Count - 2]) / 2;

            //Add based on the most recent Z Score * 100 if it is positive
            decimal recentZScore = adxZScores[adxZScores.Count - 1];

            if (recentZScore > 0)
            {
                baseValue += (recentZScore * 100) / 2; //to even it out
            }
            else
            {
                baseValue += 10; //pity points
            }
            //Add bonus for ADX average above 25 per investopedia recommendation
            decimal averageBonus = adxAvg > 25 ? 25 : 0;

            //calculate composite score based on the following values and weighted multipliers
            decimal composite = 0;

            composite += baseValue;
            composite += averageBonus;
            composite += (adxSlope > 0.1M) ? (adxSlope * adxSlopeMultiplier) + 10 : -10; //penalty
            composite += (zScoreSlope > 0.1M) ? (zScoreSlope * zScoreSlopeMultiplier) + 10 : 0;

            return(Math.Min(composite, 100)); //cap ADX composite at 100, no extra weight
        }
Beispiel #7
0
        public static ShortInterestResult GetShortInterest(string symbol, IEnumerable <Skender.Stock.Indicators.Quote> history, int daysToCalculate)
        {
            decimal compositeScore = 0;

            List <FinraRecord> shortRecords = GetShortVolume(symbol, history, daysToCalculate);

            int daysCalulated      = 0;
            int numberOfResults    = 0;
            HashSet <string> dates = new HashSet <string>();
            Stack <decimal>  shortInterestYList = new Stack <decimal>();

            decimal shortInterestToday = 0;
            decimal totalVolume        = 0;
            decimal totalVolumeShort   = 0;

            foreach (FinraRecord shortRecord in shortRecords)
            {
                if (daysCalulated < daysToCalculate)
                {
                    decimal shortVolume   = shortRecord.ShortVolume + shortRecord.ShortExemptVolume;
                    decimal shortInterest = (shortVolume / shortRecord.TotalVolume) * 100;

                    if (numberOfResults == 0)
                    {
                        shortInterestToday = shortInterest;
                    }

                    shortInterestYList.Push(shortInterest);
                    totalVolume      += shortRecord.TotalVolume;
                    totalVolumeShort += shortVolume;
                    numberOfResults++;

                    string shortDate = shortRecord.Date.ToString("yyyy-MM-dd");
                    if (!dates.Contains(shortDate))
                    {
                        dates.Add(shortDate);
                        daysCalulated++;
                    }
                }
                else
                {
                    break;
                }
            }

            List <decimal> shortXList = new List <decimal>();

            for (int i = 1; i <= numberOfResults; i++)
            {
                shortXList.Add(i);
            }

            List <decimal> shortYList           = shortInterestYList.ToList();
            decimal        shortSlope           = shortYList.Count > 0 ? Indicators.GetSlope(shortXList, shortYList) : 0.0M; //set to 0 if not found
            decimal        shortSlopeMultiplier = Indicators.GetSlopeMultiplier(shortSlope);
            decimal        shortInterestAverage = totalVolume > 0 ? (totalVolumeShort / totalVolume) * 100 : 30.0M;          //set to 30 if not found

            //Add these bonuses to account for normal short interest fluctuations
            //The slope cannot be in both ranges if the ranges do not overlap
            //This prevents adding both bonuses
            bool slightlyBearish   = (SlightlyBearishLowerBound <= shortSlope && shortSlope <= SlightlyBearishUpperBound);
            bool moderatelyBearish = (ModeratelyBearishLowerBound <= shortSlope && shortSlope <= ModeratelyBearishUpperBound);

            //calculate composite score based on the following values and weighted multipliers
            compositeScore += 100 - shortInterestAverage; //get base score as 100 - short interest
            compositeScore += (shortSlope < 0) ? (shortSlope * shortSlopeMultiplier) + 10 : -15;
            compositeScore += (shortSlope > 0 && slightlyBearish) ? 10 : 0;
            compositeScore += (shortSlope > 0 && moderatelyBearish) ? 5 : 0;

            //Cap this compositeScore at 100 because we should not give it extra weight
            compositeScore = Math.Min(compositeScore, 100);

            //Return ShortInterestResult
            return(new ShortInterestResult
            {
                TotalVolume = totalVolume,
                TotalVolumeShort = totalVolumeShort,
                ShortInterestPercentToday = shortInterestToday,
                ShortInterestPercentAverage = shortInterestAverage,
                ShortInterestSlope = shortSlope,
                ShortInterestCompositeScore = compositeScore
            });
        }