Skip to content

Instantly share code, notes, and snippets.

@PJensen
Last active January 4, 2018 01:54
Show Gist options
  • Save PJensen/7a3f147f7fc1005598e099ea6f1b9059 to your computer and use it in GitHub Desktop.
Save PJensen/7a3f147f7fc1005598e099ea6f1b9059 to your computer and use it in GitHub Desktop.
Downloads CAPS ratings from the Motley Fool API
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class EntryPoint
{
/// <summary>
/// The entrypoint for the program
/// </summary>
static void Main()
{
#region test XML serialization / deserialization
string xmlTestMessage;
if (!TestXML(out xmlTestMessage))
{
WriteErrorMessage(xmlTestMessage);
PressAnyKey();
}
#endregion
// create a ratings client with our API key
// TODO: paste your api key here
var ratingClient = new MotleyFoolCapsRatingClient("3d01a88a-d660-421b-8abb-c911cb6a3c59");
// create an empty list of ratings that will be built up in subsequent calls.
var allRatings = new List<MotleyFoolCapsRatingClient.Ticker>();
// create a new data-table for storing the ratings data
var tmpDataTable = new System.Data.DataTable();
// add the columns that we want to store in the extract.
tmpDataTable.Columns.Add("LastPriceDate");
tmpDataTable.Columns.Add("CompanyName");
tmpDataTable.Columns.Add("Symbol");
tmpDataTable.Columns.Add("Rating");
tmpDataTable.Columns.Add("AsOfDate");
// fetch all of the ratings one after another and add them to the "allRatings" collection
foreach (var rating in MotleyFoolCapsRatingClient.AllRatings)
{
var tickerList = ratingClient.Download(rating);
Console.WriteLine("Limit: {0}", tickerList.Limit);
Console.WriteLine("Timestamp: " + tickerList.TimeStamp);
// add the newly downloaded ratings to the data table
foreach (var detail in tickerList)
{
tmpDataTable.Rows.Add(
detail.LastPriceDate,
detail.CompanyName,
detail.Symbol,
(int)rating,
DateTime.Today.ToShortDateString());
}
}
// write the file to output.csv
File.WriteAllText("output.csv", ToCSV(tmpDataTable));
// TODO: SaveAsFile "Output.CSV" using the DataTable
// See: http://stackoverflow.com/questions/888181/convert-datatable-to-csv-stream
PressAnyKey();
}
/// <summary>
/// <seealso cref="http://stackoverflow.com/questions/888181/convert-datatable-to-csv-stream"/>
/// </summary>
/// <param name="table"></param>
/// <returns></returns>
public static string ToCSV(DataTable table)
{
var result = new StringBuilder();
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(table.Columns[i].ColumnName);
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(row[i].ToString());
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
}
return result.ToString();
}
/// <summary>
/// The Motlet Fool Caps rating client.
/// <seealso cref="http://developer.fool.com/docs/stars-caps-star-rating"/>
/// <remarks>
/// Downloads ratings data from the motley fool and returns deserialized objects
/// for later use and / or querying.
/// </remarks>
/// <example>
/// The following example shows how to download 5 star ratings.
/// <code>
/// var ratingClient = new MotleyFoolCapsRatingClient("API KEY");
/// var fiveStarRatings = ratingClient.Download(MotleyFoolCapsRatingClient.StarRating.Five)
/// </code>
/// </example>
/// </summary>
public class MotleyFoolCapsRatingClient
{
#region backing store
/// <summary>
/// the API key for *this* Caps rating client
/// </summary>
private readonly string _apiKey;
/// <summary>
/// the web client actually performs the request and downloads the raw data
/// </summary>
private readonly WebClient _webClient = new WebClient();
#endregion
/// <summary>
/// The API URL for motley fool caps star rating
/// </summary>
private const string ApiUrlFormat = "http://www.fool.com/a/caps/ws/Ticker/Stars/{0}/?apikey={1}";
/// <summary>
/// Creates a new motley fool caps rating client with the specified API key.
/// </summary>
/// <param name="apiKey">The api key</param>
public MotleyFoolCapsRatingClient(string apiKey)
{
_apiKey = apiKey;
}
/// <summary>
/// All of the ratings for the motley fool client
/// </summary>
public static IEnumerable<StarRating> AllRatings
{
get
{
return new[] { StarRating.Five,
// when in "Release" mode, download everything
StarRating.Four, StarRating.Three, StarRating.Two, StarRating.One
};
}
}
/// <summary>
/// Downloads the ticker list from motley fool
/// </summary>
/// <param name="rating"></param>
/// <returns></returns>
public TickerList Download(StarRating rating)
{
var url = string.Format(ApiUrlFormat, (int)rating, _apiKey);
Console.Write("Downloading data: {0} ", url);
var rawData = _webClient.DownloadString(url);
Console.WriteLine("[Complete]");
return Deserialize(rawData);
}
/// <summary>
/// Given a <seealso cref="TickerList"/> object return an XML string representing the object(s).
/// </summary>
/// <param name="tickerList">The ticker list to convert into a string</param>
/// <returns>A string representing the ticker list</returns>
public static string Serialize(TickerList tickerList)
{
var serializer = new XmlSerializer(typeof(TickerList));
using (var stringWriter = new System.IO.StringWriter())
using (var writer = System.Xml.XmlWriter.Create(stringWriter, new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true }))
{
serializer.Serialize(writer, tickerList);
return stringWriter.ToString();
}
}
/// <summary>
/// Given an array of bytes (string) return a ticker list object.
/// </summary>
/// <param name="rawData">The array of bytes</param>
/// <returns>The ticker list object represented by the passed xml bytes</returns>
public static TickerList Deserialize(string rawData)
{
if (string.IsNullOrEmpty(rawData))
{
throw new ArgumentNullException("rawData");
}
var bytes = Encoding.UTF8.GetBytes(rawData);
try
{
using (System.IO.Stream memoryStream = new System.IO.MemoryStream(bytes))
{
return new XmlSerializer(typeof(TickerList)).Deserialize(memoryStream) as TickerList;
}
}
catch
{
return default(TickerList);
}
}
/// <summary>
/// The various star ratings from motley fool
/// </summary>
public enum StarRating
{
[XmlEnum("1 Star Stocks")]
One = 1,
[XmlEnum("2 Star Stocks")]
Two = 2,
[XmlEnum("3 Star Stocks")]
Three = 3,
[XmlEnum("4 Star Stocks")]
Four = 4,
[XmlEnum("5 Star Stocks")]
Five = 5,
}
/// <summary>
/// The <seealso cref="TickerList"/> object from motley fool
/// <seealso cref="http://developer.fool.com/docs/stars-caps-star-rating"/>
/// </summary>
[Serializable, XmlRoot("TickerList")]
public class TickerList : List<Ticker>
{
[XmlAttribute]
public string TimeStamp { get; set; }
[XmlAttribute]
public StarRating Type { get; set; }
[XmlAttribute]
public int Limit { get; set; }
}
/// <summary>
/// The <see cref="Ticker"/> object from motley fool.
/// <seealso cref="http://developer.fool.com/docs/stars-caps-star-rating"/>
/// </summary>
[Serializable]
public class Ticker
{
[XmlAttribute]
public string Symbol { get; set; }
[XmlAttribute]
public string Exchange { get; set; }
[XmlAttribute]
public string CompanyName { get; set; }
[XmlAttribute]
public int Percentile { get; set; }
[XmlAttribute]
public decimal Day30Return { get; set; }
[XmlAttribute]
public string LastPriceDate { get; set; }
[XmlAttribute]
public int AllCompletedPicks { get; set; }
[XmlAttribute]
public int AllActivePicks { get; set; }
[XmlAttribute]
public int AllOutPicks { get; set; }
[XmlAttribute]
public int AllUnderPicks { get; set; }
[XmlAttribute]
public int ASActivePicks { get; set; }
[XmlAttribute]
public int ASOutPicks { get; set; }
[XmlAttribute]
public int ASUnderPicks { get; set; }
[XmlAttribute]
public int VPActivePicks { get; set; }
[XmlAttribute]
public int VPOutPicks { get; set; }
[XmlAttribute]
public int VPUnderPicks { get; set; }
[XmlAttribute]
public int PitchCount { get; set; }
}
}
/// <summary>
/// Writes an error message to the console window (in red)
/// </summary>
/// <param name="message">the message</param>
private static void WriteErrorMessage(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// Writes "Press any key to continue ..." and awaits keypress
/// </summary>
private static void PressAnyKey()
{
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();
}
/// <summary>
/// Calling this method just tests serialization / deserialzation.
/// <remarks>Our goal here is just to establish that we can
/// go from raw xml to a C# object and then back.</remarks>
/// </summary>
private static bool TestXML(out string message)
{
message = "OK";
try
{
// well create an object that we'll use for this test
var tickerList = new MotleyFoolCapsRatingClient.TickerList()
{
new MotleyFoolCapsRatingClient.Ticker
{
CompanyName = "Apple Computer",
Day30Return = 120.44M,
Exchange = "NYSE",
LastPriceDate = "12/12/2012",
Percentile = 5,
Symbol = "AAPL"
}
};
// convert the object above into xml
var xml = MotleyFoolCapsRatingClient.Serialize(tickerList);
// convert the xml back into an object
var tmp = MotleyFoolCapsRatingClient.Deserialize(xml);
// do a few checks to ensure the same object made it back and forth
Debug.Assert(tmp.Count == 1);
// ensure everything is ther same
Debug.Assert(tmp[0].CompanyName == tickerList[0].CompanyName);
Debug.Assert(tmp[0].Day30Return == tickerList[0].Day30Return);
Console.WriteLine(xml);
// transitivity
// A -> B, B -> C therefore A -> C
return true;
}
catch (Exception ex)
{
message = ex.Message;
return false;
}
}
}
}
@PJensen
Copy link
Author

PJensen commented Apr 4, 2016

Minor adjustment.

@PJensen
Copy link
Author

PJensen commented Apr 4, 2016

  • Add additional columns to DataTable for the extract
  • Perform the extract of the DataTable to a .CSV file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment