お金

自分が投資したい株式投資銘柄をスクリーニングツールから抽出→メール通知させてみた。手抜き感炸裂のseleniumとjenkinsで!

どーもヘタレエンジニア まる です。 最近スノーボードなどでお金を使いすぎていることもありちょっと財テクを日曜大工的にやってみよう第2弾です。

かんたんに言うと今回作ったのは単純に株のスクリーニングサイトを毎日朝にクロールして通知するだけのものです(多分もっとカンタンなやり方あったと思う・・・)

第1弾は監視カメラを作りました、興味があればそちらもどうぞ。

Raspberry Piを使った格安監視カメラシステムを作った!実家に帰省してます。今回は片手に「Raspberry Pi」を携えております・・・・・ 田舎の古民家に監視カメラを設置しよう 実は、祖...

株式投資の銘柄選びって時間かかりますよね

いやー株式投資を5年くらいだらだら放置しながら適当にやっているのですが、それでもちょろっと増えてます(年間5~10%位)「もっと上がるだろう!」とガチホして一気にマイナス突入みたいなことも何度もありました。

・・・あの時売っておけばと何万回繰り返したかと(遠い目)

せっかく本腰いれてやるのであればエンジニアらしくシステムトレード!としたいところですがそんな簡単にはいかんですよね。

大きい工程だけ考えても3つくらい考えないといけないのでもうこの時点でプライベートでやるにはお腹いっぱいになってきます・・・笑

  1. そもそもどの銘柄を買うのか?という「銘柄選定ロジック」
  2. いつ、どのタイミングで買うのかの「売買判断ロジック」
  3. 売買自動化プログラム

さすがに一気に登るとかなりツライのでまずは1の銘柄選定ロジックを作っていく事をMVP で出来るだけちっちゃくダッシュで出来るようにしてみようと思います。

銘柄選定ロジックを出来るだけちっちゃく試す!

今回はロジックを作り込む工程なので使うのはTrader’sさんのスクリーニングサイトを使います。かなり細かく項目を設定して銘柄のスクリーニングを出来ますのでこちらを利用させてもらいます。

実際は以下のように運用していきます

  1. ロジックを決めて朝イチにメール通知
  2. 会社に行く途中に注文
  3. 指値を入れておいて、超えたら売る(基本翌日に持ち越さない)
  4. 買った値+5%で利益確定
  5. 買った値-5%で損切り

実際のルールはこんな感じに設定してみた

これが正しい変わらないです、本やら・他のサイト様の情報などからいったん設定してみたものです。

  1. 東証1部上場銘柄のみ
  2. 25日乖離率がマイナス(乖離率が大きければ大きいほど良し)
  3. PER 15倍以下
  4. PBR 2倍以下

実際はこんな感じですね。

ポチッとすると結果が出てきますが、原則乖離率が大きいところが優先順位として高いのでソートします

今回はこの出力を毎朝6時にメールで飛んでくる感じにします

実際に飛んでくるのはこんな感じ

あさイチにポチポチブラウザでやらなくてもメールで画像キャプチャが送られてきて、勝手に買うべきものが分かる!という仕立てです。

ここで、今回は画像キャプチャにしてますが、この先システムトレードをしていくことなどを考えると文字情報をパースして送るほうが良いと思います。今回はとにかくちっちゃくはじめる為、楽してます。

・・・・ハイ、手抜きです!

実際に作ってみた

さて実際に作ってみましょう。

今回の構成は、さくらのクラウドにて建てたCentsOS7上でヘッドレスでSeleniumを実行、実行を設定をcronでやると色々面倒なのでjenkins先生にお願いする感じでやってみます。

サーバーOS:CentOS7 /  言語:Java(Maven3)

さくらのクラウドでCentOS7のインスタンスを建てる

いや、「AWSやGCP使えよ!このヘタレ!」とおっしゃる皆さん、分かりますよ!分かりますよ!SaaS使いやすいっすもんね。でもね、従量課金制だと本当にたまにびっくりするくらいお金が飛んでいってしまう事があるので不安な僕はさくらの安心定量課金の世界に住んでおります笑

