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