-
Notifications
You must be signed in to change notification settings - Fork 0
/
Data.fs
134 lines (112 loc) · 4.36 KB
/
Data.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
module GeldMachine.Data
open HttpClient
open FSharp.Data
open Deedle
open System
open System.IO
[<StructuralEquality>]
[<StructuralComparison>]
type OHLC = {
Date : DateTime
Open : decimal
High : decimal
Low : decimal
Close : decimal
Volume : int64
Adjusted : decimal }
with
static member (==) (a:OHLC, b:OHLC)= a.Equals b
let toFrame (ohlcs:list<OHLC>) = Frame.ofRecords ohlcs |> Frame.indexRowsDate "Date"
let low (p:OHLC) = p.Low
let high (p:OHLC) = p.High
let private STOCK_DATA_PATH = "data/"
let private YAHOO_URL = "http://ichart.finance.yahoo.com/table.csv"
type StockData = CsvProvider<"http://ichart.finance.yahoo.com/table.csv?s=^GSPC">
type private StockRow = (DateTime * decimal * decimal * decimal * decimal * int64 * decimal)
type private StockRows = StockRow seq
let private toStockRows (data : StockData) =
Seq.map (fun (r : StockData.Row) -> r.Date, r.Open, r.High, r.Low, r.Close, r.Volume, r.``Adj Close``) data.Rows
let private translateRow (row:StockData.Row) = {Date = row.Date; Open = row.Open; High = row.High; Low = row.Low; Close = row.Close; Volume = row.Volume; Adjusted = row.``Adj Close``}
let private translate (n:int Option) (rows:StockData.Row seq) =
let ohlc = Seq.map translateRow rows
let ohlc' = if Option.isSome n then Seq.take n.Value ohlc else ohlc
List.rev (Seq.toList ohlc')
let private urlForAll symbol = YAHOO_URL + "?s=" + symbol
let private urlForRange symbol (startDate:DateTime) (endDate:DateTime) =
sprintf "%s?s=%s&a=%i&b=%i&c=%i&d=%i&e=%i&f=%i"
YAHOO_URL
symbol
(startDate.Month - 1) (startDate.Day + 1) startDate.Year
(endDate.Month - 1) endDate.Day endDate.Year
let private filenameFor symbol = symbol + ".csv"
let private loadFromFile symbol =
let filename = filenameFor symbol
let exists = File.Exists(filename)
if exists
then let data = StockData.Load(filename)
Some(data)
else None
let private loadFromUrl url =
try
let test = createRequest Get url |> getResponse //FSharp.Data doesn't handle exceptions well?!
if test.StatusCode = 200 then
let data = StockData.Load(url)
Some(data)
else
None
with
err -> None
let private stockRowToString (d,o,h,l,c,v,a) = sprintf "%A,%M,%M,%M,%M,%d,%M" d o h l c v a
let private saveToFile symbol (headers:string [] option) (data:StockRows) =
let headers' = if headers.IsSome then headers.Value else [|""|]
let headers'' = String.concat "," headers'
let data' = Seq.map stockRowToString data
let data'' = String.concat "\r\n" data'
let filename = (filenameFor symbol)
try
let f = File.CreateText(filename)
f.WriteLine(headers'')
f.Write(data'')
f.Close()
printfn "Saved to file: %s" filename
with
err -> printfn "Failed save file %s: %A" filename err
let private latestDate rows =
let (latest,_,_,_,_,_,_) = Seq.head rows
latest
let private updateStockData symbol data =
let rows = toStockRows data
let latest = latestDate rows
let now = DateTime.Now
if latest.Date < now then
let url = urlForRange symbol latest now
let data' = loadFromUrl url
match data' with
| Some(data') ->
let rows' = Seq.append (toStockRows data') rows
saveToFile symbol data.Headers rows'
StockData.Load(filenameFor symbol)
| None -> data
else
data
let private getStockData' n symbol =
let data = loadFromFile symbol
match data with
| Some(data) ->
let data' = updateStockData symbol data
translate n data'.Rows
| None ->
let data = StockData.Load(urlForAll symbol)
let rows = toStockRows data
saveToFile symbol data.Headers rows
translate n data.Rows
let getStockData n = getStockData' (Some n)
let getAllStockData = getStockData' None
let private getStockDataOffline' n symbol =
let data = loadFromFile symbol
match data with
| Some(data) -> translate n data.Rows
| None -> printfn "Unable to get offline data from disk (%A): try the online version..." (filenameFor symbol)
[]
let getStockDataOffline n = getStockDataOffline' (Some n)
let getAllStockDataOffline = getStockDataOffline' None