clickclient_scrap 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|