Sikuli - 為替レートの変化を常時監視

SikuliのOCR機能と状態変化を検知する機能を使って、画面に表示されている為替レートの変化を常時監視してみます。

まずOCRに関しては、Regionクラスのtextメソッドで数字くらいなら簡単に認識してくれます。

画面変化を検知するのは少し難しいのですが、変化検知したときに動作する関数をRegionに設定しておく感じとなります。
今回はhandler1関数を定義し、OCRで取得した数字をエディタに貼り付けてみます。
Regionへのイベントハンドラ追加にはonChangeメソッドを使い、第1引数にはどれだけ変化があったらイベントハンドラを呼び出すかを指定します。今回は10を指定したので10ピクセル以上変化したらイベントハンドラがコールされます。

状態検知を有効にするためにはobserve関数を使うのですが、この関数を使うと処理が止まってしまうので通常はobserveInBackground関数を使いバックグラウンドで状態検知を有効にします。

また、処理を終了するときにはstopObserver関数を使い、状態検知を無効にします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# coding:utf-8
def handler1(event):
app.focus()
paste(r.text() + ' ')

# 変化値を貼り付けるエディタ
app = App('MKEditor')

# ユーザに監視するエリアを指定させる
r = selectRegion()

r.onChange(10, handler1)
r.observeInBackground(FOREVER)

wait(90)

r.stopObserver()
print('finished!')

この処理の動きは下記の動画で確認していますので、参考にして頂ければ幸いです。



OCRと状態変化検知を使うと、投資対象の為替レートを常時監視ができるようになり、例えば一定時間にある程度上昇したらその上昇に合わせて順張り投資するといったことが可能になります。

パラダイムシフトの時のような急激な一方方向へのトレンドがある場合は、順張り+建玉追加投資できるとかなりの利益を上げることができますが、そんなトレンドを人が張り付いて確認し続けるのは大変なので今回実装したようなRPAでの処理をうまく活用したいものです。

Sikuli - FX(デモ版)の注文を自動化

Sikuliを使ってFXの自動売買を行う処理を実装してみました。
ソースと実際の動きを動画にしましたのでよかったら参考にして下さい。
(ソース上のログインユーザ名やパスワードはさすがに隠してます。)

ソースの内容としては、waitやclick,paste,typeといった基本的なメソッドしか使っていませんがこのくらいのGUI操作なら十分自動化できます。
ただもっとエラー処理など気を付けて実装する必要があると思いますが。。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Operaブラウザを起動
app = App(r'C:\xxxxx\xxxxx\Opera\launcher.exe')
app.open()

# Operaの起動を待って、ログインページに移動
wait("1580206648525.png")
paste('https://demotrade.fx.dmm.com/fxcmdipresen/webrich/direct/login')
wait(0.1)
type(Key.ENTER)

# ログインページのロードを待ってログイン
wait("1580206818170.png",20)
click(Pattern("1580206853211.png").targetOffset(-97,123))
wait(0.1)
paste('[ユーザ名]')
wait(0.1)
type(Key.TAB)
wait(0.1)
paste('[パスワード]')
wait(0.1)
type(Key.TAB)
wait(0.1)
type(Key.ENTER)

# DMMサイトのロードを待って自動注文
wait("1580207030297.png", 20)
click(Pattern("1580213297947.png").targetOffset(89,41))
click("1580207967756.png")
click("1580213354520.png")

# ログアウト
click("1580213395792.png")
click(Pattern("1580213422219.png").exact())

# Opera閉じる
click(Pattern("1580214355504.png").targetOffset(21,-18))

popup(u'終了しました。')

ソース内で参照している画像



最初の実装サンプルではこのくらいでもいいかと思いますが、レートが大きく動いているときに順張りするとか、AIで状況を判断して売買するなどいろいろ作りこんでいくと夢のあるシステムができると思います。

Sikuli - 起動しているInternet Exploreを全て終了する方法

起動しているIEを全て終了する必要があったので実装してみました。

まずAppクラスにIEのウィンドウタイトルを設定しAppインスタンスを作成します。
(ウィンドウタイトルは一部が合致していればAppインスタンスを作成してくれるようです。)

そして生成したAppインスタンスのisRunningメソッドをコールしてIEが起動しているかどうかをチェックします。

IEが起動している場合は、closeByKeyメソッドで順次IEを終了させる・・・・といった感じで簡単に実装できるはずでした。。。

1
2
3
4
5
6
7
# coding:utf-8
chkApp = App(u"Internet")
while chkApp.isRunning():
chkApp.focus()
#chkApp.closeByKey() # 終了しないことがある
#chkApp.close() # 次回のIE起動時にクラッシュしたと表示される
type('w', Key.CTRL) # 比較的平和に全て終了する

ここからいろいろ問題が発生します。

まずcloseByKeyメソッドだとIEが終了せず無限ループになることが分かりました。原因は分からなかったのですが、IEが複数ウィンドウ起動していると1つめのウィンドウが落ちてその次のウィンドウが落ちないという現象です。

仕方ないのでcloseメソッドを使うことにしました。この方法ですと問題なく全てのIEウィンドウが閉じるのですが、次回IEを起動したときに「前回のブラウズセッションは予期せずに終了しました」と表示されてしまいます。

