clickclient_scrap 0.1.9 → 0.1.10

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 CHANGED
@@ -1,4 +1,9 @@
1
1
 
2
+ 2010-03-20 0.1.10
3
+ * クリック証券デモ取引用jijiプラグインを実装
4
+ * 建玉が6以上あると建玉一覧の取得、および約定が正しく行えない問題を修正
5
+ * 注文が6以上あると注文一覧の取得、および注文のキャンセルが正しく行えない問題を修正
6
+
2
7
  2009-12-27 0.1.9
3
8
  * レート一覧ページの仕様変更に対応。
4
9
  * スワップが1000を超えた場合に、レート一覧に該当する通貨ペアが含まれない問題を修正。
@@ -1,690 +1,728 @@
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
- DEFAULT_DEMO_HOST_NAME = "https://www.click-sec.com/m/demo/"
42
-
43
- #
44
- #===コンストラクタ
45
- #
46
- #*proxy*:: プロキシホストを利用する場合、そのホスト名とパスを指定します。
47
- # 例) https://proxyhost.com:80
48
- #
49
- def initialize( proxy=ENV["http_proxy"], demo=false )
50
- @client = WWW::Mechanize.new {|c|
51
- # プロキシ
52
- if proxy
53
- uri = URI.parse( proxy )
54
- c.set_proxy( uri.host, uri.port )
55
- end
56
- }
57
- @client.keep_alive = false
58
- @client.max_history=0
59
- @client.user_agent_alias = 'Windows IE 7'
60
- @demo = demo
61
- @host_name = @demo ? DEFAULT_DEMO_HOST_NAME : DEFAULT_HOST_NAME
62
- end
63
-
64
- #ログインし、セッションを開始します。
65
- #-ブロックを指定した場合、引数としてセッションを指定してブロックを実行します。ブロック実行後、ログアウトします。
66
- #-そうでない場合、セッションを返却します。この場合、ClickClientScrap::FX::FxSession#logoutを実行しログアウトしてください。
67
- #
68
- #userid:: ユーザーID
69
- #password:: パスワード
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
+ DEFAULT_DEMO_HOST_NAME = "https://www.click-sec.com/m/demo/"
42
+
43
+ #
44
+ #===コンストラクタ
45
+ #
46
+ #*proxy*:: プロキシホストを利用する場合、そのホスト名とパスを指定します。
47
+ # 例) https://proxyhost.com:80
48
+ #
49
+ def initialize( proxy=ENV["http_proxy"], demo=false )
50
+ @client = WWW::Mechanize.new {|c|
51
+ # プロキシ
52
+ if proxy
53
+ uri = URI.parse( proxy )
54
+ c.set_proxy( uri.host, uri.port )
55
+ end
56
+ }
57
+ @client.keep_alive = false
58
+ @client.max_history=0
59
+ @client.user_agent_alias = 'Windows IE 7'
60
+ @demo = demo
61
+ @host_name = @demo ? DEFAULT_DEMO_HOST_NAME : DEFAULT_HOST_NAME
62
+ end
63
+
64
+ #ログインし、セッションを開始します。
65
+ #-ブロックを指定した場合、引数としてセッションを指定してブロックを実行します。ブロック実行後、ログアウトします。
66
+ #-そうでない場合、セッションを返却します。この場合、ClickClientScrap::FX::FxSession#logoutを実行しログアウトしてください。
67
+ #
68
+ #userid:: ユーザーID
69
+ #password:: パスワード
70
70
  #options:: オプション
