unageanu-clickclient_scrap 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -9,8 +9,14 @@
9
9
  - ログアウト
10
10
  - レート一覧の取得
11
11
  - 注文一覧の取得
12
- - 指値注文
13
- - OCO注文
12
+ - 建玉一覧の取得
13
+ - 注文
14
+ -- 成行
15
+ -- 指値
16
+ -- OCO
17
+ - 注文のキャンセル
18
+ - 決済
19
+ -- 成行
14
20
 
15
21
  ===注意事項
16
22
  - スクレイピングによるアクセスのため、Webサイトの仕様変更等により
@@ -5,6 +5,7 @@ end
5
5
  require 'mechanize'
6
6
  require 'date'
7
7
  require 'kconv'
8
+ require 'set'
8
9
 
9
10
  #
10
11
  #=== クリック証券アクセスクライアント
@@ -17,10 +18,10 @@ require 'kconv'
17
18
  #
18
19
  #====基本的な使い方
19
20
  #
20
- # require 'clickclient'
21
+ # require 'clickclient_scrap'
21
22
  #
22
23
  # c = ClickClient::Client.new
23
- # # c = ClickClient::Client.new https://<プロキシホスト>:<プロキシポート> # プロキシを利用する場合
24
+ # # c = ClickClientScrap::Client.new https://<プロキシホスト>:<プロキシポート> # プロキシを利用する場合
24
25
  # c.fx_session( "<ユーザー名>", "<パスワード>" ) { | fx_session |
25
26
  # # 通貨ペア一覧取得
26
27
  # list = fx_session.list_rates
@@ -31,7 +32,7 @@ require 'kconv'
31
32
  #- 本ライブラリの利用は自己責任でお願いします。
32
33
  #- ライブラリの不備・不具合等によるあらゆる損害について、作成者は責任を負いません。
33
34
  #
34
- module ClickClient
35
+ module ClickClientScrap
35
36
 
36
37
  # クライアント
37
38
  class Client
@@ -44,7 +45,7 @@ module ClickClient
44
45
  #*proxy*:: プロキシホストを利用する場合、そのホスト名とパスを指定します。
45
46
  # 例) https://proxyhost.com:80
46
47
  #
47
- def initialize( proxy=nil )
48
+ def initialize( proxy=nil )
48
49
  @client = WWW::Mechanize.new {|c|
49
50
  # プロキシ
50
51
  if proxy
@@ -52,25 +53,30 @@ module ClickClient
52
53
  c.set_proxy( uri.host, uri.port )
53
54
  end
54
55
  }
56
+ @client.keep_alive = false
55
57
  @client.user_agent_alias = 'Windows IE 7'
56
58
  @host_name = DEFAULT_HOST_NAME
57
59
  end
58
60
 
59
61
  #ログインし、セッションを開始します。
60
62
  #-ブロックを指定した場合、引数としてセッションを指定してブロックを実行します。ブロック実行後、ログアウトします。
61
- #-そうでない場合、セッションを返却します。この場合、ClickClient::FX::FxSession#logoutを実行しログアウトしてください。
63
+ #-そうでない場合、セッションを返却します。この場合、ClickClientScrap::FX::FxSession#logoutを実行しログアウトしてください。
62
64
  #
63
- #戻り値:: ClickClient::FX::FxSession
64
- def fx_session( userid, password, &block )
65
+ #userid:: ユーザーID
66
+ #password:: パスワード
67
+ #options:: オプション
68
+ #戻り値:: ClickClientScrap::FX::FxSession
69
+ def fx_session( userid, password, options={}, &block )
65
70
  page = @client.get(@host_name)
66
- ClickClient::Client.error(page) if page.forms.length <= 0
71
+ ClickClientScrap::Client.error(page) if page.forms.length <= 0
67
72
  form = page.forms.first
68
73
  form.j_username = userid
69
74
  form.j_password = password
70
75
  result = @client.submit(form, form.buttons.first)
71
76
  if result.body.toutf8 =~ /<META HTTP-EQUIV="REFRESH" CONTENT="0;URL=([^"]*)">/
72
77
  result = @client.get($1)
