tushare 0.1.0
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 +7 -0
- data/.gitignore +21 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +28 -0
- data/README.md +41 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/tushare.rb +18 -0
- data/lib/tushare/datayes.rb +50 -0
- data/lib/tushare/datayes/bond.rb +69 -0
- data/lib/tushare/datayes/constants.rb +598 -0
- data/lib/tushare/datayes/equity.rb +111 -0
- data/lib/tushare/datayes/fund.rb +115 -0
- data/lib/tushare/datayes/fundamental.rb +260 -0
- data/lib/tushare/datayes/future.rb +22 -0
- data/lib/tushare/datayes/hk_equity.rb +18 -0
- data/lib/tushare/datayes/idx.rb +19 -0
- data/lib/tushare/datayes/iv.rb +60 -0
- data/lib/tushare/datayes/macro.rb +3517 -0
- data/lib/tushare/datayes/market.rb +286 -0
- data/lib/tushare/datayes/master.rb +67 -0
- data/lib/tushare/datayes/options.rb +22 -0
- data/lib/tushare/datayes/subject.rb +349 -0
- data/lib/tushare/internet/box_office.rb +155 -0
- data/lib/tushare/stock/billboard.rb +197 -0
- data/lib/tushare/stock/classifying.rb +288 -0
- data/lib/tushare/stock/fundamental.rb +232 -0
- data/lib/tushare/stock/macro.rb +253 -0
- data/lib/tushare/stock/news_event.rb +165 -0
- data/lib/tushare/stock/reference.rb +473 -0
- data/lib/tushare/stock/shibor.rb +136 -0
- data/lib/tushare/stock/trading.rb +513 -0
- data/lib/tushare/util.rb +293 -0
- data/lib/tushare/version.rb +3 -0
- data/tushare.gemspec +32 -0
- metadata +211 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'tushare/util'
|
2
|
+
|
3
|
+
module Tushare
|
4
|
+
module Stock
|
5
|
+
# 上海银行间同业拆放利率(Shibor)数据接口
|
6
|
+
module Shibor
|
7
|
+
extend Tushare::Util
|
8
|
+
|
9
|
+
# 获取上海银行间同业拆放利率(Shibor)
|
10
|
+
# Parameters
|
11
|
+
# ------
|
12
|
+
# year:年份(int)
|
13
|
+
|
14
|
+
# Return
|
15
|
+
# ------
|
16
|
+
# date:日期
|
17
|
+
# ON:隔夜拆放利率
|
18
|
+
# 1W:1周拆放利率
|
19
|
+
# 2W:2周拆放利率
|
20
|
+
# 1M:1个月拆放利率
|
21
|
+
# 3M:3个月拆放利率
|
22
|
+
# 6M:6个月拆放利率
|
23
|
+
# 9M:9个月拆放利率
|
24
|
+
# 1Y:1年拆放利率
|
25
|
+
def shibor_data(year = Time.now.year)
|
26
|
+
url = format(SHIBOR_DATA_URL, P_TYPE['http'], DOMAINS['shibor'],
|
27
|
+
PAGES['dw'], 'Shibor', year, SHIBOR_TYPE['Shibor'], year)
|
28
|
+
xls = ::Roo::Spreadsheet.open(URI.encode(url), extension: 'xls')
|
29
|
+
process_xls_file(xls, SHIBOR_COLS)
|
30
|
+
end
|
31
|
+
|
32
|
+
# 获取Shibor银行报价数据
|
33
|
+
# Parameters
|
34
|
+
# ------
|
35
|
+
# year:年份(int)
|
36
|
+
|
37
|
+
# Return
|
38
|
+
# ------
|
39
|
+
# date:日期
|
40
|
+
# bank:报价银行名称
|
41
|
+
# ON:隔夜拆放利率
|
42
|
+
# ON_B:隔夜拆放买入价
|
43
|
+
# ON_A:隔夜拆放卖出价
|
44
|
+
# 1W_B:1周买入
|
45
|
+
# 1W_A:1周卖出
|
46
|
+
# 2W_B:买入
|
47
|
+
# 2W_A:卖出
|
48
|
+
# 1M_B:买入
|
49
|
+
# 1M_A:卖出
|
50
|
+
# 3M_B:买入
|
51
|
+
# 3M_A:卖出
|
52
|
+
# 6M_B:买入
|
53
|
+
# 6M_A:卖出
|
54
|
+
# 9M_B:买入
|
55
|
+
# 9M_A:卖出
|
56
|
+
# 1Y_B:买入
|
57
|
+
# 1Y_A:卖出
|
58
|
+
def shibor_quote_data(year = Time.now.year)
|
59
|
+
url = format(SHIBOR_DATA_URL, P_TYPE['http'], DOMAINS['shibor'],
|
60
|
+
PAGES['dw'], 'Quote', year, SHIBOR_TYPE['Quote'], year)
|
61
|
+
xls = ::Roo::Spreadsheet.open(URI.encode(url), extension: 'xls')
|
62
|
+
process_xls_file(xls, QUOTE_COLS)
|
63
|
+
end
|
64
|
+
|
65
|
+
# 获取Shibor均值数据
|
66
|
+
# Parameters
|
67
|
+
# ------
|
68
|
+
# year:年份(int)
|
69
|
+
|
70
|
+
# Return
|
71
|
+
# ------
|
72
|
+
# date:日期
|
73
|
+
# 其它分别为各周期5、10、20均价
|
74
|
+
def shibor_ma_data(year = Time.now.year)
|
75
|
+
url = format(SHIBOR_DATA_URL, P_TYPE['http'], DOMAINS['shibor'],
|
76
|
+
PAGES['dw'], 'Shibor_Tendency', year, SHIBOR_TYPE['Tendency'],
|
77
|
+
year)
|
78
|
+
xls = ::Roo::Spreadsheet.open(URI.encode(url), extension: 'xls')
|
79
|
+
process_xls_file(xls, SHIBOR_MA_COLS)
|
80
|
+
end
|
81
|
+
|
82
|
+
# 获取贷款基础利率(LPR)
|
83
|
+
# Parameters
|
84
|
+
# ------
|
85
|
+
# year:年份(int)
|
86
|
+
|
87
|
+
# Return
|
88
|
+
# ------
|
89
|
+
# date:日期
|
90
|
+
# 1Y:1年贷款基础利率
|
91
|
+
def lpr_data(year = Time.now.year)
|
92
|
+
url = format(SHIBOR_DATA_URL, P_TYPE['http'], DOMAINS['shibor'],
|
93
|
+
PAGES['dw'], 'LPR', year, SHIBOR_TYPE['LPR'], year)
|
94
|
+
xls = ::Roo::Spreadsheet.open(URI.encode(url), extension: 'xls')
|
95
|
+
process_xls_file(xls, LPR_COLS)
|
96
|
+
end
|
97
|
+
|
98
|
+
# 获取贷款基础利率均值数据
|
99
|
+
# Parameters
|
100
|
+
# ------
|
101
|
+
# year:年份(int)
|
102
|
+
|
103
|
+
# Return
|
104
|
+
# ------
|
105
|
+
# date:日期
|
106
|
+
# 1Y_5:5日均值
|
107
|
+
# 1Y_10:10日均值
|
108
|
+
# 1Y_20:20日均值
|
109
|
+
def lpr_ma_data(year = Time.now.year)
|
110
|
+
url = format(SHIBOR_DATA_URL, P_TYPE['http'], DOMAINS['shibor'],
|
111
|
+
PAGES['dw'], 'LPR_Tendency', year,
|
112
|
+
SHIBOR_TYPE['LPR_Tendency'], year)
|
113
|
+
xls = ::Roo::Spreadsheet.open(URI.encode(url), extension: 'xls')
|
114
|
+
process_xls_file(xls, LPR_MA_COLS)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# process xls every sheet and every sheet skip the first row
|
120
|
+
def process_xls_file(xls, cols)
|
121
|
+
result = []
|
122
|
+
xls.each_with_pagename do |_, sheet|
|
123
|
+
sheet.drop(1).each do |row|
|
124
|
+
object = {}
|
125
|
+
cols.each_with_index { |key, index| object[key] = row[index] }
|
126
|
+
result << object
|
127
|
+
end
|
128
|
+
end
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
module_function :shibor_data, :shibor_quote_data, :shibor_ma_data,
|
133
|
+
:lpr_data, :lpr_ma_data, :process_xls_file
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,513 @@
|
|
1
|
+
require 'tushare/util'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
module Tushare
|
6
|
+
module Stock
|
7
|
+
module Trading
|
8
|
+
extend Tushare::Util
|
9
|
+
|
10
|
+
# 获取个股历史交易记录
|
11
|
+
# Parameters
|
12
|
+
# ------
|
13
|
+
# code:string
|
14
|
+
# 股票代码 e.g. 600848
|
15
|
+
# start_date:string
|
16
|
+
# 开始日期 format:YYYY-MM-DD 为空时取到API所提供的最早日期数据
|
17
|
+
# end_date:string
|
18
|
+
# 结束日期 format:YYYY-MM-DD 为空时取到最近一个交易日数据
|
19
|
+
# ktype:string
|
20
|
+
# 数据类型,D=日k线 W=周 M=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为D
|
21
|
+
# return
|
22
|
+
# -------
|
23
|
+
# DataFrame
|
24
|
+
# 属性:日期 ,开盘价, 最高价, 收盘价, 最低价, 成交量, 价格变动 ,涨跌幅,5日均价,10日均价,20日均价,5日均量,10日均量,20日均量,换手率
|
25
|
+
def get_hist_data(code, options = {})
|
26
|
+
|
27
|
+
symbol_code = _code_to_symbol(code)
|
28
|
+
|
29
|
+
raise 'invalid code' if symbol_code == ''
|
30
|
+
|
31
|
+
options = { start_date: '', end_date: '', ktype: 'D', ascending: false }.merge(options)
|
32
|
+
|
33
|
+
if K_LABELS.include?(options[:ktype].upcase)
|
34
|
+
url = format(DAY_PRICE_URL,
|
35
|
+
P_TYPE['http'],
|
36
|
+
DOMAINS['ifeng'],
|
37
|
+
K_TYPE[options[:ktype].upcase],
|
38
|
+
symbol_code)
|
39
|
+
elsif K_MIN_LABELS.include?(options[:ktype])
|
40
|
+
url = format(DAY_PRICE_MIN_URL,
|
41
|
+
P_TYPE['http'],
|
42
|
+
DOMAINS['ifeng'],
|
43
|
+
symbol_code,
|
44
|
+
options[:ktype])
|
45
|
+
else
|
46
|
+
raise 'ktype input error.'
|
47
|
+
end
|
48
|
+
|
49
|
+
cols = INDEX_LABELS.include?(code) ? INX_DAY_PRICE_COLUMNS : DAY_PRICE_COLUMNS
|
50
|
+
|
51
|
+
resp = HTTParty.get(url)
|
52
|
+
if resp.code.to_s == '200'
|
53
|
+
records = JSON.parse(resp.body)['record']
|
54
|
+
unless records.empty?
|
55
|
+
cols = INX_DAY_PRICE_COLUMNS if records.first.size == 14
|
56
|
+
|
57
|
+
records.reverse! if options[:ascending] == false
|
58
|
+
if options[:start_date] != '' && options[:end_date] != ''
|
59
|
+
records = records.select { |x| Time.parse(x[0]) <= Time.parse("#{options[:end_date]} 23:59") && Time.parse(x[0]) >= Time.parse("#{options[:start_date]} 00:00") }
|
60
|
+
end
|
61
|
+
|
62
|
+
records.map { |r| Hash[cols.zip(r)] }
|
63
|
+
end
|
64
|
+
else
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
# '''
|
69
|
+
# 获取分笔数据
|
70
|
+
# Parameters
|
71
|
+
# ------
|
72
|
+
# code:string
|
73
|
+
# 股票代码 e.g. 600848
|
74
|
+
# date:string
|
75
|
+
# 日期 format:YYYY-MM-DD
|
76
|
+
# return
|
77
|
+
# -------
|
78
|
+
# DataFrame 当日所有股票交易数据(DataFrame)
|
79
|
+
# 属性:成交时间、成交价格、价格变动,成交手、成交金额(元),买卖类型
|
80
|
+
# '''
|
81
|
+
def get_tick_data(code, date = '')
|
82
|
+
return nil if code.nil? || code.size != 6 || date == ''
|
83
|
+
|
84
|
+
symbol_code = _code_to_symbol(code)
|
85
|
+
|
86
|
+
raise 'invalid code' if symbol_code == ''
|
87
|
+
|
88
|
+
url = format(TICK_PRICE_URL, P_TYPE['http'], DOMAINS['sf'], PAGES['dl'], date, symbol_code)
|
89
|
+
|
90
|
+
resp = HTTParty.get(url)
|
91
|
+
if resp.code.to_s == '200'
|
92
|
+
tb_value = resp.body.encode('utf-8', 'gbk')
|
93
|
+
CSV.new(tb_value, headers: :first_row, encoding: 'utf-8', col_sep: ' ').map { |a| Hash[TICK_COLUMNS.zip(a.fields)] }
|
94
|
+
else
|
95
|
+
[]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
# '''
|
99
|
+
# 获取sina大单数据
|
100
|
+
# Parameters
|
101
|
+
# ------
|
102
|
+
# code:string
|
103
|
+
# 股票代码 e.g. 600848
|
104
|
+
# date:string
|
105
|
+
# 日期 format:YYYY-MM-DD
|
106
|
+
# retry_count : int, 默认 3
|
107
|
+
# 如遇网络等问题重复执行的次数
|
108
|
+
# pause : int, 默认 0
|
109
|
+
# 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题
|
110
|
+
# return
|
111
|
+
# -------
|
112
|
+
# DataFrame 当日所有股票交易数据(DataFrame)
|
113
|
+
# 属性:股票代码 股票名称 交易时间 价格 成交量 前一笔价格 类型(买、卖、中性盘)
|
114
|
+
# '''
|
115
|
+
def get_sina_dd(code, options = {})
|
116
|
+
return nil if code.nil? || code.size != 6
|
117
|
+
symbol_code = _code_to_symbol(code)
|
118
|
+
raise 'invalid code' if symbol_code == ''
|
119
|
+
|
120
|
+
options = { date: Time.now.strftime('%Y-%m-%d'), vol: 400 }.merge(options)
|
121
|
+
|
122
|
+
vol = options[:vol] * 100
|
123
|
+
url = format(SINA_DD, P_TYPE['http'], DOMAINS['vsf'], PAGES['sinadd'], symbol_code, vol, options[:date])
|
124
|
+
resp = HTTParty.get(url)
|
125
|
+
if resp.code.to_s == '200'
|
126
|
+
val = resp.body.encode('utf-8', 'gbk')
|
127
|
+
CSV.new(val, headers: :first_row, encoding: 'utf-8', col_sep: ',').map do |a|
|
128
|
+
fields = a.fields
|
129
|
+
fields[0] = fields[0][2..-1]
|
130
|
+
Hash[SINA_DD_COLS.zip(fields)]
|
131
|
+
end
|
132
|
+
else
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
# '''
|
137
|
+
# 获取当日分笔明细数据
|
138
|
+
# Parameters
|
139
|
+
# ------
|
140
|
+
# code:string
|
141
|
+
# 股票代码 e.g. 600848
|
142
|
+
# retry_count : int, 默认 3
|
143
|
+
# 如遇网络等问题重复执行的次数
|
144
|
+
# pause : int, 默认 0
|
145
|
+
# 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题
|
146
|
+
# return
|
147
|
+
# -------
|
148
|
+
# DataFrame 当日所有股票交易数据(DataFrame)
|
149
|
+
# 属性:成交时间、成交价格、价格变动,成交手、成交金额(元),买卖类型
|
150
|
+
# '''
|
151
|
+
def get_today_ticks(code)
|
152
|
+
return nil if code.nil? || code.size != 6
|
153
|
+
symbol_code = _code_to_symbol(code)
|
154
|
+
raise 'invalid code' if symbol_code == ''
|
155
|
+
|
156
|
+
all_ticks = []
|
157
|
+
|
158
|
+
url = format(TODAY_TICKS_PAGE_URL, P_TYPE['http'], DOMAINS['vsf'], PAGES['jv'], Time.now.strftime('%Y-%m-%d'), symbol_code)
|
159
|
+
resp = HTTParty.get(url)
|
160
|
+
if resp.code.to_s == '200'
|
161
|
+
val = eval(resp.body[1..-2])
|
162
|
+
pages = val[:detailPages].sort { |x, y| x[:page] <=> y[:page] }
|
163
|
+
date = Time.now.strftime('%Y-%m-%d')
|
164
|
+
_write_head
|
165
|
+
pages.each do |page|
|
166
|
+
_write_console
|
167
|
+
_today_ticks(symbol_code, date, page[:page], all_ticks)
|
168
|
+
end
|
169
|
+
all_ticks
|
170
|
+
else
|
171
|
+
[]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
# '''
|
175
|
+
# 一次性获取最近一个日交易日所有股票的交易数据
|
176
|
+
# return
|
177
|
+
# -------
|
178
|
+
# DataFrame
|
179
|
+
# 属性:代码,名称,涨跌幅,现价,开盘价,最高价,最低价,前日收盘价,成交量,换手率
|
180
|
+
# '''
|
181
|
+
def get_today_all
|
182
|
+
_write_head
|
183
|
+
all_data = []
|
184
|
+
data = _parsing_dayprice_json(1)
|
185
|
+
unless data.nil?
|
186
|
+
all_data << data
|
187
|
+
(2...PAGE_NUM[0]).each do |page|
|
188
|
+
nd = _parsing_dayprice_json(page)
|
189
|
+
all_data << nd unless nd.nil?
|
190
|
+
end
|
191
|
+
all_data.flatten!
|
192
|
+
end
|
193
|
+
all_data
|
194
|
+
end
|
195
|
+
|
196
|
+
# 获取实时交易数据 getting real time quotes data
|
197
|
+
# 用于跟踪交易情况(本次执行的结果-上一次执行的数据)
|
198
|
+
# Parameters
|
199
|
+
# ------
|
200
|
+
# symbols : string, array-like object (list, tuple, Series).
|
201
|
+
# return
|
202
|
+
# -------
|
203
|
+
# DataFrame 实时交易数据
|
204
|
+
# 属性:0:name,股票名字
|
205
|
+
# 1:open,今日开盘价
|
206
|
+
# 2:pre_close,昨日收盘价
|
207
|
+
# 3:price,当前价格
|
208
|
+
# 4:high,今日最高价
|
209
|
+
# 5:low,今日最低价
|
210
|
+
# 6:bid,竞买价,即“买一”报价
|
211
|
+
# 7:ask,竞卖价,即“卖一”报价
|
212
|
+
# 8:volumn,成交量 maybe you need do volumn/100
|
213
|
+
# 9:amount,成交金额(元 CNY)
|
214
|
+
# 10:b1_v,委买一(笔数 bid volume)
|
215
|
+
# 11:b1_p,委买一(价格 bid price)
|
216
|
+
# 12:b2_v,“买二”
|
217
|
+
# 13:b2_p,“买二”
|
218
|
+
# 14:b3_v,“买三”
|
219
|
+
# 15:b3_p,“买三”
|
220
|
+
# 16:b4_v,“买四”
|
221
|
+
# 17:b4_p,“买四”
|
222
|
+
# 18:b5_v,“买五”
|
223
|
+
# 19:b5_p,“买五”
|
224
|
+
# 20:a1_v,委卖一(笔数 ask volume)
|
225
|
+
# 21:a1_p,委卖一(价格 ask price)
|
226
|
+
# ...
|
227
|
+
# 30:date,日期;
|
228
|
+
# 31:time,时间;
|
229
|
+
def get_realtime_quotes(symbols = nil)
|
230
|
+
symbols = [symbols] if symbols.is_a?(String)
|
231
|
+
symbols_list = (symbols || []).map { |code| code_to_symbol(code) }
|
232
|
+
.join(',')
|
233
|
+
url = format(LIVE_DATA_URL, P_TYPE['http'], DOMAINS['sinahq'],
|
234
|
+
_random, symbols_list)
|
235
|
+
resp = HTTParty.get(url)
|
236
|
+
resp_string = resp.body.encode('utf-8', 'gbk')
|
237
|
+
result = []
|
238
|
+
resp_string.scan(/\="(.*)";/).each_with_index do |match_array, match_index|
|
239
|
+
string = match_array.first
|
240
|
+
object = { 'code' => symbols[match_index] }
|
241
|
+
string.split(',').each_with_index do |s, index|
|
242
|
+
next if LIVE_DATA_COLS[index] == 's'
|
243
|
+
object[LIVE_DATA_COLS[index]] = s
|
244
|
+
end
|
245
|
+
result << object
|
246
|
+
end
|
247
|
+
result
|
248
|
+
end
|
249
|
+
|
250
|
+
# 获取历史复权数据
|
251
|
+
# Parameters
|
252
|
+
# ------
|
253
|
+
# code:string
|
254
|
+
# 股票代码 e.g. 600848
|
255
|
+
# start:string
|
256
|
+
# 开始日期 format:YYYY-MM-DD 为空时取当前日期
|
257
|
+
# end:string
|
258
|
+
# 结束日期 format:YYYY-MM-DD 为空时取去年今日
|
259
|
+
# autype:string
|
260
|
+
# 复权类型,qfq-前复权 hfq-后复权 None-不复权,默认为qfq
|
261
|
+
# drop_factor : bool, 默认 True
|
262
|
+
# 是否移除复权因子,在分析过程中可能复权因子意义不大,但是如需要先储存到数据库之后再分析的话,有该项目会更加灵活
|
263
|
+
# return
|
264
|
+
# -------
|
265
|
+
# DataFrame
|
266
|
+
# date 交易日期 (index)
|
267
|
+
# open 开盘价
|
268
|
+
# high 最高价
|
269
|
+
# close 收盘价
|
270
|
+
# low 最低价
|
271
|
+
# volume 成交量
|
272
|
+
# amount 成交金额
|
273
|
+
def get_h_data(code, start_date = nil, end_date = nil, autype = 'qfq',
|
274
|
+
index = false, drop_factor = true)
|
275
|
+
start_date = if start_date.nil?
|
276
|
+
Date.today.prev_year
|
277
|
+
else
|
278
|
+
Date.strptime(start_date, '%F')
|
279
|
+
end
|
280
|
+
end_date = if end_date.nil?
|
281
|
+
Date.today
|
282
|
+
else
|
283
|
+
Date.strptime(end_date, '%F')
|
284
|
+
end
|
285
|
+
qs = start_date.step(end_date)
|
286
|
+
.map { |d| [d.year, (d.month + 2) / 3] }
|
287
|
+
.uniq
|
288
|
+
_write_head
|
289
|
+
result = _parse_fq_data(_get_index_url(index, code, qs[0]), index)
|
290
|
+
if qs.length > 1
|
291
|
+
1.upto(qs.length - 1).each do |i|
|
292
|
+
_write_console
|
293
|
+
result.concat _parse_fq_data(_get_index_url(index, code, qs[i]),
|
294
|
+
index)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
return [] if result.empty?
|
298
|
+
sorted_result = result.sort_by { |object| Date.strptime(object['date'], '%F') }
|
299
|
+
last_date = sorted_result.last['date']
|
300
|
+
result = result.uniq { |object| object['date'] }.select do |object|
|
301
|
+
date = Date.strptime(object['date'], '%F')
|
302
|
+
date >= start_date && date <= end_date
|
303
|
+
end
|
304
|
+
if index
|
305
|
+
return result.sort_by { |object| Date.strptime(object['date'], '%F') }
|
306
|
+
end
|
307
|
+
# 判断复权
|
308
|
+
if autype == 'hfq'
|
309
|
+
result.map do |object|
|
310
|
+
object.delete 'factor' if drop_factor
|
311
|
+
%w(open high close low).each do |key|
|
312
|
+
object[key] = object[key].to_f.round(2)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
if index
|
316
|
+
return result.sort_by { |object| Date.strptime(object['date'], '%F') }
|
317
|
+
end
|
318
|
+
result
|
319
|
+
elsif autype == 'qfq'
|
320
|
+
result.map { |object| object.delete 'factor' } if drop_factor
|
321
|
+
fq_factors = _parse_fq_factor(code, start_date, end_date)
|
322
|
+
fq_factors.sort_by! { |object| Date.strptime(object['date'], '%F') }
|
323
|
+
frow = fq_factors.select { |object| object['date'] == last_date }.first
|
324
|
+
rt = get_realtime_quotes(code)
|
325
|
+
return nil if rt.empty?
|
326
|
+
pre_close = if rt.first['high'].to_f == 0 && rt.first['low'].to_f == 0
|
327
|
+
rt.first['pre_close'].to_f
|
328
|
+
elsif holiday? Date.today
|
329
|
+
rt.first['price'].to_f
|
330
|
+
elsif Time.now.hour > 9 && Time.now.hour < 18
|
331
|
+
rt.first['pre_close'].to_f
|
332
|
+
else
|
333
|
+
rt.first['price'].to_f
|
334
|
+
end
|
335
|
+
rate = frow['factor'].to_f / pre_close
|
336
|
+
result.each do |object|
|
337
|
+
%w(open high low close).each do |key|
|
338
|
+
object[key] = (object[key].to_f / rate).round(2)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
result.sort_by { |object| Date.strptime(object['date'], '%F') }
|
342
|
+
else
|
343
|
+
result.each do |object|
|
344
|
+
%w(open high low close).each do |key|
|
345
|
+
object[key] = (object[key].to_f / object['factor'].to_f).round(2)
|
346
|
+
end
|
347
|
+
object.delete 'factor' if drop_factor
|
348
|
+
end
|
349
|
+
result.sort_by { |object| Date.strptime(object['date'], '%F') }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# 获取大盘指数行情
|
354
|
+
# return
|
355
|
+
# -------
|
356
|
+
# DataFrame
|
357
|
+
# code:指数代码
|
358
|
+
# name:指数名称
|
359
|
+
# change:涨跌幅
|
360
|
+
# open:开盘价
|
361
|
+
# preclose:昨日收盘价
|
362
|
+
# close:收盘价
|
363
|
+
# high:最高价
|
364
|
+
# low:最低价
|
365
|
+
# volume:成交量(手)
|
366
|
+
# amount:成交金额(亿元)
|
367
|
+
def get_index
|
368
|
+
url = format(INDEX_HQ_URL, P_TYPE['http'], DOMAINS['sinahq'])
|
369
|
+
resp = HTTParty.get(url)
|
370
|
+
string = resp.body.encode('utf-8', 'gbk')
|
371
|
+
.delete('var hq_str_sh')
|
372
|
+
.delete('var hq_str_sz')
|
373
|
+
.delete('";')
|
374
|
+
.delete('"')
|
375
|
+
.tr!('=', ',')
|
376
|
+
csv = CSV.new(string)
|
377
|
+
result = []
|
378
|
+
headers = INDEX_HEADER.split(',')
|
379
|
+
csv.each do |row|
|
380
|
+
object = {}
|
381
|
+
row.each_with_index do |value, index|
|
382
|
+
next unless INDEX_COLS.include? headers[index]
|
383
|
+
object[headers[index]] = value
|
384
|
+
end
|
385
|
+
object['change'] = ((object['close'].to_f / object['preclose'].to_f - 1) * 100).round(2)
|
386
|
+
object['amount'] = (object['amount'].to_f / 100_000_000).round(4)
|
387
|
+
result << object
|
388
|
+
end
|
389
|
+
result
|
390
|
+
end
|
391
|
+
|
392
|
+
def get_hists(symbols, start_date: '', end_date: '', ktype: 'D')
|
393
|
+
if symbols.respond_to? :each
|
394
|
+
result = []
|
395
|
+
symbols.each do |symbol|
|
396
|
+
data = get_hist_data(symbol, start_date: start_date,
|
397
|
+
end_date: end_date,
|
398
|
+
ktype: ktype)
|
399
|
+
data.map { |object| object['code'] = symbol }
|
400
|
+
result.concat data
|
401
|
+
end
|
402
|
+
result
|
403
|
+
else
|
404
|
+
nil
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
protected
|
409
|
+
|
410
|
+
def _parse_fq_factor(code, start_date, end_date)
|
411
|
+
symbol = _code_to_symbol(code)
|
412
|
+
url = format(HIST_FQ_FACTOR_URL, P_TYPE['http'], DOMAINS['vsf'],
|
413
|
+
symbol)
|
414
|
+
resp = HTTParty.get(url)
|
415
|
+
string = resp.body[1..-2].gsub('{_', '{"')
|
416
|
+
.gsub!('total', '"total"')
|
417
|
+
.gsub!('data', '"data"')
|
418
|
+
.gsub!(':"', '":"')
|
419
|
+
.gsub!('",_', '","')
|
420
|
+
.tr!('_', '-')
|
421
|
+
json = JSON.parse(string)
|
422
|
+
result = []
|
423
|
+
json['data'].each_pair do |key, value|
|
424
|
+
result << { 'date' => key, 'factor' => value.to_f }
|
425
|
+
end
|
426
|
+
result.uniq { |object| object['date'] }
|
427
|
+
end
|
428
|
+
|
429
|
+
def _parse_fq_data(url, index)
|
430
|
+
html = Nokogiri::HTML(open(url), nil, 'gbk')
|
431
|
+
columns = index ? HIST_FQ_COLS[0..7] : HIST_FQ_COLS
|
432
|
+
result = []
|
433
|
+
html.css('table#FundHoldSharesTable tr').drop(2).each do |tr|
|
434
|
+
object = {}
|
435
|
+
tr.css('td').each_with_index do |td, i|
|
436
|
+
next if columns[i].nil?
|
437
|
+
object[columns[i]] = td.text.strip
|
438
|
+
end
|
439
|
+
result << object
|
440
|
+
end
|
441
|
+
result
|
442
|
+
end
|
443
|
+
|
444
|
+
def _get_index_url(index, code, qt)
|
445
|
+
if index
|
446
|
+
format(HIST_INDEX_URL, P_TYPE['http'], DOMAINS['vsf'], code, qt[0],
|
447
|
+
qt[1])
|
448
|
+
else
|
449
|
+
format(HIST_FQ_URL, P_TYPE['http'], DOMAINS['vsf'], code, qt[0],
|
450
|
+
qt[1])
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def _today_ticks(symbol, tdate, pageNo, ticks)
|
455
|
+
url = format(TODAY_TICKS_URL, P_TYPE['http'], DOMAINS['vsf'], PAGES['t_ticks'], symbol, tdate, pageNo)
|
456
|
+
doc = Nokogiri::HTML(open(url), nil, 'gbk')
|
457
|
+
doc.css('table#datatbl > tbody > tr').each do |tr|
|
458
|
+
items = []
|
459
|
+
tr.children.each do |td|
|
460
|
+
items << td.content.gsub(/\s+/, '').gsub('--', '0').gsub('%', '')
|
461
|
+
end
|
462
|
+
ticks << Hash[TODAY_TICK_COLUMNS.zip(items)] unless items.empty?
|
463
|
+
end
|
464
|
+
end
|
465
|
+
# '''
|
466
|
+
# 处理当日行情分页数据,格式为json
|
467
|
+
# Parameters
|
468
|
+
# ------
|
469
|
+
# pageNum:页码
|
470
|
+
# return
|
471
|
+
# -------
|
472
|
+
# DataFrame 当日所有股票交易数据(DataFrame)
|
473
|
+
# '''
|
474
|
+
def _parsing_dayprice_json(pageNum = 1)
|
475
|
+
_write_console
|
476
|
+
url = format(SINA_DAY_PRICE_URL, P_TYPE['http'], DOMAINS['vsf'], PAGES['jv'], pageNum)
|
477
|
+
|
478
|
+
resp = HTTParty.get(url)
|
479
|
+
if resp.code.to_s == '200'
|
480
|
+
val = resp.body.encode('utf-8', 'gbk')
|
481
|
+
return nil if val == 'null'
|
482
|
+
hs = eval(val)
|
483
|
+
cols = DAY_TRADING_COLUMNS.clone
|
484
|
+
cols.delete('symbol')
|
485
|
+
hs.map { |x| t = {}; cols.each { |c| t[c.to_sym] = x[c.to_sym] }; t }
|
486
|
+
|
487
|
+
else
|
488
|
+
nil
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def code_to_symbol(code)
|
493
|
+
return INDEX_LIST[code] if INDEX_LABELS.include? code
|
494
|
+
return '' if code.length != 6
|
495
|
+
return "sh#{code}" if %w(5 6 9).include? code[0]
|
496
|
+
return "sz#{code}"
|
497
|
+
end
|
498
|
+
|
499
|
+
def _random(n = 13)
|
500
|
+
start_int = 10 ** (n - 1)
|
501
|
+
end_int = (10 ** n) -1
|
502
|
+
rand(start_int..end_int).to_s
|
503
|
+
end
|
504
|
+
|
505
|
+
module_function :get_hist_data, :get_tick_data, :get_sina_dd,
|
506
|
+
:get_today_ticks, :get_today_all, :_today_ticks,
|
507
|
+
:_parsing_dayprice_json, :get_realtime_quotes,
|
508
|
+
:_random, :code_to_symbol, :get_h_data, :_parse_fq_factor,
|
509
|
+
:_get_index_url, :_parse_fq_data, :get_index, :get_hists
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
end
|