71
- #戻り値:: ClickClientScrap::FX::FxSession
72
- def fx_session( userid, password, options={}, &block )
73
- page = @client.get(@host_name)
74
- ClickClientScrap::Client.error(page) if page.forms.length <= 0
75
- form = page.forms.first
76
- form.j_username = userid
77
- form.j_password = password
78
- result = @client.submit(form, form.buttons.first)
79
- # デモサイトではjsによるリダイレクトは不要。
80
- if !@demo
81
- if result.body.toutf8 =~ /<META HTTP-EQUIV="REFRESH" CONTENT="0;URL=([^"]*)">/
82
- result = @client.get($1)
83
- ClickClientScrap::Client.error( result ) if result.links.size <= 0
84
- else
85
- ClickClientScrap::Client.error( result )
86
- end
87
- end
88
- session = FX::FxSession.new( @client, result.links, options )
89
- if block_given?
90
- begin
91
- yield session
92
- ensure
93
- session.logout
94
- end
95
- else
96
- return session
97
- end
98
- end
99
- def self.error( page )
100
- msgs = page.body.scan( /<font color="red">([^<]*)</ ).flatten
101
- error = !msgs.empty? ? msgs.map{|m| m.strip}.join(",") : page.body
102
- raise "operation failed.detail=#{error}".toutf8
103
- end
104
-
105
- #ホスト名
106
- attr :host_name, true
107
- end
108
-
109
- module FX
110
-
111
- # 通貨ペア: 米ドル-円
112
- USDJPY = :USDJPY
113
- # 通貨ペア: ユーロ-円
114
- EURJPY = :EURJPY
115
- # 通貨ペア: イギリスポンド-円
116
- GBPJPY = :GBPJPY
117
- # 通貨ペア: 豪ドル-円
118
- AUDJPY = :AUDJPY
119
- # 通貨ペア: ニュージーランドドル-円
120
- NZDJPY = :NZDJPY
121
- # 通貨ペア: カナダドル-円
122
- CADJPY = :CADJPY
123
- # 通貨ペア: スイスフラン-円
124
- CHFJPY = :CHFJPY
125
- # 通貨ペア: 南アランド-円
126
- ZARJPY = :ZARJPY
127
- # 通貨ペア: ユーロ-米ドル
128
- EURUSD = :EURUSD
129
- # 通貨ペア: イギリスポンド-米ドル
130
- GBPUSD = :GBPUSD
131
- # 通貨ペア: 豪ドル-米ドル
132
- AUDUSD = :AUDUSD
133
- # 通貨ペア: ユーロ-スイスフラン
134
- EURCHF = :EURCHF
135
- # 通貨ペア: イギリスポンド-スイスフラン
136
- GBPCHF = :GBPCHF
137
- # 通貨ペア: 米ドル-スイスフラン
138
- USDCHF = :USDCHF
139
-
140
- # 売買区分: 買い
141
- BUY = 0
142
- # 売買区分: 売り
143
- SELL = 1
144
-
145
- # 注文タイプ: 通常
146
- ORDER_TYPE_MARKET_ORDER = "00"
147
- # 注文タイプ: 通常
148
- ORDER_TYPE_NORMAL = "01"
149
- # 注文タイプ: IFD
150
- ORDER_TYPE_IFD = "11"
151
- # 注文タイプ: OCO
152
- ORDER_TYPE_OCO = "21"
153
- # 注文タイプ: IFD-OCO
154
- ORDER_TYPE_IFD_OCO = "31"
155
-
156
- # 有効期限: 当日限り
157
- EXPIRATION_TYPE_TODAY = 0
158
- # 有効期限: 週末まで
159
- EXPIRATION_TYPE_WEEK_END = 1
160
- # 有効期限: 無期限
161
- EXPIRATION_TYPE_INFINITY = 2
162
- # 有効期限: 日付指定
163
- EXPIRATION_TYPE_SPECIFIED = 3
164
-
165
- # 注文状況: すべて
166
- ORDER_CONDITION_ALL = ""
167
- # 注文状況: 注文中
168
- ORDER_CONDITION_ON_ORDER = "0"
169
- # 注文状況: 取消済
170
- ORDER_CONDITION_CANCELED = "1"
171
- # 注文状況: 約定
172
- ORDER_CONDITION_EXECUTION = "2"
173
- # 注文状況: 不成立
174
- ORDER_CONDITION_FAILED = "3"
175
-
176
- # トレード種別: 新規
177
- TRADE_TYPE_NEW = "新規"
178
- # トレード種別: 決済
179
- TRADE_TYPE_SETTLEMENT = "決済"
180
-
181
- # 執行条件: 成行
182
- EXECUTION_EXPRESSION_MARKET_ORDER = "成行"
183
- # 執行条件: 指値
184
- EXECUTION_EXPRESSION_LIMIT_ORDER = "指値"
185
- # 執行条件: 逆指値
186
- EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER = "逆指値"
187
-
188
- #=== FX取引のためのセッションクラス
189
- #Client#fx_sessionのブロックの引数として渡されます。詳細はClient#fx_sessionを参照ください。
190
- class FxSession
191
-
192
- def initialize( client, links, options={} )
193
- @client = client
194
- @links = links
195
- @options = options
196
- end
197
-
198
- #レート一覧を取得します。
199
- #
200
- #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Rateのハッシュ。
201
- def list_rates
202
- result = link_click( "1" )
203
- if !@last_update_time_of_swaps \
204
- || Time.now.to_i - @last_update_time_of_swaps > (@options[:swap_update_interval] || 60*60)
205
- @swaps = list_swaps
206
- @last_update_time_of_swaps = Time.now.to_i
207
- end
208
- reg = />([A-Z]+\/[A-Z]+)<\/a>[^\-\.\d]*?([\d]+\.[\d]+)\-[^\-\.\d]*([\d\.]+)/
209
- tokens = result.body.toutf8.scan( reg )
210
- ClickClientScrap::Client.error( result ) if !tokens || tokens.empty?
211
- return tokens.inject({}) {|r,l|
212
- pair = to_pair( l[0] )
213
- swap = @swaps[pair]
214
- rate = FxSession.convert_rate "#{l[1]}-#{l[2]}"
215
- if ( rate && swap )
216
- r[pair] = Rate.new( pair, rate[0], rate[1], swap.sell_swap, swap.buy_swap )
217
- end
218
- r
219
- }
220
- end
221
- #12.34-35 形式の文字列をbidレート、askレートに変換する。
222
- def self.convert_rate( str ) #:nodoc:
223
- if str =~ /^([\d]+)\.([\d]+)\-([\d]+)$/
224
- high = $1
225
- low = $2
226
- low2 = $3
227
- bid = high.to_f+(low.to_f/(10**low.length))
228
- ask_low = (low[0...low.length-low2.length] + low2).to_f
229
- if low.to_f > ask_low
230
- ask_low += 10**low2.length
231
- end
232
- ask = high.to_f+(ask_low/10**low.length)
233
- return [bid,ask]
234
- elsif str =~ /^([\d]+\.[\d]+)\-([\d]+\.[\d]+)$/
235
- return [$1.to_f,$2.to_f]
236
- end
237
- end
238
-
239
- #スワップの一覧を取得します。
240
- #
241
- #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Swapのハッシュ。
242
- def list_swaps
243
- result = link_click( "8" )
244
- reg = /<dd>([A-Z]+\/[A-Z]+) <font[^>]*>売<\/font>[^\-\d]*?([\-\d,]+)[^\-\d]*<font[^>]*>買<\/font>[^\-\d]*([\-\d,]+)[^\-\d]*<\/dd>/
245
- return result.body.toutf8.scan( reg ).inject({}) {|r,l|
246
- pair = to_pair( l[0] )
247
- r[pair] = Swap.new( pair, l[1].sub(/,/,"").to_i, l[2].sub(/,/,"").to_i ); r
248
- }
249
- end
250
-
251
- #
252
- #注文を行います。
253
- #
254
- #currency_pair_code:: 通貨ペアコード(必須)
255
- #sell_or_buy:: 売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
256
- #unit:: 取引数量(必須)
257
- #options:: 注文のオプション。注文方法に応じて以下の情報を設定できます。
258
- # - <b>成り行き注文</b>
259
- # - <tt>:slippage</tt> .. スリッページ (オプション)。何pips以内かを整数で指定します。
260
- # - <b>通常注文</b> ※注文レートが設定されていれば通常取引となります。
261
- # - <tt>:rate</tt> .. 注文レート(必須)
262
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
263
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
264
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
265
- # - <b>OCO注文</b> ※逆指値レートが設定されていればOCO取引となります。
266
- # - <tt>:rate</tt> .. 注文レート(必須)
267
- # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
268
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
269
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
270
- # - <b>IFD注文</b> ※決済取引の指定があればIFD取引となります。
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
- # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
276
- # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
277
- # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
278
- # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
279
- # - <tt>:execution_expression</tt> .. 決済取引の執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
280
- # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
281
- # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
282
- # - <b>IFD-OCO注文</b> ※決済取引の指定と逆指値レートの指定があればIFD-OCO取引となります。
283
- # - <tt>:rate</tt> .. 注文レート(必須)
284
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
285
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
286
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
287
- # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
288
- # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
289
- # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
290
- # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
291
- # - <tt>:stop_order_rate</tt> .. 決済取引の逆指値レート(必須)
292
- # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
293
- # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
294
- #戻り値:: ClickClientScrap::FX::OrderResult
295
- #
296
- def order ( currency_pair_code, sell_or_buy, unit, options={} )
297
-
298
- # 取り引き種別の判別とパラメータチェック
299
- type = ORDER_TYPE_MARKET_ORDER
300
- if ( options && options[:settle] != nil )
301
- if ( options[:settle][:stop_order_rate] != nil)
302
- # 逆指値レートと決済取引の指定があればIFD-OCO取引
303
- raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
304
- type = ORDER_TYPE_IFD_OCO
305
- else
306
- # 決済取引の指定のみがあればIFD取引
307
- raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
308
- raise "options[:settle][:execution_expression] is required." if options[:settle][:execution_expression] == nil
309
- type = ORDER_TYPE_IFD
310
- end
311
- raise "options[:rate] is required." if options[:rate] == nil
312
- raise "options[:execution_expression] is required." if options[:execution_expression] == nil
313
- raise "options[:expiration_type] is required." if options[:expiration_type] == nil
314
- raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
315
- raise "options[:settle][:sell_or_buy] is required." if options[:settle][:sell_or_buy] == nil
316
- raise "options[:settle][:unit] is required." if options[:settle][:unit] == nil
317
- raise "options[:settle][:expiration_type] is required." if options[:expiration_type] == nil
318
- elsif ( options && options[:rate] != nil )
319
- if ( options[:stop_order_rate] != nil )
320
- # 逆指値レートが指定されていればOCO取引
321
- type = ORDER_TYPE_OCO
322
- else
323
- # そうでなければ通常取引
324
- raise "options[:execution_expression] is required." if options[:execution_expression] == nil
325
- type = ORDER_TYPE_NORMAL
326
- end
327
- raise "options[:expiration_type] is required." if options[:expiration_type] == nil
328
- else
329
- # 成り行き
330
- type = ORDER_TYPE_MARKET_ORDER
331
- end
332
-
333
- #注文前の注文一覧
334
- before = list_orders( ORDER_CONDITION_ON_ORDER ).inject(Set.new) {|s,o| s << o[0]; s }
335
-
336
- # レート一覧
337
- result = link_click( "1" )
338
-
339
- ClickClientScrap::Client.error( result ) if result.forms.empty?
340
- form = result.forms.first
341
-
342
- # 通貨ペア
343
- option = form.fields.find{|f| f.name == "P001" }.options.find {|o|
344
- to_pair( o.text.strip ) == currency_pair_code
345
- }
346
- raise "illegal currency_pair_code. currency_pair_code=#{currency_pair_code.to_s}" unless option
347
- option.select
348
-
349
- #注文方式
350
- form["P100"] = type
351
-
352
- # 詳細設定画面へ
353
- result = @client.submit(form)
354
- ClickClientScrap::Client.error( result ) if result.forms.empty?
355
- form = result.forms.first
356
- case type
357
- when ORDER_TYPE_MARKET_ORDER
358
- # 成り行き
359
- form["P003"] = unit.to_s # 取り引き数量
360
- form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
361
- form["P005"] = options[:slippage].to_s if ( options && options[:slippage] != nil ) # スリッページ
362
- when ORDER_TYPE_NORMAL
363
- # 指値
364
- form["P003"] = options[:rate].to_s # レート
365
- form["P005"] = unit.to_s # 取り引き数量
366
- form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
367
- exp = options[:execution_expression]
368
- form["P004.0"] = exp == ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER ? "2" : "1" #指値/逆指値
369
- set_expiration( form, options, "P008", "P009" ) # 有効期限
370
- when ORDER_TYPE_OCO
371
- # OCO
372
- form["P003"] = options[:rate].to_s # レート
373
- form["P005"] = options[:stop_order_rate].to_s # 逆指値レート
374
- form["P007"] = unit.to_s # 取り引き数量
375
- form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
376
- set_expiration( form, options, "P010", "P011" ) # 有効期限
377
- else
378
- raise "not supported yet."
379
- end
380
-
381
- # 確認画面へ
382
- result = @client.submit(form)
383
- ClickClientScrap::Client.error( result ) if result.forms.empty?
384
- result = @client.submit(result.forms.first)
385
- ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文受付完了/
386
-
387
- #注文前の一覧と注文後の一覧を比較して注文を割り出す。
388
- #成り行き注文の場合、即座に約定するのでnilになる(タイミングによっては取得できるかも)
389
- tmp = list_orders( ORDER_CONDITION_ON_ORDER ).find {|o| !before.include?(o[0]) }
390
- return OrderResult.new( tmp ? tmp[1].order_no : nil )
391
- end
392
-
393
- # 有効期限を設定する
394
- #form:: フォーム
395
- #options:: パラメータ
396
- #input_type:: 有効期限の種別を入力するinput要素名
397
- #input_date:: 有効期限が日付指定の場合に、日付を入力するinput要素名
398
- def set_expiration( form, options, input_type, input_date )
399
- case options[:expiration_type]
400
- when ClickClientScrap::FX::EXPIRATION_TYPE_TODAY
401
- form[input_type] = "0"
402
- when ClickClientScrap::FX::EXPIRATION_TYPE_WEEK_END
403
- form[input_type] = "1"
404
- when ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED
405
- form[input_type] = "3"
406
- raise "options[:expiration_date] is required." unless options[:expiration_date]
407
- form["#{input_date}.Y"] = options[:expiration_date].year
408
- form["#{input_date}.M"] = options[:expiration_date].month
409
- form["#{input_date}.D"] = options[:expiration_date].day
410
- form["#{input_date}.h"] = options[:expiration_date].respond_to?(:hour) ? options[:expiration_date].hour : "0"
411
- else
412
- form[input_type] = "2"
413
- end
414
- end
415
-
416
- #
417
- #=== 注文をキャンセルします。
418
- #
419
- #order_no:: 注文番号
420
- #戻り値:: なし
421
- #
422
- def cancel_order( order_no )
423
-
424
- raise "order_no is nil." unless order_no
425
-
426
- # 注文一覧
427
- result = link_click( "2" )
428
- ClickClientScrap::Client.error( result ) if result.forms.empty?
429
- form = result.forms.first
430
- form["P002"] = ORDER_CONDITION_ON_ORDER
431
- result = @client.submit(form)
432
-
433
- # 対象となる注文をクリック
434
- link = result.links.find {|l|
435
- l.href =~ /[^"]*GKEY=([a-zA-Z0-9]*)[^"]*/ && $1 == order_no
436
- }
437
- raise "illegal order_no. order_no=#{order_no}" unless link
438
- result = @client.click(link)
439
- ClickClientScrap::Client.error( result ) if result.forms.empty?
440
-
441
- # キャンセル
442
- form = result.forms[1]
443
- result = @client.submit(form)
444
- ClickClientScrap::Client.error( result ) if result.forms.empty?
445
- form = result.forms.first
446
- result = @client.submit(form)
447
- ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文取消受付完了/
448
- end
449
-
450
-
451
- #
452
- #=== 決済注文を行います。
453
- #
454
- #*open_interest_id*:: 決済する建玉番号
455
- #*unit*:: 取引数量
456
- #*options*:: 決済注文のオプション。注文方法に応じて以下の情報を設定できます。
457
- # - <b>成り行き注文</b>
458
- # - <tt>:slippage</tt> .. スリッページ (オプション)
459
- # - <tt>:slippage_base_rate</tt> .. スリッページの基準となる取引レート(スリッページが指定された場合、必須。)
460
- # - <b>通常注文</b> <b>※未実装</b> ※注文レートが設定されていれば通常取引となります。
461
- # - <tt>:rate</tt> .. 注文レート(必須)
462
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
463
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
464
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
465
- # - <b>OCO注文</b> <b>※未実装</b> ※注文レートと逆指値レートが設定されていればOCO取引となります。
466
- # - <tt>:rate</tt> .. 注文レート(必須)
467
- # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
468
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
469
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
470
- #<b>戻り値</b>:: なし
471
- #
472
- def settle ( open_interest_id, unit, options={} )
473
- if ( options[:rate] != nil && options[:stop_order_rate] != nil )
474
- # レートと逆指値レートが指定されていればOCO取引
475
- raise "options[:expiration_type] is required." if options[:expiration_type] == nil
476
- elsif ( options[:rate] != nil )
477
- # レートが指定されていれば通常取引
478
- raise "options[:execution_expression] is required." if options[:execution_expression] == nil
479
- raise "options[:expiration_type] is required." if options[:expiration_type] == nil
480
- else
481
- # 成り行き
482
- if ( options[:slippage] != nil )
483
- raise "if you use a slippage, options[:slippage_base_rate] is required." if options[:slippage_base_rate] == nil
484
- end
485
- end
486
-
487
- # 建玉一覧
488
- result = link_click( "3" )
489
-
490
- # 対象となる建玉をクリック
491
- link = result.links.find {|l|
492
- l.href =~ /[^"]*ORDERNO=([a-zA-Z0-9]*)[^"]*/ && $1 == open_interest_id
493
- }
494
- raise "illegal open_interest_id. open_interest_id=#{open_interest_id}" unless link
495
- result = @client.click(link)
496
-
497
- # 決済
498
- form = result.forms.first
499
- form["P100"] = "00" # 成り行き TODO 通常(01),OCO取引(21)対応
500
- result = @client.submit(form)
501
- ClickClientScrap::Client.error( result ) if result.forms.empty?
502
-
503
- # 設定
504
- form = result.forms.first
505
- form["L111"] = unit.to_s
506
- form["P005"] = options[:slippage].to_s if options[:slippage]
507
- result = @client.submit(form)
508
- ClickClientScrap::Client.error( result ) if result.forms.empty?
509
-
510
- # 確認
511
- form = result.forms.first
512
- result = @client.submit(form)
513
- ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /完了/
514
- end
515
-
516
-
517
- #
518
- #=== 注文一覧を取得します。
519
- #
520
- #order_condition_code:: 注文状況コード(必須)
521
- #currency_pair_code:: 通貨ペアコード <b>※未実装</b>
522
- #戻り値:: 注文番号をキーとするClickClientScrap::FX::Orderのハッシュ。
523
- #
524
- def list_orders( order_condition_code=ClickClientScrap::FX::ORDER_CONDITION_ALL, currency_pair_code=nil )
525
- result = link_click( "2" )
526
- ClickClientScrap::Client.error( result ) if result.forms.empty?
527
- form = result.forms.first
528
- form["P001"] = "" # TODO currency_pair_codeでの絞り込み
529
- form["P002"] = order_condition_code
530
- result = @client.submit(form)
531
-
532
- 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 )
533
- tmp = {}
534
- list.each {|i|
535
- order_no = i[0]
536
- order_type = to_order_type_code(i[2])
537
- trade_type = i[3] == "" ? ClickClientScrap::FX::TRADE_TYPE_NEW : ClickClientScrap::FX::TRADE_TYPE_SETTLEMENT
538
- pair = to_pair( i[1] )
539
- sell_or_buy = i[4] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
540
- count = pair == :ZARJPY ? i[5].to_i/10 : i[5].to_i
541
- rate = i[6].to_f
542
- execution_expression = if i[7] == "指"
543
- ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER
544
- elsif i[7] == "逆"
545
- ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER
546
- else
547
- ClickClientScrap::FX::EXECUTION_EXPRESSION_MARKET_ORDER
548
- end
549
- tmp[order_no] = Order.new( order_no, trade_type, order_type, execution_expression, sell_or_buy, pair, count, rate, i[8])
550
- }
551
- return tmp
552
- end
553
-
554
- #
555
- #=== 建玉一覧を取得します。
556
- #
557
- #currency_pair_code:: 通貨ペアコード。<b>※未実装</b>
558
- #戻り値:: 建玉IDをキーとするClickClientScrap::FX::OpenInterestのハッシュ。
559
- #
560
- def list_open_interests( currency_pair_code=nil )
561
- result = link_click( "3" )
562
- ClickClientScrap::Client.error( result ) if result.forms.empty?
563
- form = result.forms.first
564
- form["P001"] = "" # TODO currency_pair_codeでの絞り込み
565
- result = @client.submit(form)
566
-
567
- list = result.body.toutf8.scan( /<a href="[^"]*">([A-Z]{3}\/[A-Z]{3}):([^<]*)<\/a><br>[^;]*;<font[^>]*>([^<]*)<\/font>([\d\.]*)[^\s@]*@([\d\.]*).*?<font[^>]*>([^<]*)<\/font>/m )
568
- tmp = {}
569
- list.each {|i|
570
- open_interest_id = i[1]
571
- pair = to_pair( i[0] )
572
- sell_or_buy = i[2] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
573
- count = i[3].to_i
574
- rate = i[4].to_f
575
- profit_or_loss = i[5].to_i
576
- tmp[open_interest_id] = OpenInterest.new(open_interest_id, pair, sell_or_buy, count, rate, profit_or_loss )
577
- }
578
- return tmp
579
- end
580
-
581
- #
582
- #=== 余力情報を取得します。
583
- #
584
- #戻り値:: ClickClientScrap::FX::Marginのハッシュ。
585
- #
586
- def get_margin
587
- result = link_click( "7" )
588
- list = result.body.toutf8.scan( /【([^<]*)[^>]*>[^>]*>([^<]*)</m )
589
- values = list.inject({}) {|r,i|
590
- if ( i[0] == "証拠金維持率】" )
591
- r[i[0]] = i[1]
592
- else
593
- r[i[0]] = i[1].gsub(/,/, "").to_i
594
- end
595
- r
596
- }
597
- return Margin.new(
598
- values["時価評価総額】"],
599
- values["建玉評価損益】"],
600
- values["口座残高】"],
601
- values["証拠金維持率】"],
602
- values["余力】"],
603
- values["拘束証拠金】"],
604
- values["必要証拠金】"],
605
- values["注文中必要証拠金】"],
606
- values["振替可能額】"]
607
- )
608
- end
609
-
610
- # ログアウトします。
611
- def logout
612
- @client.click( @links.find {|i|
613
- i.text == "\303\233\302\270\303\236\302\261\302\263\303\204" \
614
- || i.text == "ログアウト"
71
+ #戻り値:: ClickClientScrap::FX::FxSession
72
+ def fx_session( userid, password, options={}, &block )
73
+ page = @client.get(@host_name)
74
+ ClickClientScrap::Client.error(page) if page.forms.length <= 0
75
+ form = page.forms.first
76
+ form.j_username = userid
77
+ form.j_password = password
78
+ result = @client.submit(form, form.buttons.first)
79
+ # デモサイトではjsによるリダイレクトは不要。
80
+ if !@demo
81
+ if result.body.toutf8 =~ /<META HTTP-EQUIV="REFRESH" CONTENT="0;URL=([^"]*)">/
82
+ result = @client.get($1)
83
+ ClickClientScrap::Client.error( result ) if result.links.size <= 0
84
+ else
85
+ ClickClientScrap::Client.error( result )
86
+ end
87
+ end
88
+ session = FX::FxSession.new( @client, result.links, options )
89
+ if block_given?
90
+ begin
91
+ yield session
92
+ ensure
93
+ session.logout
94
+ end
95
+ else
96
+ return session
97
+ end
98
+ end
99
+ def self.error( page )
100
+ msgs = page.body.scan( /<font color="red">([^<]*)</ ).flatten
101
+ error = !msgs.empty? ? msgs.map{|m| m.strip}.join(",") : page.body
102
+ raise "operation failed.detail=#{error}".toutf8
103
+ end
104
+
105
+ #ホスト名
106
+ attr :host_name, true
107
+ end
108
+
109
+ module FX
110
+
111
+ # 通貨ペア: 米ドル-円
112
+ USDJPY = :USDJPY
113
+ # 通貨ペア: ユーロ-円
114
+ EURJPY = :EURJPY
115
+ # 通貨ペア: イギリスポンド-円
116
+ GBPJPY = :GBPJPY
117
+ # 通貨ペア: 豪ドル-円
118
+ AUDJPY = :AUDJPY
119
+ # 通貨ペア: ニュージーランドドル-円
120
+ NZDJPY = :NZDJPY
121
+ # 通貨ペア: カナダドル-円
122
+ CADJPY = :CADJPY
123
+ # 通貨ペア: スイスフラン-円
124
+ CHFJPY = :CHFJPY
125
+ # 通貨ペア: 南アランド-円
126
+ ZARJPY = :ZARJPY
127
+ # 通貨ペア: ユーロ-米ドル
128
+ EURUSD = :EURUSD
129
+ # 通貨ペア: イギリスポンド-米ドル
130
+ GBPUSD = :GBPUSD
131
+ # 通貨ペア: 豪ドル-米ドル
132
+ AUDUSD = :AUDUSD
133
+ # 通貨ペア: ユーロ-スイスフラン
134
+ EURCHF = :EURCHF
135
+ # 通貨ペア: イギリスポンド-スイスフラン
136
+ GBPCHF = :GBPCHF
137
+ # 通貨ペア: 米ドル-スイスフラン
138
+ USDCHF = :USDCHF
139
+
140
+ # 売買区分: 買い
141
+ BUY = 0
142
+ # 売買区分: 売り
143
+ SELL = 1
144
+
145
+ # 注文タイプ: 通常
146
+ ORDER_TYPE_MARKET_ORDER = "00"
147
+ # 注文タイプ: 通常
148
+ ORDER_TYPE_NORMAL = "01"
149
+ # 注文タイプ: IFD
150
+ ORDER_TYPE_IFD = "11"
151
+ # 注文タイプ: OCO
152
+ ORDER_TYPE_OCO = "21"
153
+ # 注文タイプ: IFD-OCO
154
+ ORDER_TYPE_IFD_OCO = "31"
155
+
156
+ # 有効期限: 当日限り
157
+ EXPIRATION_TYPE_TODAY = 0
158
+ # 有効期限: 週末まで
159
+ EXPIRATION_TYPE_WEEK_END = 1
160
+ # 有効期限: 無期限
161
+ EXPIRATION_TYPE_INFINITY = 2
162
+ # 有効期限: 日付指定
163
+ EXPIRATION_TYPE_SPECIFIED = 3
164
+
165
+ # 注文状況: すべて
166
+ ORDER_CONDITION_ALL = ""
167
+ # 注文状況: 注文中
168
+ ORDER_CONDITION_ON_ORDER = "0"
169
+ # 注文状況: 取消済
170
+ ORDER_CONDITION_CANCELED = "1"
171
+ # 注文状況: 約定
172
+ ORDER_CONDITION_EXECUTION = "2"
173
+ # 注文状況: 不成立
174
+ ORDER_CONDITION_FAILED = "3"
175
+
176
+ # トレード種別: 新規
177
+ TRADE_TYPE_NEW = "新規"
178
+ # トレード種別: 決済
179
+ TRADE_TYPE_SETTLEMENT = "決済"
180
+
181
+ # 執行条件: 成行
182
+ EXECUTION_EXPRESSION_MARKET_ORDER = "成行"
183
+ # 執行条件: 指値
184
+ EXECUTION_EXPRESSION_LIMIT_ORDER = "指値"
185
+ # 執行条件: 逆指値
186
+ EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER = "逆指値"
187
+
188
+ #=== FX取引のためのセッションクラス
189
+ #Client#fx_sessionのブロックの引数として渡されます。詳細はClient#fx_sessionを参照ください。
190
+ class FxSession
191
+
192
+ def initialize( client, links, options={} )
193
+ @client = client
194
+ @links = links
195
+ @options = options
196
+ end
197
+
198
+ #レート一覧を取得します。
199
+ #
200
+ #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Rateのハッシュ。
201
+ def list_rates
202
+ result = link_click( "1" )
203
+ if !@last_update_time_of_swaps \
204
+ || Time.now.to_i - @last_update_time_of_swaps > (@options[:swap_update_interval] || 60*60)
205
+ @swaps = list_swaps
206
+ @last_update_time_of_swaps = Time.now.to_i
207
+ end
208
+ reg = />([A-Z]+\/[A-Z]+)<\/a>[^\-\.\d]*?([\d]+\.[\d]+)\-[^\-\.\d]*([\d\.]+)/
209
+ tokens = result.body.toutf8.scan( reg )
210
+ ClickClientScrap::Client.error( result ) if !tokens || tokens.empty?
211
+ return tokens.inject({}) {|r,l|
212
+ pair = to_pair( l[0] )
213
+ swap = @swaps[pair]
214
+ rate = FxSession.convert_rate "#{l[1]}-#{l[2]}"
215
+ if ( rate && swap )
216
+ r[pair] = Rate.new( pair, rate[0], rate[1], swap.sell_swap, swap.buy_swap )
217
+ end
218
+ r
219
+ }
220
+ end
221
+ #12.34-35 形式の文字列をbidレート、askレートに変換する。
222
+ def self.convert_rate( str ) #:nodoc:
223
+ if str =~ /^([\d]+)\.([\d]+)\-([\d]+)$/
224
+ high = $1
225
+ low = $2
226
+ low2 = $3
227
+ bid = high.to_f+(low.to_f/(10**low.length))
228
+ ask_low = (low[0...low.length-low2.length] + low2).to_f
229
+ if low.to_f > ask_low
230
+ ask_low += 10**low2.length
231
+ end
232
+ ask = high.to_f+(ask_low/10**low.length)
233
+ return [bid,ask]
234
+ elsif str =~ /^([\d]+\.[\d]+)\-([\d]+\.[\d]+)$/
235
+ return [$1.to_f,$2.to_f]
236
+ end
237
+ end
238
+
239
+ #スワップの一覧を取得します。
240
+ #
241
+ #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Swapのハッシュ。
242
+ def list_swaps
243
+ result = link_click( "8" )
244
+ reg = /<dd>([A-Z]+\/[A-Z]+) <font[^>]*>売<\/font>[^\-\d]*?([\-\d,]+)[^\-\d]*<font[^>]*>買<\/font>[^\-\d]*([\-\d,]+)[^\-\d]*<\/dd>/
245
+ return result.body.toutf8.scan( reg ).inject({}) {|r,l|
246
+ pair = to_pair( l[0] )
247
+ r[pair] = Swap.new( pair, l[1].sub(/,/,"").to_i, l[2].sub(/,/,"").to_i ); r
248
+ }
249
+ end
250
+
251
+ #
252
+ #注文を行います。
253
+ #
254
+ #currency_pair_code:: 通貨ペアコード(必須)
255
+ #sell_or_buy:: 売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
256
+ #unit:: 取引数量(必須)
257
+ #options:: 注文のオプション。注文方法に応じて以下の情報を設定できます。
258
+ # - <b>成り行き注文</b>
259
+ # - <tt>:slippage</tt> .. スリッページ (オプション)。何pips以内かを整数で指定します。
260
+ # - <b>通常注文</b> ※注文レートが設定されていれば通常取引となります。
261
+ # - <tt>:rate</tt> .. 注文レート(必須)
262
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
263
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
264
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
265
+ # - <b>OCO注文</b> ※逆指値レートが設定されていればOCO取引となります。
266
+ # - <tt>:rate</tt> .. 注文レート(必須)
267
+ # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
268
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
269
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
270
+ # - <b>IFD注文</b> ※決済取引の指定があればIFD取引となります。
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
+ # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
276
+ # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
277
+ # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
278
+ # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
279
+ # - <tt>:execution_expression</tt> .. 決済取引の執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
280
+ # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
281
+ # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
282
+ # - <b>IFD-OCO注文</b> ※決済取引の指定と逆指値レートの指定があればIFD-OCO取引となります。
283
+ # - <tt>:rate</tt> .. 注文レート(必須)
284
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
285
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
286
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
287
+ # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
288
+ # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
289
+ # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
290
+ # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
291
+ # - <tt>:stop_order_rate</tt> .. 決済取引の逆指値レート(必須)
292
+ # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
293
+ # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
294
+ #戻り値:: ClickClientScrap::FX::OrderResult
295
+ #
296
+ def order ( currency_pair_code, sell_or_buy, unit, options={} )
297
+
298
+ # 取り引き種別の判別とパラメータチェック
299
+ type = ORDER_TYPE_MARKET_ORDER
300
+ if ( options && options[:settle] != nil )
301
+ if ( options[:settle][:stop_order_rate] != nil)
302
+ # 逆指値レートと決済取引の指定があればIFD-OCO取引
303
+ raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
304
+ type = ORDER_TYPE_IFD_OCO
305
+ else
306
+ # 決済取引の指定のみがあればIFD取引
307
+ raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
308
+ raise "options[:settle][:execution_expression] is required." if options[:settle][:execution_expression] == nil
309
+ type = ORDER_TYPE_IFD
310
+ end
311
+ raise "options[:rate] is required." if options[:rate] == nil
312
+ raise "options[:execution_expression] is required." if options[:execution_expression] == nil
313
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
314
+ raise "options[:settle][:rate] is required." if options[:settle][:rate] == nil
315
+ raise "options[:settle][:sell_or_buy] is required." if options[:settle][:sell_or_buy] == nil
316
+ raise "options[:settle][:unit] is required." if options[:settle][:unit] == nil
317
+ raise "options[:settle][:expiration_type] is required." if options[:expiration_type] == nil
318
+ elsif ( options && options[:rate] != nil )
319
+ if ( options[:stop_order_rate] != nil )
320
+ # 逆指値レートが指定されていればOCO取引
321
+ type = ORDER_TYPE_OCO
322
+ else
323
+ # そうでなければ通常取引
324
+ raise "options[:execution_expression] is required." if options[:execution_expression] == nil
325
+ type = ORDER_TYPE_NORMAL
326
+ end
327
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
328
+ else
329
+ # 成り行き
330
+ type = ORDER_TYPE_MARKET_ORDER
331
+ end
332
+
333
+ #注文前の注文一覧
334
+ before = list_orders( ORDER_CONDITION_ON_ORDER ).inject(Set.new) {|s,o| s << o[0]; s }
335
+
336
+ # レート一覧
337
+ result = link_click( "1" )
338
+
339
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
340
+ form = result.forms.first
341
+
342
+ # 通貨ペア
343
+ option = form.fields.find{|f| f.name == "P001" }.options.find {|o|
344
+ to_pair( o.text.strip ) == currency_pair_code
345
+ }
346
+ raise "illegal currency_pair_code. currency_pair_code=#{currency_pair_code.to_s}" unless option
347
+ option.select
348
+
349
+ #注文方式
350
+ form["P100"] = type
351
+
352
+ # 詳細設定画面へ
353
+ result = @client.submit(form)
354
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
355
+ form = result.forms.first
356
+ case type
357
+ when ORDER_TYPE_MARKET_ORDER
358
+ # 成り行き
359
+ form["P003"] = unit.to_s # 取り引き数量
360
+ form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
361
+ form["P005"] = options[:slippage].to_s if ( options && options[:slippage] != nil ) # スリッページ
362
+ when ORDER_TYPE_NORMAL
363
+ # 指値
364
+ form["P003"] = options[:rate].to_s # レート
365
+ form["P005"] = unit.to_s # 取り引き数量
366
+ form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
367
+ exp = options[:execution_expression]
368
+ form["P004.0"] = exp == ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER ? "2" : "1" #指値/逆指値
369
+ set_expiration( form, options, "P008", "P009" ) # 有効期限
370
+ when ORDER_TYPE_OCO
371
+ # OCO
372
+ form["P003"] = options[:rate].to_s # レート
373
+ form["P005"] = options[:stop_order_rate].to_s # 逆指値レート
374
+ form["P007"] = unit.to_s # 取り引き数量
375
+ form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
376
+ set_expiration( form, options, "P010", "P011" ) # 有効期限
377
+ else
378
+ raise "not supported yet."
379
+ end
380
+
381
+ # 確認画面へ
382
+ result = @client.submit(form)
383
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
384
+ result = @client.submit(result.forms.first)
385
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文受付完了/
386
+
387
+ #注文前の一覧と注文後の一覧を比較して注文を割り出す。
388
+ #成り行き注文の場合、即座に約定するのでnilになる(タイミングによっては取得できるかも)
389
+ tmp = list_orders( ORDER_CONDITION_ON_ORDER ).find {|o| !before.include?(o[0]) }
390
+ return OrderResult.new( tmp ? tmp[1].order_no : nil )
391
+ end
392
+
393
+ # 有効期限を設定する
394
+ #form:: フォーム
395
+ #options:: パラメータ
396
+ #input_type:: 有効期限の種別を入力するinput要素名
397
+ #input_date:: 有効期限が日付指定の場合に、日付を入力するinput要素名
398
+ def set_expiration( form, options, input_type, input_date )
399
+ case options[:expiration_type]
400
+ when ClickClientScrap::FX::EXPIRATION_TYPE_TODAY
401
+ form[input_type] = "0"
402
+ when ClickClientScrap::FX::EXPIRATION_TYPE_WEEK_END
403
+ form[input_type] = "1"
404
+ when ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED
405
+ form[input_type] = "3"
406
+ raise "options[:expiration_date] is required." unless options[:expiration_date]
407
+ form["#{input_date}.Y"] = options[:expiration_date].year
408
+ form["#{input_date}.M"] = options[:expiration_date].month
409
+ form["#{input_date}.D"] = options[:expiration_date].day
410
+ form["#{input_date}.h"] = options[:expiration_date].respond_to?(:hour) ? options[:expiration_date].hour : "0"
411
+ else
412
+ form[input_type] = "2"
413
+ end
414
+ end
415
+
416
+ #
417
+ #=== 注文をキャンセルします。
418
+ #
419
+ #order_no:: 注文番号
420
+ #戻り値:: なし
421
+ #
422
+ def cancel_order( order_no )
423
+
424
+ raise "order_no is nil." unless order_no
425
+
426
+ # 注文一覧
427
+ result = link_click( "2" )
428
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
429
+ form = result.forms.first
430
+ form["P002"] = ORDER_CONDITION_ON_ORDER
431
+ result = @client.submit(form)
432
+
433
+ # 対象となる注文をクリック
434
+ link = nil
435
+ each_page( result ) {|page|
436
+ link = page.links.find {|l|
437
+ l.href =~ /[^"]*GKEY=([a-zA-Z0-9]*)[^"]*/ && $1 == order_no
438
+ }
439
+ break if link
440
+ }
441
+ raise "illegal order_no. order_no=#{order_no}" unless link
442
+ result = @client.click(link)
443
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
444
+
445
+ # キャンセル
446
+ form = result.forms[1]
447
+ result = @client.submit(form)
448
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
449
+ form = result.forms.first
450
+ result = @client.submit(form)
451
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文取消受付完了/
452
+ end
453
+
454
+
455
+ #
456
+ #=== 決済注文を行います。
457
+ #
458
+ #*open_interest_id*:: 決済する建玉番号
459
+ #*unit*:: 取引数量
460
+ #*options*:: 決済注文のオプション。注文方法に応じて以下の情報を設定できます。
461
+ # - <b>成り行き注文</b>
462
+ # - <tt>:slippage</tt> .. スリッページ (オプション)
463
+ # - <tt>:slippage_base_rate</tt> .. スリッページの基準となる取引レート(スリッページが指定された場合、必須。)
464
+ # - <b>通常注文</b> <b>※未実装</b> ※注文レートが設定されていれば通常取引となります。
465
+ # - <tt>:rate</tt> .. 注文レート(必須)
466
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
467
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
468
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
469
+ # - <b>OCO注文</b> <b>※未実装</b> ※注文レートと逆指値レートが設定されていればOCO取引となります。
470
+ # - <tt>:rate</tt> .. 注文レート(必須)
471
+ # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
472
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
473
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
474
+ #<b>戻り値</b>:: なし
475
+ #
476
+ def settle ( open_interest_id, unit, options={} )
477
+ if ( options[:rate] != nil && options[:stop_order_rate] != nil )
478
+ # レートと逆指値レートが指定されていればOCO取引
479
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
480
+ elsif ( options[:rate] != nil )
481
+ # レートが指定されていれば通常取引
482
+ raise "options[:execution_expression] is required." if options[:execution_expression] == nil
483
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
484
+ else
485
+ # 成り行き
486
+ if ( options[:slippage] != nil )
487
+ raise "if you use a slippage, options[:slippage_base_rate] is required." if options[:slippage_base_rate] == nil
488
+ end
489
+ end
490
+
491
+ # 建玉一覧
492
+ result = link_click( "3" )
493
+
494
+ # 対象となる建玉をクリック
495
+ link = nil
496
+ each_page( result ) {|page|
497
+ link = page.links.find {|l|
498
+ l.href =~ /[^"]*ORDERNO=([a-zA-Z0-9]*)[^"]*/ && $1 == open_interest_id
499
+ }
500
+ break if link
501
+ }
502
+ raise "illegal open_interest_id. open_interest_id=#{open_interest_id}" unless link
503
+ result = @client.click(link)
504
+
505
+ # 決済
506
+ form = result.forms.first
507
+ form["P100"] = "00" # 成り行き TODO 通常(01),OCO取引(21)対応
508
+ result = @client.submit(form)
509
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
510
+
511
+ # 設定
512
+ form = result.forms.first
513
+ form["P003"] = unit.to_s
514
+ form["P005"] = options[:slippage].to_s if options[:slippage]
515
+ result = @client.submit(form)
516
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
517
+
518
+ # 確認
519
+ form = result.forms.first
520
+ result = @client.submit(form)
521
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /完了/
522
+ end
523
+
524
+
525
+ #
526
+ #=== 注文一覧を取得します。
527
+ #
528
+ #order_condition_code:: 注文状況コード(必須)
529
+ #currency_pair_code:: 通貨ペアコード <b>※未実装</b>
530
+ #戻り値:: 注文番号をキーとするClickClientScrap::FX::Orderのハッシュ。
531
+ #
532
+ def list_orders( order_condition_code=ClickClientScrap::FX::ORDER_CONDITION_ALL, currency_pair_code=nil )
533
+ result = link_click( "2" )
534
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
535
+ form = result.forms.first
536
+ form["P001"] = "" # TODO currency_pair_codeでの絞り込み
537
+ form["P002"] = order_condition_code
538
+ result = @client.submit(form)
539
+
540
+ list = []
541
+ each_page( result ) {|page|
542
+ list += page.body.toutf8.scan( /<a href="[^"]*GKEY=([a-zA-Z0-9]*)">([A-Z]{3}\/[A-Z]{3}) ([^<]*)<\/a><br>[^;]*;([^<]*)<font[^>]*>([^<]*)<\/font>([^@]*)@([\d\.]*)([^\s]*) ([^<]*)<br>([^;]*;([^<]*)<font[^>]*>([^<]*)<\/font>([^@]*)@([\d\.]*)([^\s]*) ([^<]*)<br>)?/m )
543
+ }
544
+ tmp = {}
545
+ list.each {|i|
546
+ order_no = i[0]
547
+ order_type = to_order_type_code(i[2])
548
+ trade_type = i[3] == "新" ? ClickClientScrap::FX::TRADE_TYPE_NEW \
549
+ : ClickClientScrap::FX::TRADE_TYPE_SETTLEMENT
550
+ pair = to_pair( i[1] )
551
+ sell_or_buy = i[4] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
552
+ count = pair == :ZARJPY ? i[5].to_i/10 : i[5].to_i
553
+ rate = i[6].to_f
554
+ execution_expression = to_execution_expression(i[7])
555
+ stop_order_rate = i[13] ? i[13].to_f : nil
556
+ stop_order_execution_expression = i[14] ? to_execution_expression(i[14]) : nil
557
+
558
+ tmp[order_no] = Order.new( order_no, trade_type, order_type, execution_expression, \
559
+ sell_or_buy, pair, count, rate, i[8], stop_order_rate, stop_order_execution_expression)
560
+ }
561
+ return tmp
562
+ end
563
+
564
+ #
565
+ #=== 建玉一覧を取得します。
566
+ #
567
+ #currency_pair_code:: 通貨ペアコード。<b>※未実装</b>
568
+ #戻り値:: 建玉IDをキーとするClickClientScrap::FX::OpenInterestのハッシュ。
569
+ #
570
+ def list_open_interests( currency_pair_code=nil )
571
+ result = link_click( "3" )
572
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
573
+ form = result.forms.first
574
+ form["P001"] = "" # TODO currency_pair_codeでの絞り込み
575
+ result = @client.submit(form)
576
+
577
+ list = []
578
+ each_page( result ) {|page|
579
+ list += page.body.toutf8.scan( /<a href="[^"]*">([A-Z]{3}\/[A-Z]{3}):([^<]*)<\/a><br>[^;]*;<font[^>]*>([^<]*)<\/font>([\d\.]*)[^\s@]*@([\d\.]*).*?<font[^>]*>([^<]*)<\/font>/m )
580
+ }
581
+ tmp = {}
582
+ list.each {|i|
583
+ open_interest_id = i[1]
584
+ pair = to_pair( i[0] )
585
+ sell_or_buy = i[2] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
586
+ count = i[3].to_i
587
+ rate = i[4].to_f
588
+ profit_or_loss = i[5].to_i
589
+ tmp[open_interest_id] = OpenInterest.new(open_interest_id, pair, sell_or_buy, count, rate, profit_or_loss )
590
+ }
591
+ return tmp
592
+ end
593
+
594
+ # すべてのページを列挙する。
595
+ def each_page( page )
596
+ current_page=1
597
+ while ( page )
598
+ yield page
599
+ current_page+=1
600
+ link_to_next = page.links.find{|i|
601
+ i.text == current_page.to_s
602
+ }
603
+ page = link_to_next ? @client.click( link_to_next ) : nil
604
+ end
605
+ end
606
+
607
+ #
608
+ #=== 余力情報を取得します。
609
+ #
610
+ #戻り値:: ClickClientScrap::FX::Marginのハッシュ。
611
+ #
612
+ def get_margin
613
+ result = link_click( "7" )
614
+ list = result.body.toutf8.scan( /【([^<]*)[^>]*>[^>]*>([^<]*)</m )
615
+ values = list.inject({}) {|r,i|
616
+ if ( i[0] == "証拠金維持率】" )
617
+ r[i[0]] = i[1]
618
+ else
619
+ r[i[0]] = i[1].gsub(/,/, "").to_i
620
+ end
621
+ r
622
+ }
623
+ return Margin.new(
624
+ values["時価評価総額】"],
625
+ values["建玉評価損益】"],
626
+ values["口座残高】"],
627
+ values["証拠金維持率】"],
628
+ values["余力】"],
629
+ values["拘束証拠金】"],
630
+ values["必要証拠金】"],
631
+ values["注文中必要証拠金】"],
632
+ values["振替可能額】"]
633
+ )
634
+ end
635
+
636
+ # ログアウトします。
637
+ def logout
638
+ @client.click( @links.find {|i|
639
+ i.text == "\303\233\302\270\303\236\302\261\302\263\303\204" \
640
+ || i.text == "ログアウト"
615
641
  })