73
- session = FX::FxSession.new( @client, result.links )
78
+ ClickClientScrap::Client.error( result ) if result.links.size <= 0
79
+ session = FX::FxSession.new( @client, result.links, options )
74
80
  if block_given?
75
81
  begin
76
82
  yield session
@@ -81,7 +87,7 @@ module ClickClient
81
87
  return session
82
88
  end
83
89
  else
84
- ClickClient::Client.error( result )
90
+ ClickClientScrap::Client.error( result )
85
91
  end
86
92
  end
87
93
  def self.error( page )
@@ -177,21 +183,26 @@ module ClickClient
177
183
  #Client#fx_sessionのブロックの引数として渡されます。詳細はClient#fx_sessionを参照ください。
178
184
  class FxSession
179
185
 
180
- def initialize( client, links )
186
+ def initialize( client, links, options={} )
181
187
  @client = client
182
188
  @links = links
189
+ @options = options
183
190
  end
184
191
 
185
192
  #レート一覧を取得します。
186
193
  #
187
- #戻り値:: 通貨ペアをキーとするClickClient::FX::Rateのハッシュ。
194
+ #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Rateのハッシュ。
188
195
  def list_rates
189
- result = @client.click( @links.find {|i|
190
- i.attributes["accesskey"] == "1"
191
- })
192
- @swaps = list_swaps unless @swaps
196
+ result = link_click( "1" )
197
+ if !@last_update_time_of_swaps \
198
+ || Time.now.to_i - @last_update_time_of_swaps > (@options[:swap_update_interval] || 60*60)
199
+ @swaps = list_swaps
200
+ @last_update_time_of_swaps = Time.now.to_i
201
+ end
193
202
  reg = />([A-Z]+\/[A-Z]+)<\/a>[^\-\.\d]*?([\d]+\.[\d]+\-[\d]+)/
