clickclient_scrap 0.1.7
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.
- data/ChangeLog +0 -0
- data/README +28 -0
- data/lib/clickclient_scrap.rb +672 -0
- data/lib/jiji_plugin.rb +133 -0
- data/test/test_FxSession.rb +38 -0
- data/test/test_jiji_plugin.rb +77 -0
- metadata +71 -0
data/ChangeLog
ADDED
File without changes
|
data/README
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
==クリック証券Fxクライアントライブラリ
|
3
|
+
|
4
|
+
クリック証券の携帯向けサイト「モバトレ君」にアクセスし、
|
5
|
+
スクレイピングでレート情報の取得や取り引きを行うライブラリです。
|
6
|
+
|
7
|
+
===実装済みの機能
|
8
|
+
- ログイン
|
9
|
+
- ログアウト
|
10
|
+
- レート一覧の取得
|
11
|
+
- 注文一覧の取得
|
12
|
+
- 建玉一覧の取得
|
13
|
+
- 注文
|
14
|
+
-- 成行
|
15
|
+
-- 指値
|
16
|
+
-- OCO
|
17
|
+
- 注文のキャンセル
|
18
|
+
- 決済
|
19
|
+
-- 成行
|
20
|
+
- 余力情報取得
|
21
|
+
|
22
|
+
===注意事項
|
23
|
+
- スクレイピングによるアクセスのため、Webサイトの仕様変更等により
|
24
|
+
突然動作しなくなる可能性があります。
|
25
|
+
|
26
|
+
===免責
|
27
|
+
- 本ライブラリの利用は自己責任でお願いします。
|
28
|
+
- ライブラリの不備・不具合等によるあらゆる損害について、作成者は責任を負いません。
|
@@ -0,0 +1,672 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
require 'mechanize'
|
6
|
+
require 'date'
|
7
|
+
require 'kconv'
|
8
|
+
require 'set'
|
9
|
+
|
10
|
+
#
|
11
|
+
#=== クリック証券アクセスクライアント
|
12
|
+
#
|
13
|
+
#*Version*:: -
|
14
|
+
#*License*:: Ruby ライセンスに準拠
|
15
|
+
#
|
16
|
+
#クリック証券を利用するためのクライアントライブラリです。携帯向けサイトのスクレイピングにより以下の機能を提供します。
|
17
|
+
#- 外為証拠金取引(FX)取引
|
18
|
+
#
|
19
|
+
#====基本的な使い方
|
20
|
+
#
|
21
|
+
# require 'clickclient_scrap'
|
22
|
+
#
|
23
|
+
# c = ClickClient::Client.new
|
24
|
+
# # c = ClickClientScrap::Client.new https://<プロキシホスト>:<プロキシポート> # プロキシを利用する場合
|
25
|
+
# c.fx_session( "<ユーザー名>", "<パスワード>" ) { | fx_session |
|
26
|
+
# # 通貨ペア一覧取得
|
27
|
+
# list = fx_session.list_rates
|
28
|
+
# puts list
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
#====免責
|
32
|
+
#- 本ライブラリの利用は自己責任でお願いします。
|
33
|
+
#- ライブラリの不備・不具合等によるあらゆる損害について、作成者は責任を負いません。
|
34
|
+
#
|
35
|
+
module ClickClientScrap
|
36
|
+
|
37
|
+
# クライアント
|
38
|
+
class Client
|
39
|
+
# ホスト名
|
40
|
+
DEFAULT_HOST_NAME = "https://sec-sso.click-sec.com/mf/"
|
41
|
+
|
42
|
+
#
|
43
|
+
#===コンストラクタ
|
44
|
+
#
|
45
|
+
#*proxy*:: プロキシホストを利用する場合、そのホスト名とパスを指定します。
|
46
|
+
# 例) https://proxyhost.com:80
|
47
|
+
#
|
48
|
+
def initialize( proxy=nil )
|
49
|
+
@client = WWW::Mechanize.new {|c|
|
50
|
+
# プロキシ
|
51
|
+
if proxy
|
52
|
+
uri = URI.parse( proxy )
|
53
|
+
c.set_proxy( uri.host, uri.port )
|
54
|
+
end
|
55
|
+
}
|
56
|
+
@client.keep_alive = false
|
57
|
+
@client.max_history=0
|
58
|
+
@client.user_agent_alias = 'Windows IE 7'
|
59
|
+
@host_name = DEFAULT_HOST_NAME
|
60
|
+
end
|
61
|
+
|
62
|
+
#ログインし、セッションを開始します。
|
63
|
+
#-ブロックを指定した場合、引数としてセッションを指定してブロックを実行します。ブロック実行後、ログアウトします。
|
64
|
+
#-そうでない場合、セッションを返却します。この場合、ClickClientScrap::FX::FxSession#logoutを実行しログアウトしてください。
|
65
|
+
#
|
66
|
+
#userid:: ユーザーID
|
67
|
+
#password:: パスワード
|
68
|
+
#options:: オプション
|
69
|
+
#戻り値:: ClickClientScrap::FX::FxSession
|
70
|
+
def fx_session( userid, password, options={}, &block )
|
71
|
+
page = @client.get(@host_name)
|
72
|
+
ClickClientScrap::Client.error(page) if page.forms.length <= 0
|
73
|
+
form = page.forms.first
|
74
|
+
form.j_username = userid
|
75
|
+
form.j_password = password
|
76
|
+
result = @client.submit(form, form.buttons.first)
|
77
|
+
if result.body.toutf8 =~ /<META HTTP-EQUIV="REFRESH" CONTENT="0;URL=([^"]*)">/
|
78
|
+
result = @client.get($1)
|
79
|
+
ClickClientScrap::Client.error( result ) if result.links.size <= 0
|
80
|
+
session = FX::FxSession.new( @client, result.links, options )
|
81
|
+
if block_given?
|
82
|
+
begin
|
83
|
+
yield session
|
84
|
+
ensure
|
85
|
+
session.logout
|
86
|
+
end
|
87
|
+
else
|
88
|
+
return session
|
89
|
+
end
|
90
|
+
else
|
91
|
+
ClickClientScrap::Client.error( result )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def self.error( page )
|
95
|
+
msgs = page.body.scan( /<font color="red">([^<]*)</ ).flatten
|
96
|
+
error = !msgs.empty? ? msgs.map{|m| m.strip}.join(",") : page.body
|
97
|
+
raise "operation failed.detail=#{error}".toutf8
|
98
|
+
end
|
99
|
+
|
100
|
+
#ホスト名
|
101
|
+
attr :host_name, true
|
102
|
+
end
|
103
|
+
|
104
|
+
module FX
|
105
|
+
|
106
|
+
# 通貨ペア: 米ドル-円
|
107
|
+
USDJPY = :USDJPY
|
108
|
+
# 通貨ペア: ユーロ-円
|
109
|
+
EURJPY = :EURJPY
|
110
|
+
# 通貨ペア: イギリスポンド-円
|
111
|
+
GBPJPY = :GBPJPY
|
112
|
+
# 通貨ペア: 豪ドル-円
|
113
|
+
AUDJPY = :AUDJPY
|
114
|
+
# 通貨ペア: ニュージーランドドル-円
|
115
|
+
NZDJPY = :NZDJPY
|
116
|
+
# 通貨ペア: カナダドル-円
|
117
|
+
CADJPY = :CADJPY
|
118
|
+
# 通貨ペア: スイスフラン-円
|
119
|
+
CHFJPY = :CHFJPY
|
120
|
+
# 通貨ペア: 南アランド-円
|
121
|
+
ZARJPY = :ZARJPY
|
122
|
+
# 通貨ペア: ユーロ-米ドル
|
123
|
+
EURUSD = :EURUSD
|
124
|
+
# 通貨ペア: イギリスポンド-米ドル
|
125
|
+
GBPUSD = :GBPUSD
|
126
|
+
# 通貨ペア: 豪ドル-米ドル
|
127
|
+
AUDUSD = :AUDUSD
|
128
|
+
# 通貨ペア: ユーロ-スイスフラン
|
129
|
+
EURCHF = :EURCHF
|
130
|
+
# 通貨ペア: イギリスポンド-スイスフラン
|
131
|
+
GBPCHF = :GBPCHF
|
132
|
+
# 通貨ペア: 米ドル-スイスフラン
|
133
|
+
USDCHF = :USDCHF
|
134
|
+
|
135
|
+
# 売買区分: 買い
|
136
|
+
BUY = 0
|
137
|
+
# 売買区分: 売り
|
138
|
+
SELL = 1
|
139
|
+
|
140
|
+
# 注文タイプ: 通常
|
141
|
+
ORDER_TYPE_MARKET_ORDER = "00"
|
142
|
+
# 注文タイプ: 通常
|
143
|
+
ORDER_TYPE_NORMAL = "01"
|
144
|
+
# 注文タイプ: IFD
|
145
|
+
ORDER_TYPE_IFD = "11"
|
146
|
+
# 注文タイプ: OCO
|
147
|
+
ORDER_TYPE_OCO = "21"
|
148
|
+
# 注文タイプ: IFD-OCO
|
149
|
+
ORDER_TYPE_IFD_OCO = "31"
|
150
|
+
|
151
|
+
# 有効期限: 当日限り
|
152
|
+
EXPIRATION_TYPE_TODAY = 0
|
153
|
+
# 有効期限: 週末まで
|
154
|
+
EXPIRATION_TYPE_WEEK_END = 1
|
155
|
+
# 有効期限: 無期限
|
156
|
+
EXPIRATION_TYPE_INFINITY = 2
|
157
|
+
# 有効期限: 日付指定
|
158
|
+
EXPIRATION_TYPE_SPECIFIED = 3
|
159
|
+
|
160
|
+
# 注文状況: すべて
|
161
|
+
ORDER_CONDITION_ALL = ""
|
162
|
+
# 注文状況: 注文中
|
163
|
+
ORDER_CONDITION_ON_ORDER = "0"
|
164
|
+
# 注文状況: 取消済
|
165
|
+
ORDER_CONDITION_CANCELED = "1"
|
166
|
+
# 注文状況: 約定
|
167
|
+
ORDER_CONDITION_EXECUTION = "2"
|
168
|
+
# 注文状況: 不成立
|
169
|
+
ORDER_CONDITION_FAILED = "3"
|
170
|
+
|
171
|
+
# トレード種別: 新規
|
172
|
+
TRADE_TYPE_NEW = "新規"
|
173
|
+
# トレード種別: 決済
|
174
|
+
TRADE_TYPE_SETTLEMENT = "決済"
|
175
|
+
|
176
|
+
# 執行条件: 成行
|
177
|
+
EXECUTION_EXPRESSION_MARKET_ORDER = "成行"
|
178
|
+
# 執行条件: 指値
|
179
|
+
EXECUTION_EXPRESSION_LIMIT_ORDER = "指値"
|
180
|
+
# 執行条件: 逆指値
|
181
|
+
EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER = "逆指値"
|
182
|
+
|
183
|
+
#=== FX取引のためのセッションクラス
|
184
|
+
#Client#fx_sessionのブロックの引数として渡されます。詳細はClient#fx_sessionを参照ください。
|
185
|
+
class FxSession
|
186
|
+
|
187
|
+
def initialize( client, links, options={} )
|
188
|
+
@client = client
|
189
|
+
@links = links
|
190
|
+
@options = options
|
191
|
+
end
|
192
|
+
|
193
|
+
#レート一覧を取得します。
|
194
|
+
#
|
195
|
+
#戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Rateのハッシュ。
|
196
|
+
def list_rates
|
197
|
+
result = link_click( "1" )
|
198
|
+
if !@last_update_time_of_swaps \
|
199
|
+
|| Time.now.to_i - @last_update_time_of_swaps > (@options[:swap_update_interval] || 60*60)
|
200
|
+
@swaps = list_swaps
|
201
|
+
@last_update_time_of_swaps = Time.now.to_i
|
202
|
+
end
|
203
|
+
reg = />([A-Z]+\/[A-Z]+)<\/a>[^\-\.\d]*?([\d]+\.[\d]+\-[\d]+)/
|
204
|
+
tokens = result.body.toutf8.scan( reg )
|
205
|
+
ClickClientScrap::Client.error( result ) if !tokens || tokens.empty?
|
206
|
+
return tokens.inject({}) {|r,l|
|
207
|
+
pair = to_pair( l[0] )
|
208
|
+
swap = @swaps[pair]
|
209
|
+
rate = FxSession.convert_rate l[1]
|
210
|
+
if ( rate && swap )
|
211
|
+
r[pair] = Rate.new( pair, rate[0], rate[1], swap.sell_swap, swap.buy_swap )
|
212
|
+
end
|
213
|
+
r
|
214
|
+
}
|
215
|
+
end
|
216
|
+
#12.34-35 形式の文字列をbidレート、askレートに変換する。
|
217
|
+
def self.convert_rate( str ) #:nodoc:
|
218
|
+
if str =~ /([\d]+)\.([\d]+)\-([\d]+)/
|
219
|
+
high = $1
|
220
|
+
low = $2
|
221
|
+
low2 = $3
|
222
|
+
bid = high.to_f+(low.to_f/(10**low.length))
|
223
|
+
ask_low = (low[0...low.length-low2.length] + low2).to_f
|
224
|
+
if low.to_f > ask_low
|
225
|
+
ask_low += 10**low2.length
|
226
|
+
end
|
227
|
+
ask = high.to_f+(ask_low/10**low.length)
|
228
|
+
return [bid,ask]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
#スワップの一覧を取得します。
|
233
|
+
#
|
234
|
+
#戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Swapのハッシュ。
|
235
|
+
def list_swaps
|
236
|
+
result = link_click( "8" )
|
237
|
+
reg = /<dd>([A-Z]+\/[A-Z]+) <font[^>]*>売<\/font>[^\-\d]*?([\-\d]+)[^\-\d]*<font[^>]*>買<\/font>[^\-\d]*([\-\d]+)[^\-\d]*<\/dd>/
|
238
|
+
return result.body.toutf8.scan( reg ).inject({}) {|r,l|
|
239
|
+
pair = to_pair( l[0] )
|
240
|
+
r[pair] = Swap.new( pair, l[1].to_i, l[2].to_i ); r
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
#
|
245
|
+
#注文を行います。
|
246
|
+
#
|
247
|
+
#currency_pair_code:: 通貨ペアコード(必須)
|
248
|
+
#sell_or_buy:: 売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
|
249
|
+
#unit:: 取引数量(必須)
|
250
|
+
#options:: 注文のオプション。注文方法に応じて以下の情報を設定できます。
|
251
|
+
# - <b>成り行き注文</b>
|
252
|
+
# - <tt>:slippage</tt> .. スリッページ (オプション)。何pips以内かを整数で指定します。
|
253
|
+
# - <b>通常注文</b> ※注文レートが設定されていれば通常取引となります。
|
254
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
255
|
+
# - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
|
256
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
257
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
258
|
+
# - <b>OCO注文</b> ※逆指値レートが設定されていればOCO取引となります。
|
259
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
260
|
+
# - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
|
261
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
262
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
263
|
+
# - <b>IFD注文</b> ※決済取引の指定があればIFD取引となります。
|
264
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
265
|
+
# - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
|
266
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
267
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
268
|
+
# - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
|
269
|
+
# - <tt>:unit</tt> .. 決済取引の取引数量(必須)
|
270
|
+
# - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
|
271
|
+
# - <tt>:rate</tt> .. 決済取引の注文レート(必須)
|
272
|
+
# - <tt>:execution_expression</tt> .. 決済取引の執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
|
273
|
+
# - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
274
|
+
# - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
275
|
+
# - <b>IFD-OCO注文</b> ※決済取引の指定と逆指値レートの指定があればIFD-OCO取引となります。
|
276
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
277
|
+
# - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
|
278
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
279
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
280
|
+
# - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
|
281
|
+
# - <tt>:unit</tt> .. 決済取引の取引数量(必須)
|
282
|
+
# - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
|
283
|
+
# - <tt>:rate</tt> .. 決済取引の注文レート(必須)
|
284
|
+
# - <tt>:stop_order_rate</tt> .. 決済取引の逆指値レート(必須)
|
285
|
+
# - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
286
|
+
# - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
287
|
+
#戻り値:: ClickClientScrap::FX::OrderResult
|
288
|
+
#
|
289
|
+
def order ( currency_pair_code, sell_or_buy, unit, options={} )
|
290
|
+
|
291
|
+
# 取り引き種別の判別とパラメータチェック
|
292
|
+
type = ORDER_TYPE_MARKET_ORDER
|
293
|
+
if ( options && options[:settle] != nil )
|
294
|
+
if ( options[:settle][:stop_order_rate] != nil)
|
295
|
+
# 逆指値レートと決済取引の指定があればIFD-OCO取引
|
296
|
+
raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
|
297
|
+
type = ORDER_TYPE_IFD_OCO
|
298
|
+
else
|
299
|
+
# 決済取引の指定のみがあればIFD取引
|
300
|
+
raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
|
301
|
+
raise "options[:settle][:execution_expression] is required." if options[:settle][:execution_expression] == nil
|
302
|
+
type = ORDER_TYPE_IFD
|
303
|
+
end
|
304
|
+
raise "options[:rate] is required." if options[:rate] == nil
|
305
|
+
raise "options[:execution_expression] is required." if options[:execution_expression] == nil
|
306
|
+
raise "options[:expiration_type] is required." if options[:expiration_type] == nil
|
307
|
+
raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
|
308
|
+
raise "options[:settle][:sell_or_buy] is required." if options[:settle][:sell_or_buy] == nil
|
309
|
+
raise "options[:settle][:unit] is required." if options[:settle][:unit] == nil
|
310
|
+
raise "options[:settle][:expiration_type] is required." if options[:expiration_type] == nil
|
311
|
+
elsif ( options && options[:rate] != nil )
|
312
|
+
if ( options[:stop_order_rate] != nil )
|
313
|
+
# 逆指値レートが指定されていればOCO取引
|
314
|
+
type = ORDER_TYPE_OCO
|
315
|
+
else
|
316
|
+
# そうでなければ通常取引
|
317
|
+
raise "options[:execution_expression] is required." if options[:execution_expression] == nil
|
318
|
+
type = ORDER_TYPE_NORMAL
|
319
|
+
end
|
320
|
+
raise "options[:expiration_type] is required." if options[:expiration_type] == nil
|
321
|
+
else
|
322
|
+
# 成り行き
|
323
|
+
type = ORDER_TYPE_MARKET_ORDER
|
324
|
+
end
|
325
|
+
|
326
|
+
#注文前の注文一覧
|
327
|
+
before = list_orders( ORDER_CONDITION_ON_ORDER ).inject(Set.new) {|s,o| s << o[0]; s }
|
328
|
+
|
329
|
+
# レート一覧
|
330
|
+
result = link_click( "1" )
|
331
|
+
|
332
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
333
|
+
form = result.forms.first
|
334
|
+
|
335
|
+
# 通貨ペア
|
336
|
+
option = form.fields.find{|f| f.name == "P001" }.options.find {|o|
|
337
|
+
to_pair( o.text.strip ) == currency_pair_code
|
338
|
+
}
|
339
|
+
raise "illegal currency_pair_code. currency_pair_code=#{currency_pair_code.to_s}" unless option
|
340
|
+
option.select
|
341
|
+
|
342
|
+
#注文方式
|
343
|
+
form["P100"] = type
|
344
|
+
|
345
|
+
# 詳細設定画面へ
|
346
|
+
result = @client.submit(form)
|
347
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
348
|
+
form = result.forms.first
|
349
|
+
case type
|
350
|
+
when ORDER_TYPE_MARKET_ORDER
|
351
|
+
# 成り行き
|
352
|
+
form["P002"] = unit.to_s # 取り引き数量
|
353
|
+
form["P005.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
|
354
|
+
form["P007"] = options[:slippage].to_s if ( options && options[:slippage] != nil ) # スリッページ
|
355
|
+
when ORDER_TYPE_NORMAL
|
356
|
+
# 指値
|
357
|
+
form["P003"] = options[:rate].to_s # レート
|
358
|
+
form["P005"] = unit.to_s # 取り引き数量
|
359
|
+
form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
|
360
|
+
exp = options[:execution_expression]
|
361
|
+
form["P004.0"] = exp == ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER ? "2" : "1" #指値/逆指値
|
362
|
+
set_expiration( form, options, "P008", "P009" ) # 有効期限
|
363
|
+
when ORDER_TYPE_OCO
|
364
|
+
# OCO
|
365
|
+
form["P003"] = options[:rate].to_s # レート
|
366
|
+
form["P005"] = options[:stop_order_rate].to_s # 逆指値レート
|
367
|
+
form["P007"] = unit.to_s # 取り引き数量
|
368
|
+
form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
|
369
|
+
set_expiration( form, options, "P010", "P011" ) # 有効期限
|
370
|
+
else
|
371
|
+
raise "not supported yet."
|
372
|
+
end
|
373
|
+
|
374
|
+
# 確認画面へ
|
375
|
+
result = @client.submit(form)
|
376
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
377
|
+
result = @client.submit(result.forms.first)
|
378
|
+
ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文受付完了/
|
379
|
+
|
380
|
+
#注文前の一覧と注文後の一覧を比較して注文を割り出す。
|
381
|
+
#成り行き注文の場合、即座に約定するのでnilになる(タイミングによっては取得できるかも)
|
382
|
+
tmp = list_orders( ORDER_CONDITION_ON_ORDER ).find {|o| !before.include?(o[0]) }
|
383
|
+
return OrderResult.new( tmp ? tmp[1].order_no : nil )
|
384
|
+
end
|
385
|
+
|
386
|
+
# 有効期限を設定する
|
387
|
+
#form:: フォーム
|
388
|
+
#options:: パラメータ
|
389
|
+
#input_type:: 有効期限の種別を入力するinput要素名
|
390
|
+
#input_date:: 有効期限が日付指定の場合に、日付を入力するinput要素名
|
391
|
+
def set_expiration( form, options, input_type, input_date )
|
392
|
+
case options[:expiration_type]
|
393
|
+
when ClickClientScrap::FX::EXPIRATION_TYPE_TODAY
|
394
|
+
form[input_type] = "0"
|
395
|
+
when ClickClientScrap::FX::EXPIRATION_TYPE_WEEK_END
|
396
|
+
form[input_type] = "1"
|
397
|
+
when ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED
|
398
|
+
form[input_type] = "3"
|
399
|
+
raise "options[:expiration_date] is required." unless options[:expiration_date]
|
400
|
+
form["#{input_date}.Y"] = options[:expiration_date].year
|
401
|
+
form["#{input_date}.M"] = options[:expiration_date].month
|
402
|
+
form["#{input_date}.D"] = options[:expiration_date].day
|
403
|
+
form["#{input_date}.h"] = options[:expiration_date].respond_to?(:hour) ? options[:expiration_date].hour : "0"
|
404
|
+
else
|
405
|
+
form[input_type] = "2"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
#
|
410
|
+
#=== 注文をキャンセルします。
|
411
|
+
#
|
412
|
+
#order_no:: 注文番号
|
413
|
+
#戻り値:: なし
|
414
|
+
#
|
415
|
+
def cancel_order( order_no )
|
416
|
+
|
417
|
+
raise "order_no is nil." unless order_no
|
418
|
+
|
419
|
+
# 注文一覧
|
420
|
+
result = link_click( "2" )
|
421
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
422
|
+
form = result.forms.first
|
423
|
+
form["P002"] = ORDER_CONDITION_ON_ORDER
|
424
|
+
result = @client.submit(form)
|
425
|
+
|
426
|
+
# 対象となる注文をクリック
|
427
|
+
link = result.links.find {|l|
|
428
|
+
l.href =~ /[^"]*GKEY=([a-zA-Z0-9]*)[^"]*/ && $1 == order_no
|
429
|
+
}
|
430
|
+
raise "illegal order_no. order_no=#{order_no}" unless link
|
431
|
+
result = @client.click(link)
|
432
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
433
|
+
|
434
|
+
# キャンセル
|
435
|
+
form = result.forms[1]
|
436
|
+
result = @client.submit(form)
|
437
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
438
|
+
form = result.forms.first
|
439
|
+
result = @client.submit(form)
|
440
|
+
ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文取消受付完了/
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
#
|
445
|
+
#=== 決済注文を行います。
|
446
|
+
#
|
447
|
+
#*open_interest_id*:: 決済する建玉番号
|
448
|
+
#*unit*:: 取引数量
|
449
|
+
#*options*:: 決済注文のオプション。注文方法に応じて以下の情報を設定できます。
|
450
|
+
# - <b>成り行き注文</b>
|
451
|
+
# - <tt>:slippage</tt> .. スリッページ (オプション)
|
452
|
+
# - <tt>:slippage_base_rate</tt> .. スリッページの基準となる取引レート(スリッページが指定された場合、必須。)
|
453
|
+
# - <b>通常注文</b> <b>※未実装</b> ※注文レートが設定されていれば通常取引となります。
|
454
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
455
|
+
# - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
|
456
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
457
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
458
|
+
# - <b>OCO注文</b> <b>※未実装</b> ※注文レートと逆指値レートが設定されていればOCO取引となります。
|
459
|
+
# - <tt>:rate</tt> .. 注文レート(必須)
|
460
|
+
# - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
|
461
|
+
# - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
|
462
|
+
# - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
|
463
|
+
#<b>戻り値</b>:: なし
|
464
|
+
#
|
465
|
+
def settle ( open_interest_id, unit, options={} )
|
466
|
+
if ( options[:rate] != nil && options[:stop_order_rate] != nil )
|
467
|
+
# レートと逆指値レートが指定されていればOCO取引
|
468
|
+
raise "options[:expiration_type] is required." if options[:expiration_type] == nil
|
469
|
+
elsif ( options[:rate] != nil )
|
470
|
+
# レートが指定されていれば通常取引
|
471
|
+
raise "options[:execution_expression] is required." if options[:execution_expression] == nil
|
472
|
+
raise "options[:expiration_type] is required." if options[:expiration_type] == nil
|
473
|
+
else
|
474
|
+
# 成り行き
|
475
|
+
if ( options[:slippage] != nil )
|
476
|
+
raise "if you use a slippage, options[:slippage_base_rate] is required." if options[:slippage_base_rate] == nil
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# 建玉一覧
|
481
|
+
result = link_click( "3" )
|
482
|
+
|
483
|
+
# 対象となる建玉をクリック
|
484
|
+
link = result.links.find {|l|
|
485
|
+
l.href =~ /[^"]*ORDERNO=([a-zA-Z0-9]*)[^"]*/ && $1 == open_interest_id
|
486
|
+
}
|
487
|
+
raise "illegal open_interest_id. open_interest_id=#{open_interest_id}" unless link
|
488
|
+
result = @client.click(link)
|
489
|
+
|
490
|
+
# 決済
|
491
|
+
form = result.forms.first
|
492
|
+
form["P100"] = "00" # 成り行き TODO 通常(01),OCO取引(21)対応
|
493
|
+
result = @client.submit(form)
|
494
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
495
|
+
|
496
|
+
# 設定
|
497
|
+
form = result.forms.first
|
498
|
+
form["L111"] = unit.to_s
|
499
|
+
form["P005"] = options[:slippage].to_s if options[:slippage]
|
500
|
+
result = @client.submit(form)
|
501
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
502
|
+
|
503
|
+
# 確認
|
504
|
+
form = result.forms.first
|
505
|
+
result = @client.submit(form)
|
506
|
+
ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /完了/
|
507
|
+
end
|
508
|
+
|
509
|
+
|
510
|
+
#
|
511
|
+
#=== 注文一覧を取得します。
|
512
|
+
#
|
513
|
+
#order_condition_code:: 注文状況コード(必須)
|
514
|
+
#currency_pair_code:: 通貨ペアコード <b>※未実装</b>
|
515
|
+
#戻り値:: 注文番号をキーとするClickClientScrap::FX::Orderのハッシュ。
|
516
|
+
#
|
517
|
+
def list_orders( order_condition_code=ClickClientScrap::FX::ORDER_CONDITION_ALL, currency_pair_code=nil )
|
518
|
+
result = link_click( "2" )
|
519
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
520
|
+
form = result.forms.first
|
521
|
+
form["P001"] = "" # TODO currency_pair_codeでの絞り込み
|
522
|
+
form["P002"] = order_condition_code
|
523
|
+
result = @client.submit(form)
|
524
|
+
|
525
|
+
list = result.body.toutf8.scan( /<a href="[^"]*GKEY=([a-zA-Z0-9]*)">([A-Z]{3}\/[A-Z]{3}) ([^<]*)<\/a><br>[^;]*;([^<]*)<font[^>]*>([^<]*)<\/font>([^@]*)@([\d\.]*)([^\s]*) ([^<]*)<br>/m )
|
526
|
+
tmp = {}
|
527
|
+
list.each {|i|
|
528
|
+
order_no = i[0]
|
529
|
+
order_type = to_order_type_code(i[2])
|
530
|
+
trade_type = i[3] == "新" ? ClickClientScrap::FX::TRADE_TYPE_NEW : ClickClientScrap::FX::TRADE_TYPE_SETTLEMENT
|
531
|
+
pair = to_pair( i[1] )
|
532
|
+
sell_or_buy = i[4] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
|
533
|
+
count = pair == :ZARJPY ? i[5].to_i/10 : i[5].to_i
|
534
|
+
rate = i[6].to_f
|
535
|
+
execution_expression = if i[7] == "指"
|
536
|
+
ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER
|
537
|
+
elsif i[7] == "逆"
|
538
|
+
ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER
|
539
|
+
else
|
540
|
+
ClickClientScrap::FX::EXECUTION_EXPRESSION_MARKET_ORDER
|
541
|
+
end
|
542
|
+
tmp[order_no] = Order.new( order_no, trade_type, order_type, execution_expression, sell_or_buy, pair, count, rate, i[8])
|
543
|
+
}
|
544
|
+
return tmp
|
545
|
+
end
|
546
|
+
|
547
|
+
#
|
548
|
+
#=== 建玉一覧を取得します。
|
549
|
+
#
|
550
|
+
#currency_pair_code:: 通貨ペアコード。<b>※未実装</b>
|
551
|
+
#戻り値:: 建玉IDをキーとするClickClientScrap::FX::OpenInterestのハッシュ。
|
552
|
+
#
|
553
|
+
def list_open_interests( currency_pair_code=nil )
|
554
|
+
result = link_click( "3" )
|
555
|
+
ClickClientScrap::Client.error( result ) if result.forms.empty?
|
556
|
+
form = result.forms.first
|
557
|
+
form["P001"] = "" # TODO currency_pair_codeでの絞り込み
|
558
|
+
result = @client.submit(form)
|
559
|
+
|
560
|
+
list = result.body.toutf8.scan( /<a href="[^"]*">([A-Z]{3}\/[A-Z]{3}):([^<]*)<\/a><br>[^;]*;<font[^>]*>([^<]*)<\/font>([\d\.]*)[^\s@]*@([\d\.]*).*?<font[^>]*>([^<]*)<\/font>/m )
|
561
|
+
tmp = {}
|
562
|
+
list.each {|i|
|
563
|
+
open_interest_id = i[1]
|
564
|
+
pair = to_pair( i[0] )
|
565
|
+
sell_or_buy = i[2] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
|
566
|
+
count = i[3].to_i
|
567
|
+
rate = i[4].to_f
|
568
|
+
profit_or_loss = i[5].to_i
|
569
|
+
tmp[open_interest_id] = OpenInterest.new(open_interest_id, pair, sell_or_buy, count, rate, profit_or_loss )
|
570
|
+
}
|
571
|
+
return tmp
|
572
|
+
end
|
573
|
+
|
574
|
+
#
|
575
|
+
#=== 余力情報を取得します。
|
576
|
+
#
|
577
|
+
#戻り値:: ClickClientScrap::FX::Marginのハッシュ。
|
578
|
+
#
|
579
|
+
def get_margin
|
580
|
+
result = link_click( "7" )
|
581
|
+
list = result.body.toutf8.scan( /【([^<]*)[^>]*>[^>]*>([^<]*)</m )
|
582
|
+
values = list.inject({}) {|r,i|
|
583
|
+
if ( i[0] == "証拠金維持率】" )
|
584
|
+
r[i[0]] = i[1]
|
585
|
+
else
|
586
|
+
r[i[0]] = i[1].gsub(/,/, "").to_i
|
587
|
+
end
|
588
|
+
r
|
589
|
+
}
|
590
|
+
return Margin.new(
|
591
|
+
values["時価評価総額】"],
|
592
|
+
values["建玉評価損益】"],
|
593
|
+
values["口座残高】"],
|
594
|
+
values["証拠金維持率】"],
|
595
|
+
values["余力】"],
|
596
|
+
values["拘束証拠金】"],
|
597
|
+
values["必要証拠金】"],
|
598
|
+
values["注文中必要証拠金】"],
|
599
|
+
values["振替可能額】"]
|
600
|
+
)
|
601
|
+
end
|
602
|
+
|
603
|
+
# ログアウトします。
|
604
|
+
def logout
|
605
|
+
@client.click( @links.find {|i|
|
606
|
+
i.text == "\303\233\302\270\303\236\302\261\302\263\303\204"
|
607
|
+
})
|
608
|
+
end
|
609
|
+
|
610
|
+
private
|
611
|
+
# "USD/JPY"を:USDJPYのようなシンボルに変換します。
|
612
|
+
def to_pair( str )
|
613
|
+
str.gsub( /\//, "" ).to_sym
|
614
|
+
end
|
615
|
+
|
616
|
+
# 注文種別を注文種別コードに変換します。
|
617
|
+
def to_order_type_code( order_type )
|
618
|
+
return case order_type
|
619
|
+
when "成行注文"
|
620
|
+
ClickClientScrap::FX::ORDER_TYPE_MARKET_ORDER
|
621
|
+
when "通常注文"
|
622
|
+
ClickClientScrap::FX::ORDER_TYPE_NORMAL
|
623
|
+
when "OCO注文"
|
624
|
+
ClickClientScrap::FX::ORDER_TYPE_OCO
|
625
|
+
when "IFD注文"
|
626
|
+
ClickClientScrap::FX::ORDER_TYPE_IFD
|
627
|
+
when "IFD-OCO注文"
|
628
|
+
ClickClientScrap::FX::ORDER_TYPE_IFD_OCO
|
629
|
+
else
|
630
|
+
raise "illegal order_type. order_type=#{order_type}"
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
def link_click( no )
|
635
|
+
link = @links.find {|i|
|
636
|
+
i.attributes["accesskey"] == no
|
637
|
+
}
|
638
|
+
raise "link isnot found. accesskey=#{no}" unless link
|
639
|
+
@client.click( link )
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
# オプション
|
644
|
+
attr :options, true
|
645
|
+
|
646
|
+
#=== スワップ
|
647
|
+
Swap = Struct.new(:pair, :sell_swap, :buy_swap)
|
648
|
+
#=== レート
|
649
|
+
Rate = Struct.new(:pair, :bid_rate, :ask_rate, :sell_swap, :buy_swap )
|
650
|
+
#===注文
|
651
|
+
Order = Struct.new(:order_no, :trade_type, :order_type, :execution_expression, :sell_or_buy, :pair, :count, :rate, :order_state )
|
652
|
+
#===注文結果
|
653
|
+
OrderResult = Struct.new(:order_no )
|
654
|
+
#===建玉
|
655
|
+
OpenInterest = Struct.new(:open_interest_id, :pair, :sell_or_buy, :count, :rate, :profit_or_loss )
|
656
|
+
#===余力
|
657
|
+
Margin = Struct.new(
|
658
|
+
:market_value, #時価評価の総額
|
659
|
+
:appraisal_profit_or_loss_of_open_interest, #建玉の評価損益
|
660
|
+
:balance_in_account, # 口座残高
|
661
|
+
:guarantee_money_maintenance_ratio, #証拠金の維持率
|
662
|
+
:margin, #余力
|
663
|
+
:freezed_guarantee_money, #拘束されている証拠金
|
664
|
+
:required_guarantee_money, #必要な証拠金
|
665
|
+
:ordered_guarantee_money, #注文中の証拠金
|
666
|
+
:transferable_money_amount #振替可能額
|
667
|
+
)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
|
672
|
+
|
data/lib/jiji_plugin.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'jiji/plugin/securities_plugin'
|
4
|
+
require 'clickclient_scrap'
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
# クリック証券アクセスプラグイン
|
8
|
+
class ClickSecuritiesPlugin
|
9
|
+
include JIJI::Plugin::SecuritiesPlugin
|
10
|
+
|
11
|
+
#プラグインの識別子を返します。
|
12
|
+
def plugin_id
|
13
|
+
:click_securities
|
14
|
+
end
|
15
|
+
#プラグインの表示名を返します。
|
16
|
+
def display_name
|
17
|
+
"CLICK Securities"
|
18
|
+
end
|
19
|
+
#「jiji setting」でユーザーに入力を要求するデータの情報を返します。
|
20
|
+
def input_infos
|
21
|
+
[ Input.new( :user, "Please input a user name of CLICK Securities.", false, nil ),
|
22
|
+
Input.new( :password, "Please input a password of CLICK Securities.", true, nil ),
|
23
|
+
Input.new( :proxy, "Please input a proxy. example: http://example.com:80 (default: nil )", false, nil ) ]
|
24
|
+
end
|
25
|
+
|
26
|
+
#プラグインを初期化します。
|
27
|
+
def init_plugin( props, logger )
|
28
|
+
@session = ClickSecuritiesPluginSession.new( props, logger )
|
29
|
+
end
|
30
|
+
#プラグインを破棄します。
|
31
|
+
def destroy_plugin
|
32
|
+
@session.close
|
33
|
+
end
|
34
|
+
|
35
|
+
#利用可能な通貨ペア一覧を取得します。
|
36
|
+
def list_pairs
|
37
|
+
return ALL_PAIRS.map {|pair|
|
38
|
+
Pair.new( pair, pair == ClickClientScrap::FX::ZARJPY ? 100000 : 10000 )
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
#現在のレートを取得します。
|
43
|
+
def list_rates
|
44
|
+
@session.list_rates.inject({}) {|r,p|
|
45
|
+
r[p[0]] = Rate.new( p[1].bid_rate, p[1].ask_rate, p[1].sell_swap, p[1].buy_swap )
|
46
|
+
r
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
#成り行きで発注を行います。
|
51
|
+
def order( pair, sell_or_buy, count )
|
52
|
+
|
53
|
+
# 建玉一覧を取得
|
54
|
+
before = @session.list_open_interests.inject( Set.new ) {|s,i| s << i[0]; s }
|
55
|
+
# 発注
|
56
|
+
@session.order( pair, sell_or_buy == :buy ? ClickClientScrap::FX::BUY : ClickClientScrap::FX::SELL, count )
|
57
|
+
# 建玉を特定
|
58
|
+
position = nil
|
59
|
+
# 10s待っても取得できなければあきらめる
|
60
|
+
20.times {|i|
|
61
|
+
sleep 0.5
|
62
|
+
position = @session.list_open_interests.find {|i| !before.include?(i[0]) }
|
63
|
+
break if position
|
64
|
+
}
|
65
|
+
raise "order fialed." unless position
|
66
|
+
return JIJI::Plugin::SecuritiesPlugin::Position.new( position[1].open_interest_id )
|
67
|
+
end
|
68
|
+
|
69
|
+
#建玉を決済します。
|
70
|
+
def commit( position_id, count )
|
71
|
+
@session.settle( position_id, count )
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
ALL_PAIRS = [
|
77
|
+
ClickClientScrap::FX::USDJPY, ClickClientScrap::FX::EURJPY,
|
78
|
+
ClickClientScrap::FX::GBPJPY, ClickClientScrap::FX::AUDJPY,
|
79
|
+
ClickClientScrap::FX::NZDJPY, ClickClientScrap::FX::CADJPY,
|
80
|
+
ClickClientScrap::FX::CHFJPY, ClickClientScrap::FX::ZARJPY,
|
81
|
+
ClickClientScrap::FX::EURUSD, ClickClientScrap::FX::GBPUSD,
|
82
|
+
ClickClientScrap::FX::AUDUSD, ClickClientScrap::FX::EURCHF,
|
83
|
+
ClickClientScrap::FX::GBPCHF, ClickClientScrap::FX::USDCHF
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
class ClickSecuritiesPluginSession
|
88
|
+
def initialize( props, logger )
|
89
|
+
@props = props
|
90
|
+
@logger = logger
|
91
|
+
@m = Mutex.new
|
92
|
+
end
|
93
|
+
def method_missing( name, *args )
|
94
|
+
@m.synchronize {
|
95
|
+
begin
|
96
|
+
session.send( name, *args )
|
97
|
+
rescue
|
98
|
+
# エラーになった場合はセッションを再作成する
|
99
|
+
close
|
100
|
+
raise $!
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
104
|
+
def close
|
105
|
+
begin
|
106
|
+
@session.logout if @session
|
107
|
+
rescue
|
108
|
+
@logger.error $!
|
109
|
+
ensure
|
110
|
+
@session = nil
|
111
|
+
@client = nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
def session
|
115
|
+
begin
|
116
|
+
proxy = nil
|
117
|
+
if @props.key?(:proxy) && @props[:proxy] != nil && @props[:proxy].length > 0
|
118
|
+
proxy = @props[:proxy]
|
119
|
+
end
|
120
|
+
@client ||= ClickClientScrap::Client.new( proxy )
|
121
|
+
@session ||= @client.fx_session( @props[:user], @props[:password] )
|
122
|
+
rescue
|
123
|
+
@logger.error $!
|
124
|
+
raise $!
|
125
|
+
end
|
126
|
+
@session
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
JIJI::Plugin.register(
|
131
|
+
JIJI::Plugin::SecuritiesPlugin::FUTURE_NAME,
|
132
|
+
ClickSecuritiesPlugin.new )
|
133
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$: << "../lib"
|
4
|
+
|
5
|
+
require "runit/testcase"
|
6
|
+
require "runit/cui/testrunner"
|
7
|
+
require 'clickclient_scrap'
|
8
|
+
|
9
|
+
class FxSessionTest < RUNIT::TestCase
|
10
|
+
|
11
|
+
#convert_rate のテスト
|
12
|
+
def test_convert_rate
|
13
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.34-37").map {|i| i.to_s }
|
14
|
+
assert_equals rate, [ "123.34", "123.37" ]
|
15
|
+
|
16
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.534-37").map {|i| i.to_s }
|
17
|
+
assert_equals rate, [ "123.534", "123.537" ]
|
18
|
+
|
19
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.594-02").map {|i| i.to_s }
|
20
|
+
assert_equals rate, [ "123.594", "123.602" ]
|
21
|
+
|
22
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.00-02").map {|i| i.to_s }
|
23
|
+
assert_equals rate, [ "123.0", "123.02" ]
|
24
|
+
|
25
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.34-33").map {|i| i.to_s }
|
26
|
+
assert_equals rate, [ "123.34", "124.33" ]
|
27
|
+
|
28
|
+
rate = ClickClient::FX::FxSession.convert_rate("123.34-34").map {|i| i.to_s }
|
29
|
+
assert_equals rate, [ "123.34", "123.34" ]
|
30
|
+
|
31
|
+
rate = ClickClient::FX::FxSession.convert_rate("0.334-335").map {|i| i.to_s }
|
32
|
+
assert_equals rate, [ "0.334", "0.335" ]
|
33
|
+
|
34
|
+
rate = ClickClient::FX::FxSession.convert_rate("0.334-333").map {|i| i.to_s }
|
35
|
+
assert_equals rate, [ "0.334", "1.333" ]
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$: << "../lib"
|
4
|
+
|
5
|
+
require "runit/testcase"
|
6
|
+
require "logger"
|
7
|
+
require "runit/cui/testrunner"
|
8
|
+
require 'clickclient_scrap'
|
9
|
+
require 'jiji/plugin/plugin_loader'
|
10
|
+
require 'jiji/plugin/securities_plugin'
|
11
|
+
|
12
|
+
# jijiプラグインのテスト
|
13
|
+
# ※実際に取引を行うので注意!
|
14
|
+
class JIJIPluginTest < RUNIT::TestCase
|
15
|
+
|
16
|
+
def setup
|
17
|
+
@logger = Logger.new STDOUT
|
18
|
+
@user = IO.read( "../sample/user" )
|
19
|
+
@pass = IO.read( "../sample/pass" )
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_basic
|
23
|
+
# ロード
|
24
|
+
JIJI::Plugin::Loader.new.load
|
25
|
+
plugins = JIJI::Plugin.get( JIJI::Plugin::SecuritiesPlugin::FUTURE_NAME )
|
26
|
+
plugin = plugins.find {|i| i.plugin_id == :click_securities }
|
27
|
+
assert_not_nil plugin
|
28
|
+
assert_equals plugin.display_name, "CLICK Securities"
|
29
|
+
|
30
|
+
begin
|
31
|
+
plugin.init_plugin( {:user=>@user, :password=>@pass}, @logger )
|
32
|
+
|
33
|
+
# 利用可能な通貨ペア一覧とレート
|
34
|
+
pairs = plugin.list_pairs
|
35
|
+
rates = plugin.list_rates
|
36
|
+
pairs.each {|p|
|
37
|
+
# 利用可能とされたペアのレートが取得できていることを確認
|
38
|
+
assert_not_nil p.name
|
39
|
+
assert_not_nil p.trade_unit
|
40
|
+
assert_not_nil rates[p.name]
|
41
|
+
assert_not_nil rates[p.name].bid
|
42
|
+
assert_not_nil rates[p.name].ask
|
43
|
+
assert_not_nil rates[p.name].sell_swap
|
44
|
+
assert_not_nil rates[p.name].buy_swap
|
45
|
+
}
|
46
|
+
sleep 1
|
47
|
+
|
48
|
+
3.times {
|
49
|
+
rates = plugin.list_rates
|
50
|
+
pairs.each {|p|
|
51
|
+
# 利用可能とされたペアのレートが取得できていることを確認
|
52
|
+
assert_not_nil p.name
|
53
|
+
assert_not_nil p.trade_unit
|
54
|
+
assert_not_nil rates[p.name]
|
55
|
+
assert_not_nil rates[p.name].bid
|
56
|
+
assert_not_nil rates[p.name].ask
|
57
|
+
assert_not_nil rates[p.name].sell_swap
|
58
|
+
assert_not_nil rates[p.name].buy_swap
|
59
|
+
}
|
60
|
+
sleep 10
|
61
|
+
}
|
62
|
+
|
63
|
+
# # 売り/買い
|
64
|
+
# sell = plugin.order( :USDJPY, :sell, 1 )
|
65
|
+
# buy = plugin.order( :USDJPY, :buy, 1 )
|
66
|
+
# assert_not_nil sell.position_id
|
67
|
+
# assert_not_nil buy.position_id
|
68
|
+
#
|
69
|
+
# # 約定
|
70
|
+
# plugin.commit sell.position_id, 1
|
71
|
+
# plugin.commit buy.position_id, 1
|
72
|
+
ensure
|
73
|
+
plugin.destroy_plugin
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clickclient_scrap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masaya Yamauchi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-12 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mechanize
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.3
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: y-masaya@red.hot.co.jp
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
files:
|
34
|
+
- README
|
35
|
+
- ChangeLog
|
36
|
+
- lib/clickclient_scrap.rb
|
37
|
+
- lib/jiji_plugin.rb
|
38
|
+
- test/test_FxSession.rb
|
39
|
+
- test/test_jiji_plugin.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/unageanu/clickclient_scrap/tree/master
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --main
|
47
|
+
- README
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: click securities client library for ruby.
|
69
|
+
test_files:
|
70
|
+
- test/test_FxSession.rb
|
71
|
+
- test/test_jiji_plugin.rb
|