ytterb 0.2.150.232042 → 0.2.152.223223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/Rakefile +1 -1
- data/lib/ytterb.rb +3 -3
- data/lib/ytterb/{cached_symbol_data → .cache/cached_symbol_data}/.keepme +0 -0
- data/lib/ytterb/{cached_symbol_data → .cache/cached_symbol_data}/clean.sh +0 -0
- data/lib/ytterb/{local_settings → .cache/local_settings}/settings.yaml +0 -0
- data/lib/ytterb/{raw_symbol_data → .cache/raw_symbol_data}/companylist_amex.csv +0 -0
- data/lib/ytterb/{raw_symbol_data → .cache/raw_symbol_data}/companylist_nasdaq.csv +0 -0
- data/lib/ytterb/{raw_symbol_data → .cache/raw_symbol_data}/companylist_nyse.csv +0 -0
- data/lib/ytterb/stock_symbol.rb +1 -32
- data/lib/ytterb/stock_symbol/_req.rb +2 -0
- data/lib/ytterb/stock_symbol/cache_builder.rb +82 -0
- data/lib/ytterb/stock_symbol/market_loader.rb +42 -0
- data/lib/ytterb/stock_symbol/stock.rb +42 -0
- data/lib/ytterb/util.rb +1 -0
- data/lib/ytterb/util/_req.rb +2 -0
- data/lib/ytterb/util/data_persist_helper.rb +19 -0
- data/lib/ytterb/util/settings.rb +80 -0
- data/lib/ytterb/yql.rb +1 -0
- data/lib/ytterb/yql/_req.rb +2 -0
- data/lib/ytterb/yql/client.rb +40 -0
- metadata +19 -14
- data/lib/ytterb/data_persist_helper.rb +0 -17
- data/lib/ytterb/settings.rb +0 -62
- data/lib/ytterb/stock_market_loader.rb +0 -41
- data/lib/ytterb/stock_symbol_cache_builder.rb +0 -72
- data/lib/ytterb/stock_symbol_loader.rb +0 -19
- data/lib/ytterb/yql_client.rb +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4de92e40383cd2fc47c6dd00857bc62ea366000e
|
|
4
|
+
data.tar.gz: cf77d498119558b2cb956adde78d8e0a5541a7d4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 850e40a9c4b39e5147bd9c985c29203818b2590d207c8cfa3de1ce363b905eb237f52503dcceb8b78259e5db9937255c7c711e0097ed18bbd2bd4d133105b298
|
|
7
|
+
data.tar.gz: 61ca6fd0035c30f4e9ab1b54574527422b19e19c70782d4d7237887770da122fb25ad0dbc7a391d8e9b3a3f7e0e73f38de8ef75f6f2353038f371e6fc4441511
|
data/.gitignore
CHANGED
data/Rakefile
CHANGED
data/lib/ytterb.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
require 'ytterb/version'
|
|
2
|
-
require 'ytterb/
|
|
3
|
-
require 'ytterb/
|
|
4
|
-
require 'ytterb/
|
|
2
|
+
require 'ytterb/util'
|
|
3
|
+
require 'ytterb/yql'
|
|
4
|
+
require 'ytterb/stock_symbol'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/lib/ytterb/stock_symbol.rb
CHANGED
|
@@ -1,32 +1 @@
|
|
|
1
|
-
|
|
2
|
-
class StockSymbol
|
|
3
|
-
|
|
4
|
-
def initialize(options={})
|
|
5
|
-
@options = {}
|
|
6
|
-
options.reject {|k,v| !k }.each do |k,v|
|
|
7
|
-
sym_k = k.gsub(/\s+/,"_").downcase.to_sym
|
|
8
|
-
val = v.strip
|
|
9
|
-
val.gsub!(/\^/,"-") if sym_k == :symbol
|
|
10
|
-
@options[sym_k] = val
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def attributes
|
|
15
|
-
return @options.keys
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def method_missing(meth, *args, &block)
|
|
19
|
-
if @options.has_key?(meth)
|
|
20
|
-
return @options[meth]
|
|
21
|
-
else
|
|
22
|
-
# don't know how to handle it
|
|
23
|
-
super
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def to_s
|
|
28
|
-
@options.to_s
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
end # StockSymbol
|
|
32
|
-
end # Ytterb
|
|
1
|
+
require_relative 'stock_symbol/_req'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'date'
|
|
3
|
+
|
|
4
|
+
require_relative '../util'
|
|
5
|
+
require_relative '../yql'
|
|
6
|
+
require_relative 'market_loader'
|
|
7
|
+
|
|
8
|
+
module Ytterb
|
|
9
|
+
module StockSymbol
|
|
10
|
+
class CacheBuilder
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@yql = Yql::Client.new
|
|
14
|
+
@sml = MarketLoader.new
|
|
15
|
+
@local_settings = Util::Settings.new
|
|
16
|
+
|
|
17
|
+
# build the queue used when syncing
|
|
18
|
+
build_stock_sync_queue
|
|
19
|
+
|
|
20
|
+
# run the processor that fetches and persists stock info
|
|
21
|
+
stock_processor_run
|
|
22
|
+
|
|
23
|
+
@local_settings.save
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_stock_sync_queue
|
|
27
|
+
@stock_sync_queue = Queue.new
|
|
28
|
+
@sml.stock_symbols.shuffle.each do |stock|
|
|
29
|
+
@stock_sync_queue << stock.symbol
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def stock_processor_run
|
|
34
|
+
while true
|
|
35
|
+
begin
|
|
36
|
+
curr = @stock_sync_queue.pop(true) rescue nil
|
|
37
|
+
break unless curr # queue is empty
|
|
38
|
+
sleep(3600.0/@local_settings[:api_calls_per_hour])
|
|
39
|
+
puts "#{@stock_sync_queue.length} symbols in the queue"
|
|
40
|
+
puts "Processing Symbol: #{curr}"
|
|
41
|
+
curr_file = @local_settings.get_symbol_store_file(curr)
|
|
42
|
+
curr_stock_info = Util::DataPersistHelper.load(curr_file) rescue nil
|
|
43
|
+
curr_stock_info ||= {}
|
|
44
|
+
curr_stock_info[:sync_to] ||= @local_settings[:sync_from]
|
|
45
|
+
date_start = Date.parse(curr_stock_info[:sync_to])
|
|
46
|
+
if (date_start < Date.today)
|
|
47
|
+
date_end = date_start + @local_settings[:sync_increment]
|
|
48
|
+
date_end = Date.today if date_end > Date.today
|
|
49
|
+
result = @yql.get_symbol_historical(curr,date_start.to_s, date_end.to_s)
|
|
50
|
+
max_end_date = Date.parse(curr_stock_info[:sync_to])
|
|
51
|
+
symbol_updated = false
|
|
52
|
+
if result and result.has_key?("quote") and result["quote"].kind_of?(Array)
|
|
53
|
+
# TODO: need to pull all this logic into the yql client
|
|
54
|
+
# TODO: this needs some tests!
|
|
55
|
+
result["quote"].each do |item|
|
|
56
|
+
if item["Date"]
|
|
57
|
+
curr_stock_info[:data]||={}
|
|
58
|
+
curr_stock_info[:data][item["Date"]] = item
|
|
59
|
+
if Date.parse(item["Date"]) > max_end_date
|
|
60
|
+
max_end_date = Date.parse(item["Date"])
|
|
61
|
+
curr_stock_info[:sync_to] = max_end_date.to_s
|
|
62
|
+
symbol_updated = true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
@stock_sync_queue << curr if symbol_updated
|
|
67
|
+
puts curr_stock_info[:sync_to]
|
|
68
|
+
Util::DataPersistHelper.save(curr_file,curr_stock_info)
|
|
69
|
+
else
|
|
70
|
+
puts "Debug result: #{result}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
puts "error in stock processor run: #{e.message} : #{e.backtrace.join(" | ")}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
end # CacheBuilder
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require_relative '../util'
|
|
2
|
+
require_relative 'stock'
|
|
3
|
+
|
|
4
|
+
module Ytterb
|
|
5
|
+
module StockSymbol
|
|
6
|
+
class MarketLoader
|
|
7
|
+
def initialize
|
|
8
|
+
@stock_symbols = []
|
|
9
|
+
path = Util::Settings.new.get_raw_symbol_list_data_dir
|
|
10
|
+
Dir.entries(path).select {|f| !File.directory?(f) and /companylist_[a-zA-Z]+\.csv/ =~ f }.each do |file_to_process|
|
|
11
|
+
market = /companylist_(?<market>[a-zA-Z]+)\.csv/.match(file_to_process)[:market]
|
|
12
|
+
Stock.builder_from_csv(File.join(path,file_to_process),market) do |stock_symbol|
|
|
13
|
+
@stock_symbols << stock_symbol
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def industries
|
|
19
|
+
return @industries if @industries
|
|
20
|
+
@raw_industries = {}
|
|
21
|
+
@stock_symbols.each do |stock_symbol|
|
|
22
|
+
@raw_industries[stock_symbol.industry] = 1
|
|
23
|
+
end
|
|
24
|
+
@industries = @raw_industries.keys
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def sectors
|
|
28
|
+
return @sectors if @sectors
|
|
29
|
+
@raw_sectors = {}
|
|
30
|
+
@stock_symbols.each do |stock_symbol|
|
|
31
|
+
@raw_sectors[stock_symbol.sector] = 1
|
|
32
|
+
end
|
|
33
|
+
@sectors = @raw_sectors.keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def stock_symbols
|
|
37
|
+
@stock_symbols
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
|
|
3
|
+
module Ytterb
|
|
4
|
+
module StockSymbol
|
|
5
|
+
class Stock
|
|
6
|
+
|
|
7
|
+
def initialize(options={})
|
|
8
|
+
@options = {}
|
|
9
|
+
options.reject {|k,v| !k }.each do |k,v|
|
|
10
|
+
sym_k = k.gsub(/\s+/,"_").downcase.to_sym
|
|
11
|
+
val = v.strip
|
|
12
|
+
val.gsub!(/\^/,"-") if sym_k == :symbol
|
|
13
|
+
@options[sym_k] = val
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def attributes
|
|
18
|
+
return @options.keys
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(meth, *args, &block)
|
|
22
|
+
if @options.has_key?(meth)
|
|
23
|
+
return @options[meth]
|
|
24
|
+
else
|
|
25
|
+
# don't know how to handle it
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
@options.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.builder_from_csv(stock_symbol_file, exchange)
|
|
35
|
+
CSV.foreach(stock_symbol_file, :headers => true) do |obj|
|
|
36
|
+
yield Stock.new(obj.to_hash.merge("Exchange" => exchange))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end # Ytterb
|
data/lib/ytterb/util.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'util/_req'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module Ytterb
|
|
4
|
+
module Util
|
|
5
|
+
class DataPersistHelper
|
|
6
|
+
def self.save(file, value)
|
|
7
|
+
File.open(file,"w") {|f| f.write(YAML.dump(value)) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.load(file)
|
|
11
|
+
File.open(file,"r") {|f| YAML.load(f.read()) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.get_extension
|
|
15
|
+
return ".yaml"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require_relative 'data_persist_helper'
|
|
2
|
+
|
|
3
|
+
module Ytterb
|
|
4
|
+
module Util
|
|
5
|
+
class Settings
|
|
6
|
+
def initialize
|
|
7
|
+
@cache_dir = ".cache"
|
|
8
|
+
@options = {
|
|
9
|
+
:settings_file => File.join("settings",DataPersistHelper.get_extension()),
|
|
10
|
+
:settings_data_dir => [@cache_dir, "local_settings"],
|
|
11
|
+
:cached_symbol_data_dir => [@cache_dir, "cached_symbol_data"],
|
|
12
|
+
:raw_symbol_list_data_dir => [@cache_dir, "raw_symbol_data"]
|
|
13
|
+
}.freeze
|
|
14
|
+
@local_path = File.expand_path(File.dirname(__FILE__)).
|
|
15
|
+
split(File::SEPARATOR).
|
|
16
|
+
reverse.drop(1).reverse.
|
|
17
|
+
join(File::SEPARATOR)
|
|
18
|
+
|
|
19
|
+
# create the cache dirs if they don's exist
|
|
20
|
+
@options.each do |option, value|
|
|
21
|
+
options_s = option.to_s
|
|
22
|
+
next unless options_s.end_with?("data_dir")
|
|
23
|
+
FileUtils.mkdir_p(File.join(@local_path, @options[option]))
|
|
24
|
+
end
|
|
25
|
+
load
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def set_defaults_if_uninitialized
|
|
29
|
+
# defaults to be used if not defined
|
|
30
|
+
@local_settings ||= {}
|
|
31
|
+
@local_settings[:sync_from] ||= (Date.today-365).to_s
|
|
32
|
+
@local_settings[:sync_increment] ||= 120
|
|
33
|
+
@local_settings[:api_calls_per_hour] ||= 1900
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_full_path_and_file(full_path)
|
|
37
|
+
FileUtils.mkdir_p(File.dirname(full_path))
|
|
38
|
+
unless File.file?(full_path)
|
|
39
|
+
FileUtils.touch(full_path)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def full_path_settings_file
|
|
44
|
+
File.join(@local_path,@options[:settings_data_dir],@options[:settings_file])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def get_symbol_store_file(symbol, build_path_and_file = true)
|
|
48
|
+
split_for_storage = symbol.split('')
|
|
49
|
+
split_for_storage[-1] += DataPersistHelper.get_extension()
|
|
50
|
+
full_path = File.join(@local_path,@options[:cached_symbol_data_dir],split_for_storage)
|
|
51
|
+
create_full_path_and_file(full_path) if build_path_and_file == true
|
|
52
|
+
full_path
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get_raw_symbol_list_data_dir
|
|
56
|
+
File.join(@local_path,@options[:raw_symbol_list_data_dir])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def [](key)
|
|
60
|
+
@local_settings[key]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def []=(key, value)
|
|
64
|
+
@local_settings[key] = value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def load
|
|
68
|
+
@local_settings = DataPersistHelper.load(full_path_settings_file())
|
|
69
|
+
rescue
|
|
70
|
+
ensure
|
|
71
|
+
set_defaults_if_uninitialized
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def save
|
|
75
|
+
create_full_path_and_file(full_path_settings_file())
|
|
76
|
+
DataPersistHelper.save(full_path_settings_file(), @local_settings)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/ytterb/yql.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'yql/_req'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'rest-client'
|
|
2
|
+
require 'cgi'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
module Ytterb
|
|
7
|
+
module Yql
|
|
8
|
+
class Client
|
|
9
|
+
def initialize
|
|
10
|
+
@endpoint = "http://query.yahooapis.com/v1/public/yql"
|
|
11
|
+
puts "YQL Client is using endpoint @ #{@endpoint}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_yql_endpoint
|
|
15
|
+
@endpoint
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_select_query(query)
|
|
19
|
+
RestClient.get("#{get_yql_endpoint}?q=#{CGI::escape(query)}&format=json&env=store://datatables.org/alltableswithkeys") do |response, request, result|
|
|
20
|
+
raise "Failed performing YQLQuery\n Request: #{request.inspect}\n Response #{response.inspect}" unless response.code == 200
|
|
21
|
+
return JSON.parse(response)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def get_symbol_current(my_symbol)
|
|
26
|
+
run_select_query("select * from yahoo.finance.quotes where symbol in (\"#{my_symbol}\")")["query"]["results"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_symbol_historical(my_symbol, start_date = nil, end_date = nil)
|
|
30
|
+
requested_end = Date.parse(end_date) if end_date
|
|
31
|
+
requested_end = Date.today unless requested_end
|
|
32
|
+
|
|
33
|
+
requested_start = Date.parse(start_date) if start_date
|
|
34
|
+
requested_start = requested_end - 30 unless requested_start
|
|
35
|
+
run_select_query("select * from yahoo.finance.historicaldata where symbol in (\"#{my_symbol}\") and startDate='#{requested_start}' and endDate='#{requested_end}'")["query"]["results"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ytterb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.152.223223
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thext
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-
|
|
11
|
+
date: 2014-06-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -94,20 +94,25 @@ files:
|
|
|
94
94
|
- README.md
|
|
95
95
|
- Rakefile
|
|
96
96
|
- lib/ytterb.rb
|
|
97
|
-
- lib/ytterb/cached_symbol_data/.keepme
|
|
98
|
-
- lib/ytterb/cached_symbol_data/clean.sh
|
|
99
|
-
- lib/ytterb/
|
|
100
|
-
- lib/ytterb/
|
|
101
|
-
- lib/ytterb/raw_symbol_data/
|
|
102
|
-
- lib/ytterb/raw_symbol_data/
|
|
103
|
-
- lib/ytterb/raw_symbol_data/companylist_nyse.csv
|
|
104
|
-
- lib/ytterb/settings.rb
|
|
105
|
-
- lib/ytterb/stock_market_loader.rb
|
|
97
|
+
- lib/ytterb/.cache/cached_symbol_data/.keepme
|
|
98
|
+
- lib/ytterb/.cache/cached_symbol_data/clean.sh
|
|
99
|
+
- lib/ytterb/.cache/local_settings/settings.yaml
|
|
100
|
+
- lib/ytterb/.cache/raw_symbol_data/companylist_amex.csv
|
|
101
|
+
- lib/ytterb/.cache/raw_symbol_data/companylist_nasdaq.csv
|
|
102
|
+
- lib/ytterb/.cache/raw_symbol_data/companylist_nyse.csv
|
|
106
103
|
- lib/ytterb/stock_symbol.rb
|
|
107
|
-
- lib/ytterb/
|
|
108
|
-
- lib/ytterb/
|
|
104
|
+
- lib/ytterb/stock_symbol/_req.rb
|
|
105
|
+
- lib/ytterb/stock_symbol/cache_builder.rb
|
|
106
|
+
- lib/ytterb/stock_symbol/market_loader.rb
|
|
107
|
+
- lib/ytterb/stock_symbol/stock.rb
|
|
108
|
+
- lib/ytterb/util.rb
|
|
109
|
+
- lib/ytterb/util/_req.rb
|
|
110
|
+
- lib/ytterb/util/data_persist_helper.rb
|
|
111
|
+
- lib/ytterb/util/settings.rb
|
|
109
112
|
- lib/ytterb/version.rb
|
|
110
|
-
- lib/ytterb/
|
|
113
|
+
- lib/ytterb/yql.rb
|
|
114
|
+
- lib/ytterb/yql/_req.rb
|
|
115
|
+
- lib/ytterb/yql/client.rb
|
|
111
116
|
- spec/spec_helper.rb
|
|
112
117
|
- spec/ytterb_spec.rb
|
|
113
118
|
- ytterb.gemspec
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
|
|
3
|
-
module Ytterb
|
|
4
|
-
class DataPersistHelper
|
|
5
|
-
def self.save(file, value)
|
|
6
|
-
File.open(file,"w") {|f| f.write(YAML.dump(value)) }
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def self.load(file)
|
|
10
|
-
File.open(file,"r") {|f| YAML.load(f.read()) }
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def self.get_extension
|
|
14
|
-
return ".yaml"
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
data/lib/ytterb/settings.rb
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
require_relative 'data_persist_helper'
|
|
2
|
-
|
|
3
|
-
module Ytterb
|
|
4
|
-
class Settings
|
|
5
|
-
def initialize
|
|
6
|
-
@options = {
|
|
7
|
-
:settings_dir => "local_settings",
|
|
8
|
-
:settings_file => File.join("settings",DataPersistHelper.get_extension()),
|
|
9
|
-
:cached_symbol_data_dir => "cached_symbol_data"
|
|
10
|
-
}.freeze
|
|
11
|
-
@local_path = File.expand_path(File.dirname(__FILE__))
|
|
12
|
-
load
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def set_defaults_if_uninitialized
|
|
16
|
-
# defaults to be used if not defined
|
|
17
|
-
@local_settings ||= {}
|
|
18
|
-
@local_settings[:sync_from] ||= (Date.today-365).to_s
|
|
19
|
-
@local_settings[:sync_increment] ||= 120
|
|
20
|
-
@local_settings[:api_calls_per_hour] ||= 1900
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def create_full_path_and_file(full_path)
|
|
24
|
-
FileUtils.mkdir_p(File.dirname(full_path))
|
|
25
|
-
unless File.file?(full_path)
|
|
26
|
-
FileUtils.touch(full_path)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def full_path_settings_file
|
|
31
|
-
File.join(@local_path,@options[:settings_dir],@options[:settings_file])
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def get_symbol_store_file(symbol, build_path_and_file = true)
|
|
35
|
-
split_for_storage = symbol.split('')
|
|
36
|
-
split_for_storage[-1] += DataPersistHelper.get_extension()
|
|
37
|
-
full_path = File.join(@local_path,@options[:cached_symbol_data_dir],split_for_storage)
|
|
38
|
-
create_full_path_and_file(full_path) if build_path_and_file == true
|
|
39
|
-
full_path
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def [](key)
|
|
43
|
-
@local_settings[key]
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def []=(key, value)
|
|
47
|
-
@local_settings[key] = value
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def load
|
|
51
|
-
@local_settings = DataPersistHelper.load(full_path_settings_file())
|
|
52
|
-
rescue
|
|
53
|
-
ensure
|
|
54
|
-
set_defaults_if_uninitialized
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def save
|
|
58
|
-
create_full_path_and_file(full_path_settings_file())
|
|
59
|
-
DataPersistHelper.load(full_path_settings_file(), @local_settings)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
require_relative 'stock_symbol_loader'
|
|
2
|
-
|
|
3
|
-
module Ytterb
|
|
4
|
-
|
|
5
|
-
class StockMarketLoader
|
|
6
|
-
def initialize
|
|
7
|
-
@stock_symbols = []
|
|
8
|
-
path = File.join(File.expand_path(File.dirname(__FILE__)),"raw_symbol_data")
|
|
9
|
-
Dir.entries(path).select {|f| !File.directory?(f) and /companylist_[a-zA-Z]+\.csv/ =~ f }.each do |file_to_process|
|
|
10
|
-
market = /companylist_(?<market>[a-zA-Z]+)\.csv/.match(file_to_process)[:market]
|
|
11
|
-
StockSymbolLoader.new(File.join(path,file_to_process),market).parse do |stock_symbol|
|
|
12
|
-
@stock_symbols << stock_symbol
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def industries
|
|
18
|
-
return @industries if @industries
|
|
19
|
-
@raw_industries = {}
|
|
20
|
-
@stock_symbols.each do |stock_symbol|
|
|
21
|
-
@raw_industries[stock_symbol.industry] = 1
|
|
22
|
-
end
|
|
23
|
-
@industries = @raw_industries.keys
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def sectors
|
|
27
|
-
return @sectors if @sectors
|
|
28
|
-
@raw_sectors = {}
|
|
29
|
-
@stock_symbols.each do |stock_symbol|
|
|
30
|
-
@raw_sectors[stock_symbol.sector] = 1
|
|
31
|
-
end
|
|
32
|
-
@sectors = @raw_sectors.keys
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def stock_symbols
|
|
36
|
-
@stock_symbols
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
end
|
|
41
|
-
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
require 'fileutils'
|
|
2
|
-
require 'thread'
|
|
3
|
-
require 'date'
|
|
4
|
-
require_relative 'data_persist_helper'
|
|
5
|
-
require_relative 'settings'
|
|
6
|
-
require_relative 'stock_market_loader'
|
|
7
|
-
require_relative 'yql_client'
|
|
8
|
-
|
|
9
|
-
module Ytterb
|
|
10
|
-
|
|
11
|
-
class StockSymbolCacheBuilder
|
|
12
|
-
|
|
13
|
-
def initialize
|
|
14
|
-
@yql = YQLClient.new
|
|
15
|
-
@sml = StockMarketLoader.new
|
|
16
|
-
@local_settings = Settings.new
|
|
17
|
-
|
|
18
|
-
# build the queue used when syncing
|
|
19
|
-
build_stock_sync_queue
|
|
20
|
-
|
|
21
|
-
# run the processor that fetches and persists stock info
|
|
22
|
-
stock_processor_run
|
|
23
|
-
|
|
24
|
-
@local_settings.save
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def build_stock_sync_queue
|
|
28
|
-
@stock_sync_queue = Queue.new
|
|
29
|
-
@sml.stock_symbols.shuffle.each do |stock|
|
|
30
|
-
@stock_sync_queue << stock.symbol
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def stock_processor_run
|
|
35
|
-
while true
|
|
36
|
-
begin
|
|
37
|
-
curr = @stock_sync_queue.pop(true) rescue nil
|
|
38
|
-
break unless curr # queue is empty
|
|
39
|
-
sleep(3600.0/@local_settings[:api_calls_per_hour])
|
|
40
|
-
puts "#{@stock_sync_queue.length} symbols in the queue"
|
|
41
|
-
puts "Processing Symbol: #{curr}"
|
|
42
|
-
curr_file = @local_settings.get_symbol_store_file(curr)
|
|
43
|
-
curr_stock_info = DataPersistHelper.load(curr_file) rescue nil
|
|
44
|
-
curr_stock_info ||= {}
|
|
45
|
-
curr_stock_info[:sync_to] ||= @local_settings[:sync_from]
|
|
46
|
-
date_start = Date.parse(curr_stock_info[:sync_to])
|
|
47
|
-
if (date_start < Date.today)
|
|
48
|
-
date_end = date_start + @local_settings[:sync_increment]
|
|
49
|
-
date_end = Date.today if date_end > Date.today
|
|
50
|
-
result = @yql.get_symbol_historical(curr,date_start.to_s, date_end.to_s)
|
|
51
|
-
curr_stock_info[:sync_to] = date_end.to_s
|
|
52
|
-
if result and result.has_key?("quote")
|
|
53
|
-
result["quote"].each do |item|
|
|
54
|
-
curr_stock_info[:data]||={}
|
|
55
|
-
curr_stock_info[:data][item["Date"]] = item
|
|
56
|
-
end
|
|
57
|
-
@stock_sync_queue << curr
|
|
58
|
-
puts curr_stock_info[:sync_to]
|
|
59
|
-
DataPersistHelper.save(curr_file,curr_stock_info)
|
|
60
|
-
else
|
|
61
|
-
puts "Debug result: #{result}"
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
rescue StandardError => e
|
|
65
|
-
puts "error in stock processor run: #{e.message} : #{e.backtrace.join(" | ")}"
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
end # StockSymbolCacheBuilder
|
|
72
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require 'csv'
|
|
2
|
-
require_relative 'stock_symbol'
|
|
3
|
-
|
|
4
|
-
module Ytterb
|
|
5
|
-
|
|
6
|
-
class StockSymbolLoader
|
|
7
|
-
def initialize(stock_symbol_file, exchange)
|
|
8
|
-
@stock_symbol_file = stock_symbol_file
|
|
9
|
-
@exchange = exchange
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def parse
|
|
13
|
-
CSV.foreach(@stock_symbol_file, :headers => true) do |obj|
|
|
14
|
-
yield StockSymbol.new(obj.to_hash.merge("Exchange" => @exchange))
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
end
|
data/lib/ytterb/yql_client.rb
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
require 'rest-client'
|
|
2
|
-
require 'cgi'
|
|
3
|
-
require 'json'
|
|
4
|
-
require 'date'
|
|
5
|
-
|
|
6
|
-
module Ytterb
|
|
7
|
-
|
|
8
|
-
class YQLClient
|
|
9
|
-
def initialize
|
|
10
|
-
@endpoint = "http://query.yahooapis.com/v1/public/yql"
|
|
11
|
-
puts "YQL Client is using endpoint @ #{@endpoint}"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def get_yql_endpoint
|
|
15
|
-
@endpoint
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def run_select_query(query)
|
|
19
|
-
RestClient.get("#{get_yql_endpoint}?q=#{CGI::escape(query)}&format=json&env=store://datatables.org/alltableswithkeys") do |response, request, result|
|
|
20
|
-
raise "Failed performing YQLQuery\n Request: #{request.inspect}\n Response #{response.inspect}" unless response.code == 200
|
|
21
|
-
return JSON.parse(response)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def get_symbol_current(my_symbol)
|
|
26
|
-
run_select_query("select * from yahoo.finance.quotes where symbol in (\"#{my_symbol}\")")["query"]["results"]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def get_symbol_historical(my_symbol, start_date = nil, end_date = nil)
|
|
30
|
-
requested_end = Date.parse(end_date) if end_date
|
|
31
|
-
requested_end = Date.today unless requested_end
|
|
32
|
-
|
|
33
|
-
requested_start = Date.parse(start_date) if start_date
|
|
34
|
-
requested_start = requested_end - 30 unless requested_start
|
|
35
|
-
run_select_query("select * from yahoo.finance.historicaldata where symbol in (\"#{my_symbol}\") and startDate='#{requested_start}' and endDate='#{requested_end}'")["query"]["results"]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
39
|
-
end
|