194
- return result.body.toutf8.scan( reg ).inject({}) {|r,l|
203
+ tokens = result.body.toutf8.scan( reg )
204
+ ClickClientScrap::Client.error( result ) if !tokens || tokens.empty?
205
+ return tokens.inject({}) {|r,l|
195
206
  pair = to_pair( l[0] )
196
207
  swap = @swaps[pair]
197
208
  rate = FxSession.convert_rate l[1]
@@ -219,11 +230,9 @@ module ClickClient
219
230
 
220
231
  #スワップの一覧を取得します。
221
232
  #
222
- #戻り値:: 通貨ペアをキーとするClickClient::FX::Swapのハッシュ。
233
+ #戻り値:: 通貨ペアをキーとするClickClientScrap::FX::Swapのハッシュ。
223
234
  def list_swaps
224
- result = @client.click( @links.find {|i|
225
- i.attributes["accesskey"] == "8"
226
- })
235
+ result = link_click( "8" )
227
236
  reg = /<dd>([A-Z]+\/[A-Z]+) <font[^>]*>売<\/font>[^\-\d]*?([\-\d]+)[^\-\d]*<font[^>]*>買<\/font>[^\-\d]*([\-\d]+)[^\-\d]*<\/dd>/
228
237
  return result.body.toutf8.scan( reg ).inject({}) {|r,l|
229
238
  pair = to_pair( l[0] )
@@ -235,46 +244,46 @@ module ClickClient
235
244
  #注文を行います。
236
245
  #
237
246
  #currency_pair_code:: 通貨ペアコード(必須)
238
- #sell_or_buy:: 売買区分。ClickClient::FX::BUY,ClickClient::FX::SELLのいずれかを指定します。(必須)
247
+ #sell_or_buy:: 売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
239
248
  #unit:: 取引数量(必須)
240
249
  #options:: 注文のオプション。注文方法に応じて以下の情報を設定できます。
241
250
  # - <b>成り行き注文</b>
242
251
  # - <tt>:slippage</tt> .. スリッページ (オプション)。何pips以内かを整数で指定します。
243
252
  # - <b>通常注文</b> ※注文レートが設定されていれば通常取引となります。
244
253
  # - <tt>:rate</tt> .. 注文レート(必須)
245
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClient::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
246
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
247
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
254
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
255
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
256
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
248
257
  # - <b>OCO注文</b> ※逆指値レートが設定されていればOCO取引となります。
249
258
  # - <tt>:rate</tt> .. 注文レート(必須)
250
259
  # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
251
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
252
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
260
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
261
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
253
262
  # - <b>IFD注文</b> ※決済取引の指定があればIFD取引となります。
254
263
  # - <tt>:rate</tt> .. 注文レート(必須)
255
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClient::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
256
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
257
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
264
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
265
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
266
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
258
267
  # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
259
268
  # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
260
- # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClient::FX::BUY,ClickClient::FX::SELLのいずれかを指定します。(必須)
269
+ # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
261
270
  # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
262
- # - <tt>:execution_expression</tt> .. 決済取引の執行条件。ClickClient::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
263
- # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
264
- # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
271
+ # - <tt>:execution_expression</tt> .. 決済取引の執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
272
+ # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
273
+ # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
265
274
  # - <b>IFD-OCO注文</b> ※決済取引の指定と逆指値レートの指定があればIFD-OCO取引となります。
266
275
  # - <tt>:rate</tt> .. 注文レート(必須)
267
- # - <tt>:execution_expression</tt> .. 執行条件。ClickClient::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
268
- # - <tt>:expiration_type</tt> .. 有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
269
- # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
276
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
277
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
278
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
270
279
  # - <tt>:settle</tt> .. 決済取引の指定。マップで指定します。
271
280
  # - <tt>:unit</tt> .. 決済取引の取引数量(必須)
272
- # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClient::FX::BUY,ClickClient::FX::SELLのいずれかを指定します。(必須)
281
+ # - <tt>:sell_or_buy</tt> .. 決済取引の売買区分。ClickClientScrap::FX::BUY,ClickClientScrap::FX::SELLのいずれかを指定します。(必須)
273
282
  # - <tt>:rate</tt> .. 決済取引の注文レート(必須)
274
283
  # - <tt>:stop_order_rate</tt> .. 決済取引の逆指値レート(必須)
275
- # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClient::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
276
- # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClient::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
277
- #戻り値:: ClickClient::FX::OrderResult TODO
284
+ # - <tt>:expiration_type</tt> .. 決済取引の有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
285
+ # - <tt>:expiration_date</tt> .. 決済取引の有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
286
+ #戻り値:: ClickClientScrap::FX::OrderResult
278
287
  #
279
288
  def order ( currency_pair_code, sell_or_buy, unit, options={} )
280
289
 
@@ -312,11 +321,14 @@ module ClickClient
312
321
  # 成り行き
313
322
  type = ORDER_TYPE_MARKET_ORDER
314
323
  end
315
-
324
+
325
+ #注文前の注文一覧
326
+ before = list_orders( ORDER_CONDITION_ON_ORDER ).inject(Set.new) {|s,o| s << o[0]; s }
327
+
316
328
  # レート一覧
317
- result = @client.click( @links.find {|i|
318
- i.attributes["accesskey"] == "1"
319
- })
329
+ result = link_click( "1" )
330
+
331
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
320
332
  form = result.forms.first
321
333
 
322
334
  # 通貨ペア
@@ -331,27 +343,28 @@ module ClickClient
331
343
 
332
344
  # 詳細設定画面へ
333
345
  result = @client.submit(form)
346
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
334
347
  form = result.forms.first
335
348
  case type
336
349
  when ORDER_TYPE_MARKET_ORDER
337
350
  # 成り行き
338
351
  form["P002"] = unit.to_s # 取り引き数量
339
- form["P005.0"] = sell_or_buy == ClickClient::FX::SELL ? "1" : "0" #売り/買い
352
+ form["P005.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
340
353
  form["P007"] = options[:slippage].to_s if ( options && options[:slippage] != nil ) # スリッページ
341
354
  when ORDER_TYPE_NORMAL
342
355
  # 指値
343
356
  form["P003"] = options[:rate].to_s # レート
344
357
  form["P005"] = unit.to_s # 取り引き数量
345
- form["P002.0"] = sell_or_buy == ClickClient::FX::SELL ? "1" : "0" #売り/買い
358
+ form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
346
359
  exp = options[:execution_expression]
347
- form["P004.0"] = exp == ClickClient::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER ? "2" : "1" #指値/逆指値
360
+ form["P004.0"] = exp == ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER ? "2" : "1" #指値/逆指値
348
361
  set_expiration( form, options, "P008", "P009" ) # 有効期限
349
362
  when ORDER_TYPE_OCO
350
363
  # OCO
351
364
  form["P003"] = options[:rate].to_s # レート
352
365
  form["P005"] = options[:stop_order_rate].to_s # 逆指値レート
353
366
  form["P007"] = unit.to_s # 取り引き数量
354
- form["P002.0"] = sell_or_buy == ClickClient::FX::SELL ? "1" : "0" #売り/買い
367
+ form["P002.0"] = sell_or_buy == ClickClientScrap::FX::SELL ? "1" : "0" #売り/買い
355
368
  set_expiration( form, options, "P010", "P011" ) # 有効期限
356
369
  else
357
370
  raise "not supported yet."
@@ -359,10 +372,14 @@ module ClickClient
359
372
 
360
373
  # 確認画面へ
361
374
  result = @client.submit(form)
375
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
362
376
  result = @client.submit(result.forms.first)
363
- #puts result.body.toutf8
364
- ClickClient::Client.error( result ) unless result.body.toutf8 =~ /注文受付完了/
365
- # TODO 結果を返す・・・どうするかな。
377
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文受付完了/
378
+
379
+ #注文前の一覧と注文後の一覧を比較して注文を割り出す。
380
+ #成り行き注文の場合、即座に約定するのでnilになる(タイミングによっては取得できるかも)
381
+ tmp = list_orders( ORDER_CONDITION_ON_ORDER ).find {|o| !before.include?(o[0]) }
382
+ return OrderResult.new( tmp ? tmp[1].order_no : nil )
366
383
  end
367
384
 
368
385
  # 有効期限を設定する
@@ -372,11 +389,11 @@ module ClickClient
372
389
  #input_date:: 有効期限が日付指定の場合に、日付を入力するinput要素名
373
390
  def set_expiration( form, options, input_type, input_date )
374
391
  case options[:expiration_type]
375
- when ClickClient::FX::EXPIRATION_TYPE_TODAY
392
+ when ClickClientScrap::FX::EXPIRATION_TYPE_TODAY
376
393
  form[input_type] = "0"
377
- when ClickClient::FX::EXPIRATION_TYPE_WEEK_END
394
+ when ClickClientScrap::FX::EXPIRATION_TYPE_WEEK_END
378
395
  form[input_type] = "1"
379
- when ClickClient::FX::EXPIRATION_TYPE_SPECIFIED
396
+ when ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED
380
397
  form[input_type] = "3"
381
398
  raise "options[:expiration_date] is required." unless options[:expiration_date]
382
399
  form["#{input_date}.Y"] = options[:expiration_date].year
@@ -388,50 +405,176 @@ module ClickClient
388
405
  end
389
406
  end
390
407
 
408
+ #
409
+ #=== 注文をキャンセルします。
410
+ #
411
+ #order_no:: 注文番号
412
+ #戻り値:: なし
413
+ #
414
+ def cancel_order( order_no )
415
+
416
+ raise "order_no is nil." unless order_no
417
+
418
+ # 注文一覧
419
+ result = link_click( "2" )
420
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
421
+ form = result.forms.first
422
+ form["P002"] = ORDER_CONDITION_ON_ORDER
423
+ result = @client.submit(form)
424
+
425
+ # 対象となる注文をクリック
426
+ link = result.links.find {|l|
427
+ l.href =~ /[^"]*GKEY=([a-zA-Z0-9]*)[^"]*/ && $1 == order_no
428
+ }
429
+ raise "illegal order_no. order_no=#{order_no}" unless link
430
+ result = @client.click(link)
431
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
432
+
433
+ # キャンセル
434
+ form = result.forms[1]
435
+ result = @client.submit(form)
436
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
437
+ form = result.forms.first
438
+ result = @client.submit(form)
439
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /注文取消受付完了/
440
+ end
441
+
442
+
443
+ #
444
+ #=== 決済注文を行います。
445
+ #
446
+ #*open_interest_id*:: 決済する建玉番号
447
+ #*unit*:: 取引数量
448
+ #*options*:: 決済注文のオプション。注文方法に応じて以下の情報を設定できます。
449
+ # - <b>成り行き注文</b>
450
+ # - <tt>:slippage</tt> .. スリッページ (オプション)
451
+ # - <tt>:slippage_base_rate</tt> .. スリッページの基準となる取引レート(スリッページが指定された場合、必須。)
452
+ # - <b>通常注文</b> ※注文レートが設定されていれば通常取引となります。
453
+ # - <tt>:rate</tt> .. 注文レート(必須)
454
+ # - <tt>:execution_expression</tt> .. 執行条件。ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER等を指定します(必須)
455
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
456
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
457
+ # - <b>OCO注文</b> ※注文レートと逆指値レートが設定されていればOCO取引となります。
458
+ # - <tt>:rate</tt> .. 注文レート(必須)
459
+ # - <tt>:stop_order_rate</tt> .. 逆指値レート(必須)
460
+ # - <tt>:expiration_type</tt> .. 有効期限。ClickClientScrap::FX::EXPIRATION_TYPE_TODAY等を指定します(必須)
461
+ # - <tt>:expiration_date</tt> .. 有効期限が「日付指定(ClickClientScrap::FX::EXPIRATION_TYPE_SPECIFIED)」の場合の有効期限をDateで指定します。(有効期限が「日付指定」の場合、必須)
462
+ #<b>戻り値</b>:: なし
463
+ #
464
+ def settle ( open_interest_id, unit, options={} )
465
+ if ( options[:rate] != nil && options[:stop_order_rate] != nil )
466
+ # レートと逆指値レートが指定されていればOCO取引
467
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
468
+ elsif ( options[:rate] != nil )
469
+ # レートが指定されていれば通常取引
470
+ raise "options[:execution_expression] is required." if options[:execution_expression] == nil
471
+ raise "options[:expiration_type] is required." if options[:expiration_type] == nil
472
+ else
473
+ # 成り行き
474
+ if ( options[:slippage] != nil )
475
+ raise "if you use a slippage, options[:slippage_base_rate] is required." if options[:slippage_base_rate] == nil
476
+ end
477
+ end
478
+
479
+ # 建玉一覧
480
+ result = link_click( "3" )
481
+
482
+ # 対象となる建玉をクリック
483
+ link = result.links.find {|l|
484
+ l.href =~ /[^"]*ORDERNO=([a-zA-Z0-9]*)[^"]*/ && $1 == open_interest_id
485
+ }
486
+ raise "illegal open_interest_id. open_interest_id=#{open_interest_id}" unless link
487
+ result = @client.click(link)
488
+
489
+ # 決済
490
+ form = result.forms.first
491
+ form["P100"] = "00" # 成り行き TODO 通常(01),OCO取引(21)対応
492
+ result = @client.submit(form)
493
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
494
+
495
+ # 設定
496
+ form = result.forms.first
497
+ form["L111"] = unit.to_s
498
+ form["P005"] = options[:slippage].to_s if options[:slippage]
499
+ result = @client.submit(form)
500
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
501
+
502
+ # 確認
503
+ form = result.forms.first
504
+ result = @client.submit(form)
505
+ ClickClientScrap::Client.error( result ) unless result.body.toutf8 =~ /完了/
506
+ end
507
+
508
+
391
509
  #
392
510
  #=== 注文一覧を取得します。
393
511
  #
394
512
  #order_condition_code:: 注文状況コード(必須)
395
513
  #currency_pair_code:: 通貨ペアコード
396
- #戻り値:: 注文番号をキーとするClickClient::FX::Orderのハッシュ。
514
+ #戻り値:: 注文番号をキーとするClickClientScrap::FX::Orderのハッシュ。
397
515
  #
398
- def list_orders( order_condition_code=ClickClient::FX::ORDER_CONDITION_ALL, currency_pair_code=nil )
399
-
400
- result = @client.click( @links.find {|i|
401
- i.attributes["accesskey"] == "2"
402
- })
516
+ def list_orders( order_condition_code=ClickClientScrap::FX::ORDER_CONDITION_ALL, currency_pair_code=nil )
517
+ result = link_click( "2" )
518
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
403
519
  form = result.forms.first
404
520
  form["P001"] = "" # TODO currency_pair_codeでの絞り込み
405
521
  form["P002"] = order_condition_code
406
- #puts result.body.toutf8
522
+ result = @client.submit(form)
523
+
407
524
  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 )
408
525
  tmp = {}
409
526
  list.each {|i|
410
527
  order_no = i[0]
411
528
  order_type = to_order_type_code(i[2])
412
- trade_type = i[3] == "新" ? ClickClient::FX::TRADE_TYPE_NEW : ClickClient::FX::TRADE_TYPE_SETTLEMENT
529
+ trade_type = i[3] == "新" ? ClickClientScrap::FX::TRADE_TYPE_NEW : ClickClientScrap::FX::TRADE_TYPE_SETTLEMENT
413
530
  pair = to_pair( i[1] )
414
- sell_or_buy = i[4] == "売" ? ClickClient::FX::SELL : ClickClient::FX::BUY
531
+ sell_or_buy = i[4] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
415
532
  count = pair == :ZARJPY ? i[5].to_i/10 : i[5].to_i
416
533
  rate = i[6].to_f
417
534
  execution_expression = if i[7] == "指"
418
- ClickClient::FX::EXECUTION_EXPRESSION_LIMIT_ORDER
535
+ ClickClientScrap::FX::EXECUTION_EXPRESSION_LIMIT_ORDER
419
536
  elsif i[7] == "逆"
420
- ClickClient::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER
537
+ ClickClientScrap::FX::EXECUTION_EXPRESSION_REVERSE_LIMIT_ORDER
421
538
  else
422
- ClickClient::FX::EXECUTION_EXPRESSION_MARKET_ORDER
539
+ ClickClientScrap::FX::EXECUTION_EXPRESSION_MARKET_ORDER
423
540
  end
424
541
  tmp[order_no] = Order.new( order_no, trade_type, order_type, execution_expression, sell_or_buy, pair, count, rate, i[8])
425
542
  }
426
543
  return tmp
427
544
  end
428
545
 
546
+ #
547
+ #=== 建玉一覧を取得します。
548
+ #
549
+ #currency_pair_code:: 通貨ペアコード
550
+ #戻り値:: 建玉IDをキーとするClickClientScrap::FX::OpenInterestのハッシュ。
551
+ #
552
+ def list_open_interests( currency_pair_code=nil )
553
+ result = link_click( "3" )
554
+ ClickClientScrap::Client.error( result ) if result.forms.empty?
555
+ form = result.forms.first
556
+ form["P001"] = "" # TODO currency_pair_codeでの絞り込み
557
+ result = @client.submit(form)
558
+
559
+ list = result.body.toutf8.scan( /<a href="[^"]*">([A-Z]{3}\/[A-Z]{3}):([^<]*)<\/a><br>[^;]*;<font[^>]*>([^<]*)<\/font>([\d\.]*)[^\s@]*@([\d\.]*).*?<font[^>]*>([^<]*)<\/font>/m )
560
+ tmp = {}
561
+ list.each {|i|
562
+ open_interest_id = i[1]
563
+ pair = to_pair( i[0] )
564
+ sell_or_buy = i[2] == "売" ? ClickClientScrap::FX::SELL : ClickClientScrap::FX::BUY
565
+ count = i[3].to_i
566
+ rate = i[4].to_f
567
+ profit_or_loss = i[5].to_i
568
+ tmp[open_interest_id] = OpenInterest.new(open_interest_id, pair, sell_or_buy, count, rate, profit_or_loss )
569
+ }
570
+ return tmp
571
+ end
429
572
 
430
573
  # ログアウトします。
431
574
  def logout
432
575
  @client.click( @links.find {|i|
433
576
  i.text == "\303\233\302\270\303\236\302\261\302\263\303\204"
434
- })
577
+ })
435
578
  end
436
579
 
437
580
  private
@@ -444,28 +587,42 @@ module ClickClient
444
587
  def to_order_type_code( order_type )
445
588
  return case order_type
446
589
  when "成行注文"
447
- ClickClient::FX::ORDER_TYPE_MARKET_ORDER
590
+ ClickClientScrap::FX::ORDER_TYPE_MARKET_ORDER
448
591
  when "通常注文"
449
- ClickClient::FX::ORDER_TYPE_NORMAL
592
+ ClickClientScrap::FX::ORDER_TYPE_NORMAL
450
593
  when "OCO注文"
451
- ClickClient::FX::ORDER_TYPE_OCO
594
+ ClickClientScrap::FX::ORDER_TYPE_OCO
452
595
  when "IFD注文"
453
- ClickClient::FX:: ORDER_TYPE_IFD
596
+ ClickClientScrap::FX::ORDER_TYPE_IFD
454
597
  when "IFD-OCO注文"
455
- ClickClient::FX:: ORDER_TYPE_IFD_OCO
598
+ ClickClientScrap::FX::ORDER_TYPE_IFD_OCO
456
599
  else
457
600
  raise "illegal order_type. order_type=#{order_type}"
458
601
  end
459
602
  end
603
+
604
+ def link_click( no )
605
+ link = @links.find {|i|
606
+ i.attributes["accesskey"] == no
607
+ }
608
+ raise "link isnot found. accesskey=#{no}" unless link
609
+ @client.click( link )
610
+ end
460
611
  end
461
612
 
613
+ # オプション
614
+ attr :options, true
615
+
462
616
  #=== スワップ
463
617
  Swap = Struct.new(:pair, :sell_swap, :buy_swap)
464
618
  #=== レート
465
619
  Rate = Struct.new(:pair, :bid_rate, :ask_rate, :sell_swap, :buy_swap )
466
620
  #===注文
467
621
  Order = Struct.new(:order_no, :trade_type, :order_type, :execution_expression, :sell_or_buy, :pair, :count, :rate, :order_state )
468
-
622
+ #===注文結果
623
+ OrderResult = Struct.new(:order_no )
624
+ #===建玉
625
+ OpenInterest = Struct.new(:open_interest_id, :pair, :sell_or_buy, :count, :rate, :profit_or_loss )
469
626
  end
470
627
  end
471
628
 
@@ -0,0 +1,130 @@
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: https://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
+ @client ||= ClickClientScrap::Client.new(
117
+ @props.key?(:proxy) ? @props[:proxy] : nil )
118
+ @session ||= @client.fx_session( @props[:user], @props[:password] )
119
+ rescue
120
+ @logger.error $!
121
+ raise $!
122
+ end
123
+ @session
124
+ end
125
+ end
126
+
127
+ JIJI::Plugin.register(
128
+ JIJI::Plugin::SecuritiesPlugin::FUTURE_NAME,
129
+ ClickSecuritiesPlugin.new )
130
+
@@ -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( :EURJPY, :sell, 1 )
65
+ # buy = plugin.order( :EURJPY, :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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unageanu-clickclient_scrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaya Yamauchi
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-08 00:00:00 -07:00
12
+ date: 2009-05-16 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies: []
15
-
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.0
24
+ version:
16
25
  description:
17
26
  email: y-masaya@red.hot.co.jp
18
27
  executables: []
@@ -25,6 +34,9 @@ files:
25
34
  - README
26
35
  - ChangeLog
27
36
  - lib/clickclient_scrap.rb
37
+ - lib/jiji_plugin.rb
38
+ - test/test_FxSession.rb
39
+ - test/test_jiji_plugin.rb
28
40
  has_rdoc: true
29
41
  homepage: http://github.com/unageanu/clickclient_scrap/tree/master
30
42
  post_install_message:
@@ -52,5 +64,6 @@ rubygems_version: 1.2.0
52
64
  signing_key:
53
65
  specification_version: 2
54
66
  summary: click securities client library for ruby.
55
- test_files: []
56
-
67
+ test_files:
68
+ - test/test_FxSession.rb
69
+ - test/test_jiji_plugin.rb