最終的には[CTRL + w]というショートカットキーを使ってIEを終了することにしました。
この方法であればIEのウィンドウは問題なく全て閉じますし、気持ち悪いメッセージが表示されることもありません。めでたし、めでたし。

Sikuli - 複数個所を連続して選択する

Regionオブジェクトのfindメソッドを使うと、引数に指定した画像をRegionオブジェクトの内部から探し最も合致した一か所を返します。

findAllメソッドを使うと合致した全ての箇所をリストとして返します。
このfindAllメソッドを使うとチェックボックスをすべてクリックするといった操作を行うことができます。

1
2
3
4
5
r = Region(1622,365,220,125)
list = r.findAll('checkbox.png')

for chk in list:
click(chk)

Sikuli - マウスホイールでスクロールする

Sikuliではマウスホイールでスクロールするための関数 wheel が用意されています。

wheel関数の引数は3つあります、

  • Regionオブジェクト
  • マウスホイールのアップ・ダウン
  • 移動数

以下の例ではフォーカスのあるウィンドウでマウスホイールを5回分ダウンさせたあとに、5回分アップさせます。

1
2
3
4
5
r = App.focusedWindow()

wheel(r, WHEEL_DOWN, 5)

wheel(r, WHEEL_UP, 5)

実際の動作としてはRegionオブジェクトの中心位置にマウスが移動し(クリックはされない)、マウスホイールを回転させる動作が実行されます。

Sikuli - 幅や高さが等間隔なものに入力する

Sikuliで幅と高さが等間隔の場合のデータ入力ではsetRasterメソッドを使うと便利です。

まずはfindメソッドで入力エリアのRegionを指定します。
そのRegionに対してsetRasterメソッドを使うと等間隔に分割することができます。

分割したエリアにはgetCell関数に行番号と列番号を指定しそれぞれのセルにアクセスすることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
# coding:utf-8
data = [[1, 2, 3, 4],
[10, 20, 30, 40]]

cells =find("1579840049307.png")
#4行3列に分割
cells.setRaster(3, 4)

for y, row in enumerate(data):
for x, col in enumerate(row):
# getCellメソッドで各セルに値を貼り付ける
paste(cells.getCell(y + 1, x), str(col))
wait(0.3)

5行目に指定した画像は下記です。

1579840049307.png

実行してみるとカーソルが1セルずつ選択を行い、データが入力されていきます。

Sikuli - ダイアログの表示位置を設定する

ダイアログはデフォルトで画面中央に表示されますが、popatメソッドを使用するとダイアログの表示位置を変更することができます。

popatメソッドの引数にx,y座標を指定することができるとのことですが私の環境ではエラーになってしまいました。
動作確認ができたLocation指定のコードは下記の通りです。

1
2
popat(Location(1511, 255))
popup('test')

LocationはSikuli IDEのツールバーで指定できますのでこの方法がおすすめです。

Sikuli - スクリーンショットをとる

Sikuliでスクリーンショットをとるにはcaptureメソッドを使います。
captureメソッドの返値には画像ファイルのフルパスが返ってくるので、必要に応じてリネームします。

(1) フォーカスのあるウィンドウだけスクリーンショットをとる場合

1
2
filename = capture(App.focusedWindow())
print('file1', filename)

(2) 画面全体のスクリーンショットをとる場合

1
2
img = SCREEN.capture()
print('file2', img.filename)

(3) ユーザが選択したエリアのスクリーンショットをとる場合

1
2
filename = capture()
print('file3', filename)

Sikuli - マウス移動の高速化

実際にSikuliでの動作を確認すると気づきますが、マウスポインタの動きは低速です。
(ロボットが頑張って動いている感じがしてやや面白いのですが。。)

これはマウスポインタの移動に対して、ウェイトがかかっているためです。
このウェイトは Settings.MoveMouseDelay で設定できます。

1
Settings.MoveMouseDelay = 0

上記のように0を設定すると、マウスポインタが瞬間移動するようになり処理を高速化させることができます。

Sikuli - ショートカットキーの設定

Sikuliにはショートカットキーを押したときに処理を実行する機能があります。

ショートカットキーでコードを実行するには、あらかじめキーが押されたときに実行したい関数を定義しておきます。引数にはHotkeyEventオブジェクトを設定します。

1
2
3
4
5
6
def hotKey(event):
r1 = App.focusedWindow()
paste(r1, 'test paste')

Env.addHotkey('k', KeyModifier.ALT, hotKey)
popup(u'[alt] + [k]で貼り付け')

HotkeyEventオブジェクトのプロパティは下記の通りです。

プロパティ名 解説
keyCode 入力されたキーのキーコード
modifiers 入力された修飾キー(Shift、Ctrl、Altなど)

ショートカットキーに関数を設定するためには、EnvクラスのaddHotkeyメソッドを使用します。
また、設定したショートカットキーを解除するためには、EnvクラスのremoveHotkeyメソッドを使います。

処理が終了してしまうとショートカットキーが解除されてしまうので、今回はポップアップメッセージを表示して処理を終了しないようにしています。