https://cloud.sakura.ad.jp/payment/

各種導入作業(Java/jenkins)

#rootで入ってもろもろ作業を進めます ※今回面倒だから全部rootでやってます
ssh root@XXX.XXX.XXX.XXX -p 22


yum update -y
#Java8のインストール
yum install -y java-1.8.0-openjdk.x86_64

#Javaパスを通す
dirname $(readlink $(readlink $(which java)))

vi /etc/profile

##dirnameで表示されたパスでJAVA_HOME PATH CLASSPATHなどを設定
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
##

#再読み込み
source /etc/profile

java -version

#jenkinsをインストール
#公開鍵が必要
wget -O /etc/yum.repos.d/junkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
yum -y install jenkins

#ポートを8080だと後々Tomacatなど入れたくなったらポートが被るので8008に変えておく

vi /etc/sysconfig/jenkins
##
JENKINS_PORT="8008"
##


#jenkinsでシェル実行時にrootで実行できるようにしておく
vi /etc/sysconfig/jenkins
##
JENKINS_USER="jenkins" → JENKINS_USER="root
##

#再起動時の自動起動登録
systemctl enable jenkins

#jenkinsを動かす
systemctl start jenkins


#もろもろ後で必要になるポートを開けておく
#centos7からはiptableよりfirewall-cmdが主流こっちでやる
#http/jenkins/メールまわりのポートを恒久的に開ける
firewall-cmd --add-service=http --zone=public --permanent
firewall-cmd --add-port=8008/tcp --zone=public --permanent
firewall-cmd --permanent --zone=public --add-port=587/tcp
#リロード
firewall-cmd --reload

ここまでやったらいったんjenkinsが正しく起動出来るか確認します。

書かれているとおりに初期パスワードを確認して入力します

cat /var/lib/jenkins/secrets/initialAdminPassword

 

jenkinsが動いたら今回はGUI上でMavenの設定やヘッドレスでブラウザを使えるようにするためのプラグイン「Xvfb plugin」を導入する。これでモニタがなくてもブラウザ起動をしていることをシュミレート出来る。

jenkins > plugin

jenkins > Grobal Tool Configuration

ブラウザインストール

今回はFireFoxを使うので 公式に書いてあるとおりにインストールしておきましょう。

終わったらgecko driverも導入しましょう。これがないとFireFoxが立ち上がらないです。

wget https://github.com/mozilla/geckodriver/releases/download/v0.18.0/geckodriver-v0.18.0-linux64.tar.gz
tar -xvzf geckodriver*
mv geckodriver /usr/local/bin/

いよいよプログラムを書きましょう

やっとコードです笑

今回はseleniumとsimple java mail というライブラリ辺りを使います。

simple java mailイイっすね。JavaMailよりシンプルに書けるので気に入りました。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>kabu_get_data</groupId>
  <artifactId>kabu_get_data</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <name>kabu_get_data</name>
   <profiles>
      <profile>
          <activation>
              <jdk>1.8</jdk>
          </activation>
      </profile>
   </profiles>
  <build>
  	<finalName>kabu_get_data</finalName>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
  <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>2.4</version>
      <configuration>
          <descriptorRefs>
              <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
              <manifest>
                  <mainClass>com.blog.marublo.ExecKabuGetController</mainClass>
              </manifest>
          </archive>
      </configuration>
      <executions>
          <execution>
              <id>make-assembly</id>
              <phase>package</phase>
              <goals>
                  <goal>single</goal>
              </goals>
          </execution>
      </executions>
  </plugin>
    </plugins>
  </build>
    <dependencies>
   		<dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.14.0</version>
       </dependency>  
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-firefox-driver</artifactId>
          <version>3.14.0</version>	    
      </dependency>
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-io</artifactId>
          <version>1.3.2</version>
      </dependency>
     <dependency>
   	 <groupId>org.simplejavamail</groupId>
   	 <artifactId>simple-java-mail</artifactId>
    	 <version>4.4.4</version>
    </dependency>
  </dependencies>
