Skip to content

Commit 287fefa

Browse files
authored
downloads: fix yahoo api (#62)
* downloads: fix yahoo api * fix fred * remove Base.kwdef * fix tests * CSV compat * Update README
1 parent af22072 commit 287fefa

File tree

10 files changed

+178
-81
lines changed

10 files changed

+178
-81
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.swp
22
docs/build/*
3+
Manifest.toml

Project.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ authors = ["JuliaQuant <https://github.com/JuliaQuant>"]
44
version = "0.12.1"
55

66
[deps]
7+
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
8+
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
79
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
10+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
811
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
912
TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e"
1013

1114
[compat]
15+
CSV = "0.7"
1216
HTTP = "0.8"
13-
TimeSeries = "0.17, 0.18"
1417
Reexport = "0.2"
18+
TimeSeries = "0.17, 0.18"
1519
julia = "1"
1620

1721
[extras]

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,55 @@
44
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://JuliaQuant.github.io/MarketData.jl/latest)
55

66
Historical financial time series data for research and testing in Julia.
7+
8+
## Examples
9+
10+
### Testing data
11+
12+
There are some data builtin for testing or demo purpose.
13+
The full list of testing data is [here](https://juliaquant.github.io/MarketData.jl/stable/test_data.html).
14+
15+
```julia
16+
julia> using MarketData
17+
18+
julia> cl
19+
500×1 TimeArray{Float64,1,Date,Array{Float64,1}} 2000-01-03 to 2001-12-31
20+
│ │ Close │
21+
├────────────┼────────┤
22+
2000-01-03111.94
23+
2000-01-04102.5
24+
2000-01-05104.0
25+
2000-01-0695.0
26+
2000-01-0799.5
27+
2000-01-1097.75
28+
2000-01-1192.75
29+
...
30+
```
31+
32+
### Remote data source
33+
34+
This package supports Yahoo Finance API for retrieving data.
35+
36+
```julia
37+
julia> yahoo(:INTC)
38+
10187×6 TimeArray{Float64,2,Date,Array{Float64,2}} 1980-03-17 to 2020-08-07
39+
│ │ Open │ High │ Low │ Close │ Adj Close │ Volume │
40+
├────────────┼────────┼────────┼────────┼────────┼───────────┼────────────┤
41+
1980-03-170.32550.33070.32550.32550.20221.09248e7
42+
1980-03-180.32550.32810.32290.32290.20061.70688e7
43+
1980-03-190.33070.33590.33070.33070.20551.85088e7
44+
1980-03-200.33070.33460.32940.32940.20471.11744e7
45+
1980-03-210.32290.32290.31770.31770.19741.21728e7
46+
1980-03-240.31640.31640.31120.31120.19338.9664e6
47+
1980-03-250.31250.31770.31250.31250.19411.13472e7
48+
1980-03-260.31250.31510.30990.30990.19251.62624e7
49+
1980-03-270.30470.30470.29950.29950.1862.69184e7
50+
1980-03-280.31120.31640.31120.31120.19332.01024e7
51+
1980-03-310.32160.32680.32160.32160.19989.0048e6
52+
1980-04-010.32290.32810.32290.32290.20068.1792e6
53+
1980-04-020.32550.33070.32550.32550.20221.25568e7
54+
55+
```
56+
57+
The full API reference is
58+
[here](https://juliaquant.github.io/MarketData.jl/stable/downloads.html).

docs/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
all:
2-
julia --color=yes make.jl
2+
julia --project --color=yes make.jl

docs/make.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ makedocs(
1111
"test_data.md",
1212
"company_financial_series.md",
1313
"downloads.md",
14-
]
14+
],
15+
modules = [MarketData],
1516
)
1617

1718
deploydocs(

docs/src/downloads.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ left with determining the correct name for the data of interest.
1515
For the `yahoo()` method, the default
1616
data is the S&P 500 end-of-day daily prices. That symbol is named `^GSPC`.
1717

18-
!!! warning
19-
20-
Yahoo! Finance has been immediately deprecated. Yahoo! substantially altered their API in late 2017 and this csv endpoint was retired.
18+
```@docs
19+
yahoo
20+
YahooOpt
21+
```
2122

2223
## FRED
2324

docs/src/test_data.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ Test data `const` objects are smaller datasets designed to be used in
44
tests for other packages. They include both `Date` and `DateTime`
55
objects. All but one of the objects has an empty TimeArray `meta` field.
66

7-
| `const` | Description | First timestamp | Number of rows
8-
|-----------|------------------------------------|---------------------|-----------------
9-
| op | single column Date data | 2000-01-03 | 500
10-
| cl | single column Date data | 2000-01-03 | 500
11-
| ohlc | four-column Date data | 2000-01-03 | 500
12-
| ohlcv | five-column Date data | 2000-01-03 | 500
13-
| mdata | 1-column Date data with meta field | 2000-01-03 | 500
14-
| datetime1 | 1-column DateTime data | 2013-12-31T00:00:00 | 5
15-
| datetime2 | 1-column DateTime data | 1961-12-31 00:00:00 | 5
7+
| `const` | Description | First timestamp | Number of rows
8+
|-----------|--------------------------------------|---------------------|-----------------
9+
| op | single column `Date` data | 2000-01-03 | 500
10+
| cl | single column `Date` data | 2000-01-03 | 500
11+
| ohlc | four-column `Date` data | 2000-01-03 | 500
12+
| ohlcv | five-column `Date` data | 2000-01-03 | 500
13+
| mdata | 1-column `Date` data with meta field | 2000-01-03 | 500
14+
| datetime1 | 1-column `DateTime` data | 2013-12-31T00:00:00 | 5
15+
| datetime2 | 1-column `DateTim`e data | 1961-12-31 00:00:00 | 5

src/MarketData.jl

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
module MarketData
22

3-
using TimeSeries
3+
using CSV
4+
using Dates
5+
using Dates: datetime2unix
46
using HTTP
7+
using Random
58
using Reexport
9+
using TimeSeries
610

711
@reexport using TimeSeries
812

913
export AAPL, BA, CAT, DELL, EBAY, F, GE, TX,
1014
cl, op, ohlc, ohlcv, datetime1, datetime2, mdata,
11-
o, h, l, c, v,
15+
o, h, l, c, v
16+
# downloads.jl
17+
export AbstractQueryOpt,
18+
YahooOpt,
1219
yahoo, fred
1320

1421
###### include ##################

src/downloads.jl

+91-60
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,108 @@ struct APIResponse
66
http_resp::HTTP.Messages.Response
77
end
88

9+
abstract type AbstractQueryOpt <: AbstractDict{Symbol,Any} end
10+
11+
Base.length(::T) where {T<:AbstractQueryOpt} = fieldcount(T)
12+
Base.length(::Type{T}) where {T<:AbstractQueryOpt} = fieldcount(T)
13+
14+
function Base.iterate(aqo::T, state = 1) where {T<:AbstractQueryOpt}
15+
(state > length(T)) && return nothing
16+
(fieldname(T, state) => getfield(aqo, state), state + 1)
17+
end
918

1019
"""
11-
Description
20+
struct YahooOpt <: AbstractQueryOpt
21+
period1 # the start time
22+
period2 # the end time
23+
interval # "1d", "1wk" or "1mo"
24+
events # currently only `:history` supported
25+
end
1226
13-
The yahoo() method is a wrapper for downloading historical stock prices from Yahoo.
27+
The Yahoo Finance HTTP API query object.
1428
15-
Usage
29+
# Examples
1630
17-
AAPL = yahoo("AAPL)
18-
SPX = yahoo()
31+
```jl-repl
32+
julia> t = Dates.now()
33+
2020-08-09T01:38:04.735
1934
20-
Method Signature(s)
35+
julia> YahooOpt(period1 = t - Year(2), period2 = t)
36+
YahooOpt{DateTime} with 4 entries:
37+
:period1 => 1533778685
38+
:period2 => 1596937085
39+
:interval => "1d"
40+
:events => :history
41+
```
42+
"""
43+
struct YahooOpt <: AbstractQueryOpt
44+
period1::DateTime
45+
period2::DateTime
46+
interval::String
47+
events::Symbol
48+
49+
YahooOpt(; period1::DateTime = DateTime(1971, 2, 8),
50+
period2::DateTime = Dates.now(),
51+
interval::String = "1d",
52+
events::Symbol = :history) =
53+
new(period1, period2, interval, events)
54+
end
2155

22-
yahoo(data::ASCIIString="^GSPC")
56+
function Base.iterate(opt::YahooOpt, state = 1)
57+
(state > length(YahooOpt)) && return nothing
58+
k = fieldname(YahooOpt, state)
59+
v = getfield(opt, state)
60+
v′ = (k (:period1, :period2)) ? round(Int, datetime2unix(v)) : v
61+
(k => v′, state + 1)
62+
end
2363

24-
Details
64+
"""
65+
yahoo(symbol::AbstractString, opt::YahooOpt = YahooOpt())::TimeArray
66+
yahoo(symbol::Symbol, opt::YahooOpt = YahooOpt())::TimeArray
2567
26-
The yahoo method takes a stock name in the form of a string and returns a TimeSeries.TimeArray data structure
27-
corresponding to the Yahoo Finance ticker. With no argument, the default historical time series is the S&P 500.
68+
This is a wrapper for downloading historical stock prices from Yahoo Finance.
2869
29-
References
70+
The yahoo method takes a stock name in the form of a string and returns a
71+
`TimeSeries.TimeArray` corresponding to the Yahoo Finance ticker.
72+
With no argument, the default historical time series is the S&P 500.
3073
31-
http://www.finance.yahoo.com
74+
# Examples
3275
33-
See Also
76+
```julia
77+
AAPL = yahoo(:AAPL)
78+
SPX = yahoo("^GSPC")
79+
NQ = yahoo("^IXIC")
80+
```
81+
82+
```jl-repl
83+
julia> start = DateTime(2018, 1, 1)
84+
2018-01-01T00:00:00
85+
86+
julia> yahoo(:AAPL, YahooOpt(period1 = start))
87+
655×6 TimeArray{Float64,2,Date,Array{Float64,2}} 2018-01-02 to 2020-08-07
88+
...
89+
```
90+
91+
# References
92+
93+
https://finance.yahoo.com
3494
35-
fred() which accesses the St. Louis Federal Reserve financial and economic data sets.
95+
# See Also
96+
97+
fred() which accesses the St. Louis Federal Reserve financial and economic data sets.
3698
"""
37-
function yahoo(data::String="^GSPC")
38-
Base.depwarn("Yahoo Finance API changed, this function may not work anymore", :yahoo)
39-
url = "http://ichart.yahoo.com/table.csv?s=$data"
40-
http_resp = HTTP.request("GET", url)
41-
resp = APIResponse(data, http_resp)
42-
TimeArray(resp)
99+
function yahoo(sym::AbstractString = "^GSPC", opt::YahooOpt = YahooOpt())
100+
host = rand(["query1", "query2"])
101+
url = "https://$host.finance.yahoo.com/v7/finance/download/$sym"
102+
res = HTTP.get(url, query = opt)
103+
@assert res.status == 200
104+
csv = CSV.File(res.body, missingstrings = ["null"])
105+
sch = TimeSeries.Tables.schema(csv)
106+
TimeArray(csv, timestamp = first(sch.names))
43107
end
44108

109+
yahoo(s::Symbol, opt::YahooOpt = YahooOpt()) = yahoo(string(s), opt)
110+
45111
"""
46112
Description
47113
@@ -72,45 +138,10 @@ See Also
72138
"""
73139
function fred(data::String="CPIAUCNS")
74140
url = "http://research.stlouisfed.org/fred2/series/$data/downloaddata/$data.csv"
75-
http_resp = HTTP.request("GET", url)
76-
resp = APIResponse(data, http_resp)
77-
TimeArray(resp)
78-
end
79-
80-
81-
function TimeArray(resp::APIResponse)
82-
#This function transform the Response object into a TimeArray
83-
# Split the data on every "\n"
84-
raw_data = String(resp.http_resp.body)
85-
data = split(raw_data, "\n")
86-
# Extract the head and body of the data
87-
head = strip(data[1])
88-
body = data[2:end]
89-
# Parse body
90-
body[end] == "" ? pop!(body) : nothing # remove trailing empty string if it's there
91-
body = [split(line, ",") for line in body] # split on comma
92-
######### Timestamp
93-
# take the first row (assuming it's date)
94-
# TODO: regex query needed to catch edge cases
95-
dates = [line[1] for line in body]
96-
timestamp = Date[Date(d) for d in dates] # parse dates
97-
######### Values
98-
svals = [line[2:end] for line in body] # get rows 2 to the end
99-
fvals = zeros(length(svals),length(svals[1]))
100-
for r in 1:size(fvals,1)
101-
for c in 1:size(fvals,2)
102-
# is not empty and is not equal to FRED's iconic "." sentinel for missingness
103-
if ~isempty(svals[r][c]) && ~isequal(svals[r][c],".\r")
104-
fvals[r,c] = parse(Float64, svals[r][c])
105-
else
106-
# captures FRED's "." sentinel
107-
fvals[r,c] = NaN
108-
end
109-
end
110-
end
111-
######### Column names
112-
names = split(head, ",")[2:end] # Won't need the Date name (fist column) for TimeArray
113-
names = String[name for name in names]
114-
return TimeArray(timestamp, fvals, names, resp.data)
141+
res = HTTP.get(url)
142+
@assert res.status == 200
143+
csv = CSV.File(res.body)
144+
sch = TimeSeries.Tables.schema(csv)
145+
TimeArray(csv, timestamp = first(sch.names))
115146
end
116147

test/downloads.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ using Test
88
end
99

1010
@testset "Yahoo" begin
11-
# ta = yahoo()
12-
# @test length(ta.timestamp) > 100
13-
@test_broken 1==2
11+
t = Dates.now() - Year(2)
12+
opt = YahooOpt(period1 = t)
13+
ta = yahoo(:AAPL, opt)
14+
@test ta |> timestamp |> length > 100
1415
end
15-
1616
end

0 commit comments

Comments
 (0)