I work with financial data in Mathematica and Excel quite frequently and have found the following framework helpful for my applications.
Helper functions:
joinColumnToData
joinColumnToData
is useful for adding a column of dates or tickers to a table of prices.
joinColumnToData[column_?VectorQ, data_?VectorQ] := Transpose@{column, data};
joinColumnToData[column_?VectorQ, data_?MatrixQ] := ArrayFlatten@{{List /@ column, data}};
convertFinancialDataToFlatDatePath
convertFinancialDataToFlatDatePath
makes working with FinancialData
easier.
For example FinancialData
returns TimeSeries
objects and I often extract the "Dates"
and "Values"
. In practice I struggle with:
"Values"
returned are in Quantity
, which is slow
Quantity
is almost always in USD, so Quantity
provides no additional value, we are only concerned with the magnitude
"Dates"
returns a DateObject
with full granularity (seconds, timezone, etc.) which is difficult to work with. Eg Select[financialDataset, #Date == DateObject["2020-11-30"] &]
would fail as we did not provide the full granularity
DateObject
does not export nicely so we would prefer this to be a string
DateObject
can be slow so we would prefer to work in AbsoluteTime
convertFinancialDataToFlatDatePath
solves these problems by returning a flat array with "Values"
converted to a magnitude and giving you the option to apply a formula to convert "Dates"
to the DateString
or DateObject
of your choice.
convertFinancialDataToFlatDatePath::"usage"="Converts a TimeSeries object from FinancialData to a flat table for easier calculations and exporting.
Option \"DateConversionFunction\" to convert the default DateObject supplied by TimeSeries. Defaults to Function[{date},DateString[date,\"ISODate\"]]
Some other useful options are:
\"DateConversionFunction\" ->Function[{date},DateString[date,\"ISODateTime\"] (* for intra-day *),
\"DateConversionFunction\" ->Function[{date},DateObject[date,\"Day\",TimeZone->None](* for working in Mathematica *),
\"DateConversionFunction\" ->AbsoluteTime (* for speed *),
\"DateConversionFunction\" ->Identity (* to use the defaults *)";
Options[convertFinancialDataToFlatDatePath]=
{
"DateConversionFunction"->Function[{date},DateString[date,"ISODate"]]
};
convertFinancialDataToFlatDatePath[timeSeries_TemporalData,OptionsPattern[]]:=
Module[
{datesRaw,valuesRaw,dates,values},
datesRaw=timeSeries["Dates"];
valuesRaw=timeSeries["Values"];
dates=OptionValue["DateConversionFunction"]/@datesRaw;
values=QuantityMagnitude@valuesRaw;
joinColumnToData[dates,values]
];
Putting it together:
Define the investment universe, allowing for a variety of stock symbols.
You mentioned you only need Open, Close, and Volume but I prefer to download as much as I can and then filter later on. Downloading is often the bottleneck and I don't like having to redownload once I realize I need High and Low data too.
tickers = {"GE", "AAPL"};
dataStart = DateString["2020-11-30", "ISODate"];
dataEnd = DateString["2021-01-01", "ISODate"];
tickerProperties = "OHLCV";
tickerPropertiesHeader = {"Open", "High", "Low", "Close","Volume"};(*change if using something other than "OHLCV"*)
headers = {"Ticker", "Date"}~Join~tickerPropertiesHeader;
Get data from Mathematica and put it in a nice format to work with.
(*get list of TimeSeries from Mathematica*)
financialDataRaw=FinancialData[tickers,tickerProperties,{dataStart,dataEnd}];
(*format it*)
financialDataFlat=convertFinancialDataToFlatDatePath/@financialDataRaw;
(*add Tickers to the data*)
tickerArray=MapThread[ConstantArray[#1,#2["PathLength"]]&,{tickers,financialDataRaw}];
financialDataWithTickers=MapThread[joinColumnToData,{tickerArray,financialDataFlat}];
financialData=Map[AssociationThread[headers,#]&,financialDataWithTickers,{2}]//Flatten;
financialData
is a list of associations, which is fast and easy to work with in Mathematica. It is my preferred format.
For example to get the attributes you want :
Select[financialData, #Ticker == "GE" &][[All, {"Date", "Open", "Close", "Volume"}]]
Export the data.
As you might download different datasets, it can be helpful to name the file according to the tickers and dates of the data in the file. This makes it easier to identify the file contents.
I have used ".xlsx" here as it allows for more rows etc. over the old ".xls" format. You can also use a text file format. I prefer ".tsv" over ".csv" as you can copy and paste to Excel directly from a ".tsv" file.
fileBaseName=StringRiffle[tickers," "]<>"_"<>dataStart<>"-"<>dataEnd;
fileExtension=".xlsx";
fileName=fileBaseName<>fileExtension;
financialDataset=Dataset[financialData];
Export[fileName,financialDataset]