</project>

jarにしちゃった、ま、いっか。

javaはこんな感じ、メールはgmailを使います。

package com.blog.marublo;

import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.support.ui.Select;
import org.simplejavamail.email.Email;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.Mailer;
import org.simplejavamail.mailer.config.TransportStrategy;


public class ExecKabuGetController {

  /**
   * @param args
   * @throws InterruptedException
   * @throws IOException
   */
  public static void main(String[] args) throws InterruptedException,
      IOException {

    
    try {
        //gecko driverの読み込み
        System.setProperty("webdriver.gecko.driver", "/usr/local/bin/geckodriver");


    } catch (Exception e) {
      e.printStackTrace();
    }

    FirefoxOptions options = new FirefoxOptions();
    WebDriver driver = new FirefoxDriver(options);
    driver.manage().window().maximize();
    
    driver.get("https://www.traders.co.jp/domestic_stocks/invest_tool/screening/screening_top.asp");
    
    Select dropdown = new Select(driver.findElement(By.id("market_1")));
    dropdown.selectByVisibleText("東証1部");
    
    driver.findElement(By.id("ritsu25_check")).click();
    driver.findElement(By.id("ritsu25_1")).sendKeys("0");
    dropdown = new Select(driver.findElement(By.id("ritsu25_2")));
    dropdown.selectByVisibleText("以下");
    driver.findElement(By.id("ritsu25_disp")).click();

    driver.findElement(By.id("per_check")).click();
    driver.findElement(By.id("per_1")).sendKeys("15");
    dropdown = new Select(driver.findElement(By.id("per_2")));
    dropdown.selectByVisibleText("以下");
    driver.findElement(By.id("per_disp")).click();
    
    driver.findElement(By.id("pbr_check")).click();
    driver.findElement(By.id("pbr_1")).sendKeys("2");
    dropdown = new Select(driver.findElement(By.id("pbr_2")));
    dropdown.selectByVisibleText("以下");
    driver.findElement(By.id("pbr_disp")).click();
    
    //親画面のハンドルを取得
    String currentHandle = driver.getWindowHandle();
    driver.findElement(By.cssSelector(".submit_btn")).click();
  
    //現在開いているウィンドウのハンドルを取得
    Set<String> windowList = driver.getWindowHandles();
    for (String window : windowList) {
        if (!currentHandle.equals(window)) {
            //親画面以外のhandleなら切替
            driver.switchTo().window(window);
        } else {
            driver.close();
        }
    }


    //元の画面スコープに戻す
    driver.switchTo().defaultContent(); 

    List<WebElement> elements = driver.findElements(By.className("sort"));
    elements.get(7).click();

    TakesScreenshot screen = (TakesScreenshot)driver;
        Email email = new EmailBuilder()
                .from("kabu", "yourmail@example.com")
                .to("kabu", "yourmail@example.com")
                .subject("本日の買うべき銘柄です")
                .addAttachment("invitation", screen.getScreenshotAs(OutputType.BYTES), "image/png")
                .text("本日の買うべき銘柄です")
                .textHTML("<b>本日の買うべき銘柄です</b>")
                .build();
        new Mailer("smtp.gmail.com", 587, "yourmail.@example.com", "your pass word",TransportStrategy.SMTP_TLS).sendMail(email);
    driver.quit();
        System.out.println("処理終了");	
  }
}

 

ここまで終わったらgithubの自分のプライベートリポジトリにpushしておきます

ビルド→実行の設定

面倒なのでビルド、実行を同じjobでまとめちゃいます。

新規job > フリースタイルプロジェクト

gitはgithubのリポジトリを設定、認証情報をprivateリポジトリにしているので設定すること

保存したあと、ビルドを実行したら動きます!

メール送信の部分でエラーが起きた場合、送信アカウントとして使うGmailアカウントの安全性が低いアプリからのアカウントへのアクセスを許可してください

果たしてうまくいくか?

まずは1週間位動かして売買してみます。

毎日の結果をまたブログに書いていきます!