616
- end
617
-
618
- private
619
- # "USD/JPY"を:USDJPYのようなシンボルに変換します。
620
- def to_pair( str )
621
- str.gsub( /\//, "" ).to_sym
622
- end
623
-
624
- # 注文種別を注文種別コードに変換します。
625
- def to_order_type_code( order_type )
626
- return case order_type
627
- when "成行注文"
628
- ClickClientScrap::FX::ORDER_TYPE_MARKET_ORDER
629
- when "通常注文"
630
- ClickClientScrap::FX::ORDER_TYPE_NORMAL
631
- when "OCO注文"
632
- ClickClientScrap::FX::ORDER_TYPE_OCO
633
- when "IFD注文"
634
- ClickClientScrap::FX::ORDER_TYPE_IFD
635
- when "IFD-OCO注文"
636
- ClickClientScrap::FX::ORDER_TYPE_IFD_OCO
637
- else
638
- raise "illegal order_type. order_type=#{order_type}"
639
- end
640
- end
641
-
642
- def link_click( no )
643
- link = @links.find {|i|
644
- i.attributes["accesskey"] == no
645
- }
646
- raise "link isnot found. accesskey=#{no}" unless link
647
- @client.click( link )
648
- end
649
- end
650
-
651
- # オプション
652
- attr :options, true
653
-
654
- #=== スワップ
655
- Swap = Struct.new(:pair, :sell_swap, :buy_swap)
656
- #=== レート
657
- Rate = Struct.new(:pair, :bid_rate, :ask_rate, :sell_swap, :buy_swap )
658
- #===注文
659
- Order = Struct.new(:order_no, :trade_type, :order_type, :execution_expression, :sell_or_buy, :pair, :count, :rate, :order_state )
660
- #===注文結果
661
- OrderResult = Struct.new(:order_no )
662
- #===建玉
663
- OpenInterest = Struct.new(:open_interest_id, :pair, :sell_or_buy, :count, :rate, :profit_or_loss )
664
- #===余力
665
- Margin = Struct.new(
666
- :market_value, #時価評価の総額
667
- :appraisal_profit_or_loss_of_open_interest, #建玉の評価損益
668
- :balance_in_account, # 口座残高
669
- :guarantee_money_maintenance_ratio, #証拠金の維持率
670
- :margin, #余力
671
- :freezed_guarantee_money, #拘束されている証拠金
672
- :required_guarantee_money, #必要な証拠金
673
- :ordered_guarantee_money, #注文中の証拠金
674
- :transferable_money_amount #振替可能額
675
- )
676
- end
677
- end
678
-
679
- class << WWW::Mechanize::Util
680
- def from_native_charset(s, code)
681
- if WWW::Mechanize.html_parser == Nokogiri::HTML
682
- return unless s
683
- Iconv.iconv(code, "UTF-8", s).join("") rescue s # エラーになった場合、変換前の文字列を返す
684
- else
685
- return s
686
- end
687
- end
688
- end
689
-
690
-
642
+ end
643
+
644
+ private
645
+ # "USD/JPY"を:USDJPYのようなシンボルに変換します。
646
+ def to_pair( str )
647
+ str.gsub( /\//, "" ).to_sym
648
+ end
649
+
650
+ # 注文種別を注文種別コードに変換します。
651
+ def to_execution_expression( execution_expression )
652
+ if execution_expression == "指"
653
+ return ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER
654
+ elsif execution_expression == "逆"
655
+ return ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER
656
+ else
657
+ return ClickClientScrap::FX::EXECUTION_EXPRESSION_MARKET_ORDER
658
+ end
659
+ end
660
+
661
+ # 注文種別を注文種別コードに変換します。
662
+ def to_order_type_code( order_type )
663
+ return case order_type
664
+ when "成行注文"
665
+ ClickClientScrap::FX::ORDER_TYPE_MARKET_ORDER
666
+ when "通常注文"
667
+ ClickClientScrap::FX::ORDER_TYPE_NORMAL
668
+ when "OCO注文"
669
+ ClickClientScrap::FX::ORDER_TYPE_OCO
670
+ when "IFD注文"
671
+ ClickClientScrap::FX::ORDER_TYPE_IFD
672
+ when "IFD-OCO注文"
673
+ ClickClientScrap::FX::ORDER_TYPE_IFD_OCO
674
+ else
675
+ raise "illegal order_type. order_type=#{order_type}"
676
+ end
677
+ end
678
+
679
+ def link_click( no )
680
+ link = @links.find {|i|
681
+ i.attributes["accesskey"] == no
682
+ }
683
+ raise "link isnot found. accesskey=#{no}" unless link
684
+ @client.click( link )
685
+ end
686
+ end
687
+
688
+ # オプション
689
+ attr :options, true
690
+
691
+ #=== スワップ
692
+ Swap = Struct.new(:pair, :sell_swap, :buy_swap)
693
+ #=== レート
694
+ Rate = Struct.new(:pair, :bid_rate, :ask_rate, :sell_swap, :buy_swap )
695
+ #===注文
696
+ Order = Struct.new(:order_no, :trade_type, :order_type, :execution_expression, :sell_or_buy, :pair,
697
+ :count, :rate, :order_state, :stop_order_rate, :stop_order_execution_expression )
698
+ #===注文結果
699
+ OrderResult = Struct.new(:order_no )
700
+ #===建玉
701
+ OpenInterest = Struct.new(:open_interest_id, :pair, :sell_or_buy, :count, :rate, :profit_or_loss )
702
+ #===余力
703
+ Margin = Struct.new(
704
+ :market_value, #時価評価の総額
705
+ :appraisal_profit_or_loss_of_open_interest, #建玉の評価損益
706
+ :balance_in_account, # 口座残高
707
+ :guarantee_money_maintenance_ratio, #証拠金の維持率
708
+ :margin, #余力
709
+ :freezed_guarantee_money, #拘束されている証拠金
710
+ :required_guarantee_money, #必要な証拠金
711
+ :ordered_guarantee_money, #注文中の証拠金
712
+ :transferable_money_amount #振替可能額
713
+ )
714
+ end
715
+ end
716
+
717
+ class << WWW::Mechanize::Util
718
+ def from_native_charset(s, code)
719
+ if WWW::Mechanize.html_parser == Nokogiri::HTML
720
+ return unless s
721
+ Iconv.iconv(code, "UTF-8", s).join("") rescue s # エラーになった場合、変換前の文字列を返す
722
+ else
723
+ return s
724
+ end
725
+ end
726
+ end
727
+
728
+