MariaDB(MySQL)から取り出した株価からテクニカル指標を計算してチャートをJupyter NotebookとExcel両方で描いてみた。

まだ続きがありまして。

Haskellで株のシステムトレードプログラムを作る。(まだまだ途中)

 

こんなのが出来るようになったので、Excelファイルをおいておきます。

興味のある方はぜひ。

システムトレードプログラムの作り方の参考になるだろうと、Rubyではじめる本を買って読んだ。全体の半分ほどはスクレイピングでした。
自分もここで進捗50%まできたと思う。

まあ、システムトレードプログラムはExcelで充分なんだけどね、linuxサーバ上で動かせない問題があるけれど。
少し進めてGITHUBにあげておきました。

データーベース(MariaDB)投入

ローカルのCSVファイルで大量の情報を扱うのはつらいからね、データーベースが必要だろうと。この時Database.MySQLを使ったけれども、今回はDatabase.Persistを使います。(マイグレーション機能が便利なんで)

データーベースのモデルを決定

前に作った変なテーブルはすべて削除して、このように定義した。

引用元 Model.hs


DB.share [DB.mkPersist DB.sqlSettings, DB.mkMigrate "migrateQuotes"] [DB.persistLowerCase|
-- | 株式銘柄テーブル
Portfolio
    ticker      TickerSymbol        -- ^ ティッカー
    caption     Text Maybe          -- ^ 銘柄名
    updateAt    UTCTime Maybe       -- ^ 価格情報を取り込んだ日付時間
    deriving Show

-- | 初値, 高値, 安値, 終値, 出来高, 売買代金テーブル
Ohlcvt
    ticker      TickerSymbol        -- ^ ティッカー
    tf          TimeFrame           -- ^ 時間枠
    at          UTCTime             -- ^ 日付時間
    open        Double Maybe        -- ^ 初値
    high        Double Maybe        -- ^ 高値
    low         Double Maybe        -- ^ 安値
    close       Double Maybe        -- ^ 終値
    volume      Double Maybe        -- ^ 出来高
    turnover    Double Maybe        -- ^ 売買代金
    source      Text Maybe          -- ^ 情報の入手元
    deriving Show

-- | テクニカル指標テーブル
TechInds
    ohlcvt      OhlcvtId        -- ^ 紐付け
    ind         TechnicalInds   -- ^ テクニカル指標
    val         Double          -- ^ 値
    deriving Show
|]

こうしておくと勝手にportfolio, ohlcvt, tech_indsの3テーブルが作られる。

それぞれティッカーはここで。時間枠はここで。テクニカル指標はここで定義した。見てのとおりデータベースにある時間はUTC時間です、日本時間ではありません。

株価をWebから収集してMariaDBへ入れる

portfolioテーブルにある銘柄をいわゆるWebスクレイピングで収集する。Ruby本と違ってk-db.com様から。(配信ありがとうございます。ちょくちょくスマホから広告見てます。)

Webスクレイピングはここで。相手様のテンプレートエンジンに対応した物なので、詳細は説明しない。(自分の管理するサーバーを自分のスクレイピングプログラムで攻撃しながら確かめて欲しい。)
ここでは、この関数でやってます。runWebCrawlingOfPortfolios – StockQuotesCrawler.hs

スクレイピングは余り歓迎される物ではないので、今はXpathをゴニョゴニョしている雰囲気を感じてもらえらばいい。

集計処理

[box type=”warning”]コードの正確性の確認はしていませんので、あしからずご了承くださいませ。[/box]

収集処理で取ってきているのは1時間足(初回なら日足もついでに)なので、それを集計して日足を作り出す処理を作りました。

引用元 aggregateOfOHLCVT – Aggregate.hs


-- | 始値, 高値, 安値, 終値, 出来高, 売買代金を集計する関数
aggregateOfOHLCVT   :: (Ohlcvt -> Maybe Ohlcvt) -- ^ 値の判定関数
                    -> [Ohlcvt]                 -- ^ 時系列通りで与えること
                    -> Maybe Ohlcvt
aggregateOfOHLCVT _ [] = Nothing                -- 空リストの結果は未定義
aggregateOfOHLCVT decide serials@(first:_) =
    decide -- 集計結果が有効か無効かの判定をゆだねる。
        Ohlcvt
            { ohlcvtTicker      = ohlcvtTicker first
            , ohlcvtTf          = ohlcvtTf first
            , ohlcvtAt          = ohlcvtAt first
            -- 6本値は欠損値を許容する
            , ohlcvtOpen        = open  $ Mb.mapMaybe ohlcvtOpen serials
            , ohlcvtHigh        = high  $ Mb.mapMaybe ohlcvtHigh serials
            , ohlcvtLow         = low   $ Mb.mapMaybe ohlcvtLow serials
            , ohlcvtClose       = close $ Mb.mapMaybe ohlcvtClose serials
            , ohlcvtVolume      = vtSum $ Mb.mapMaybe ohlcvtVolume serials
            , ohlcvtTurnover    = vtSum $ Mb.mapMaybe ohlcvtTurnover serials
            -- 
            , ohlcvtSource      = T.append "agg from " <$> ohlcvtSource first
            }
    where
    -- | 集計期間中の初値
    open :: [Double] -> Maybe Double
    open  = Safe.headMay
    -- | 集計期間中の最高値
    high :: [Double] -> Maybe Double
    high  = Safe.maximumMay
    -- | 集計期間中の最安値
    low :: [Double] -> Maybe Double
    low   = Safe.minimumMay
    -- | 集計期間中の終値
    close :: [Double] -> Maybe Double
    close = Safe.headMay . reverse
    -- | 集計期間中の総和
    vtSum :: [Double] -> Maybe Double
    vtSum [] = Nothing
    vtSum xs = Just $ sum xs
初値(open)の定義
リストの先頭
高値(high)の定義
全要素の最高値
安値(low)の定義
全要素の最安値
終値(close)の定義
リストの最終
出来高(volume)及び売買代金(turnover)の定義
全要素の和

定義そのまま書いています。

テクニカル指標を作る

[box type=”warning”]コードの正確性の確認はしていませんので、あしからずご了承くださいませ。[/box]

portfolioテーブルにある銘柄を対象にテクニカル指標を計算する処理を作りました。

引用元 runAggregateOfPortfolios – Aggregate.hs


-- | Portfolio上の情報を集計する関数
runAggregateOfPortfolios :: M.MonadIO m => Conf.Info -> C.Source m TL.Text
runAggregateOfPortfolios conf = do
    -- 処理対象銘柄の数
    counts <- M.liftIO . runStderrLoggingT . MR.runResourceT . MySQL.withMySQLConn connInfo . MySQL.runSqlConn $ do
        DB.runMigration migrateQuotes
        DB.count ([] :: [DB.Filter Portfolio])

    C.yield . TB.toLazyText $
        case counts of
        0 -> "集計処理の対象銘柄がありません。"
        x -> "集計処理の対象銘柄は全部で" <> TB.decimal x <> "個有ります。"

    -- 処理対象銘柄のリストを得る
    ps  <- M.liftIO . runStderrLoggingT . MR.runResourceT . MySQL.withMySQLConn connInfo . MySQL.runSqlConn $ do
        DB.runMigration migrateQuotes
        map DB.entityVal <$> DB.selectList [] [DB.Asc PortfolioId]

    -- 銘柄毎に集計処理
    M.forM_ (zip ps [1..]) $ \(val, number) -> do
        let ticker = portfolioTicker val
        let textCaption = Mb.fromMaybe (T.pack . show $ portfolioTicker val) $ portfolioCaption val
        let progress=  TB.singleton '[' <> TB.decimal (number :: Int)
                    <> TB.singleton '/' <> TB.decimal counts
                    <> TB.singleton ']'
        C.yield . TB.toLazyText $ TB.fromText textCaption <> " を集計中。" <> progress
        M.liftIO $ do
            aggregate connInfo ticker
            -- 
            let calc = calculate connInfo ticker
            -- 
            calc TF1d (TISMA 5)
            calc TF1d (TISMA 10)
            calc TF1d (TISMA 25)
            calc TF1d (TISMA 75)
            -- 
            calc TF1d (TIEMA 5)
            calc TF1d (TIEMA 10)
            calc TF1d (TIEMA 25)
            calc TF1d (TIEMA 75)
            -- 
            calc TF1d (TIRSI 9)
            calc TF1d (TIRSI 14)
            -- 
            calc TF1d (TIMACD 12 26)
            calc TF1d (TIMACDSIG 12 26 9)
            -- 
            calc TF1d (TIPSYCHOLO 12)

    C.yield "集計処理を終了します。"
    where
    -- 
    connInfo :: MySQL.ConnectInfo
    connInfo =
        let mdb = Conf.mariaDB conf in
        MySQL.defaultConnectInfo
            { MySQL.connectHost = Conf.host mdb
            , MySQL.connectPort = Conf.port mdb
            , MySQL.connectUser = Conf.user mdb
            , MySQL.connectPassword = Conf.password mdb
            , MySQL.connectDatabase = Conf.database mdb
            }

上の関数から呼ばれる、この関数がテクニカル指標を作る関数を呼び出す。(今回は全て終値を使って計算する)

引用元 runAggregateOfPortfolios – Aggregate.hs


-- | 指定の指標を計算する
calcIndicator :: TechnicalInds -> [DB.Entity Ohlcvt] -> [(DB.Entity Ohlcvt, TechInds)]
calcIndicator indicator entities =
    case indicator of
    TISMA period                -> ohlcvtClose `apply` (TI.sma period)
    TIEMA period                -> ohlcvtClose `apply` (TI.ema period)
    TIRSI period                -> ohlcvtClose `apply` (TI.rsi period)
    TIMACD fastP slowP          -> ohlcvtClose `apply` (TI.macd fastP slowP)
    TIMACDSIG fastP slowP sigP  -> ohlcvtClose `apply` (TI.macdSignal fastP slowP sigP)
    TIPSYCHOLO period           -> ohlcvtClose `apply` (TI.psycologicalLine period)
    where
    apply :: (Ohlcvt -> Maybe Double) -> ([Double] -> [Maybe Double]) -> [(DB.Entity Ohlcvt, TechInds)]
    apply price formula =
        let entAndPrices = [(e,pr) | e<-entities, let (Just pr)=price $ DB.entityVal e] in
        let (es,ps) = unzip entAndPrices in
        let entAndValues = zip es $ formula ps in
        [(e, makeTI e v) | (e, Just v)<-entAndValues]
    -- 
    makeTI e v = TechInds
                    { techIndsOhlcvt    = DB.entityKey e
                    , techIndsInd       = indicator
                    , techIndsVal       = v
                    }

単純移動平均(SMA)

テクニカル分析ABC > 第3回 移動平均をよく読んでSMA関数を作る。

引用元 sma – TechnicalIndicators.hs


-- | 単純移動平均(SMA)
--   リストの先頭から計算する版
sma :: Int -> [Double] -> [Maybe Double]
sma period prices
    | period < 1 = error "期間は1以上でね"
    | length prices < period = replicate (length prices) Nothing
    | otherwise =
        let (seeds, ps) = splitAt period prices in
        let na = replicate (period-1) Nothing in
        let val = snd $ sma' period (reverse seeds) ps in
        na ++ map Just val

type SmaAccumlator = [Double]
-- | 単純移動平均(SMA)
--   初期のアキュムレータを入力と別々に渡して計算する版
sma'    :: Int
        -> SmaAccumlator                -- ^| 最新の値が先頭の並びで渡す事
        -> [Double]                     -- ^| 入力は時系列通りで渡す事
        -> (SmaAccumlator, [Double])    -- ^| 計算を打ち切った時のアキュムレータの値と結果のタプル
sma' period seeds prices
    | period < 1               = error "期間は1以上でね"
    | length seeds /= period   = error "アキュムレータの数と周期が一致していないよ"
    | otherwise =
        let (acc, xs) = List.mapAccumL go seeds prices in
        let lst = snd $ go acc 0.0 in  -- 最後にアキュムレータに残った値を押し出す
        ( acc
        , xs ++ [lst]
        )
    where
    go :: SmaAccumlator -> Double -> (SmaAccumlator, Double)
    go acc price =  ( price : init acc
                    , sum acc / realToFrac period
                    )

mapAccum関数によってアキュムレータを持ち回りながら(参照透明性を確保するために)
sum acc / realToFrac period
これを計算するということ。定義通りです。

サイコロジカル・ライン

テクニカル分析ABC > 第7回 サイコロジカル・ラインをよく読んでサイコロジカル・ライン関数を作る。

引用元 psycologicalLine – TechnicalIndicators.hs


-- | サイコロジカルライン
--   リストの先頭から計算する版
psycologicalLine :: Int -> [Double] -> [Maybe Double]
psycologicalLine period prices
    | period < 1 = error "期間は1以上でね"
    | length prices <= period = replicate (length prices) Nothing
    | otherwise =
        let (seeds, ps) = splitAt (period+1) prices in
        let diffs = zipWith (-) (drop 1 seeds) seeds in
        let seed = PsycoloAccumlator
                    { psycoloDiffs      = reverse diffs
                    , psycoloLastPrice  = Safe.lastNote "たぶんこの例外は発生しないかな" seeds
                    }
        in
        let val = snd $ psycologicalLine' period seed ps in
        let na = replicate period Nothing in
        na ++ map Just val

data PsycoloAccumlator = PsycoloAccumlator
    { psycoloDiffs      :: [Double]
    , psycoloLastPrice  :: Double
    } deriving (Show, Eq, Ord)
-- | サイコロジカルライン
--   初期のアキュムレータを入力と別々に渡して計算する版
psycologicalLine'   :: Int
                    -> PsycoloAccumlator                -- ^| 最新の値が先頭の並びで渡す事
                    -> [Double]                         -- ^| 入力は時系列通りで渡す事
                    -> (PsycoloAccumlator, [Double])    -- ^| 計算を打ち切った時のアキュムレータの値と結果のタプル
psycologicalLine' period seed prices
    | period < 1 = error "期間は1以上でね"
    | otherwise =
        let (acc, xs) = List.mapAccumL go seed prices in
        let lst = snd $ go acc 0.0 in  -- 最後にアキュムレータに残った値を押し出す
        ( acc
        , xs ++ [lst]
        )
    where
    -- 
    go :: PsycoloAccumlator -> Double -> (PsycoloAccumlator, Double)
    go (PsycoloAccumlator diffs lastPrice) price =
        let t = PsycoloAccumlator
                    { psycoloDiffs      = (price - lastPrice) : init diffs
                    , psycoloLastPrice  = price
                    }
        in
        ( t
        , sum [1 | x<-diffs, x>=0] / realToFrac period * 100.0
        )

サイコロジカル・ライン= 12日間の内上昇した日 ÷ 12 × 100(%)
テクニカル分析ABC > 第7回 サイコロジカル・ライン

mapAccum関数によってアキュムレータを持ち回りながら(参照透明性を確保するために)
sum [1 | x<-diffs, x>=0] / realToFrac period * 100.0
これを計算するということ。定義通りです。

[1 | x<-diffs, x>=0] これは上がった日は1、下がった日はリストから除かれる(つまり後のsum計算に入らない)という意味。

RSI(Relative Strength Index)

テクニカル分析ABC > 第8回 RSI(Relative Strength Index)をよく読んでRSI関数を作る。

引用元 rsi – TechnicalIndicators.hs


-- | 相対力指数(RSI)
--   リストの先頭から計算する版
rsi :: Int -> [Double] -> [Maybe Double]
rsi period prices
    | period < 1 = error "期間は1以上でね"
    | length prices <= period = replicate (length prices) Nothing
    | otherwise =
        let (seeds, ps) = splitAt (period+1) prices in
        let diffs = zipWith (-) (drop 1 seeds) seeds in
        let seed = RsiAccumlator
                    { rsiAscend     = sum [abs v | v<-diffs, v >= 0] / realToFrac period
                    , rsiDescend    = sum [abs v | v<-diffs, v < 0] / realToFrac period
                    , rsiLastPrice  = Safe.lastNote "たぶんこの例外は発生しないかな" seeds
                    }
        in
        let val = snd $ rsi' period seed ps in
        let na = replicate period Nothing in
        na ++ map Just val

data RsiAccumlator = RsiAccumlator
    { rsiAscend     :: Double
    , rsiDescend    :: Double
    , rsiLastPrice  :: Double
    } deriving (Show, Eq, Ord)
-- | 相対力指数(RSI)
--   初期のアキュムレータを入力と別々に渡して計算する版
rsi'    :: Int
        -> RsiAccumlator
        -> [Double]
        -> (RsiAccumlator, [Double])    -- ^| 計算を打ち切った時のアキュムレータの値と結果のタプル
rsi' period seed prices
    | period < 1 = error "期間は1以上でね"
    | otherwise =
        let (acc, xs) = List.mapAccumL go seed prices in
        let lst = snd $ go acc 0.0 in  -- 最後にアキュムレータに残った値を押し出す
        ( acc
        , xs ++ [lst]
        )
    where
    -- 
    go :: RsiAccumlator -> Double -> (RsiAccumlator, Double)
    go acc price =
        let a = rsiAscend acc * realToFrac (period - 1) in
        let d = rsiDescend acc * realToFrac (period - 1) in
        let t = case price - rsiLastPrice acc of
                x | x >= 0      -> RsiAccumlator
                                    { rsiAscend     = (a + abs x) / realToFrac period
                                    , rsiDescend    = d / realToFrac period
                                    , rsiLastPrice  = price
                                    }
                  | otherwise   -> RsiAccumlator
                                    { rsiAscend     = a / realToFrac period
                                    , rsiDescend    = (d + abs x) / realToFrac period
                                    , rsiLastPrice  = price
                                    }
        in
        ( t
        , rsiAscend acc / (rsiAscend acc + rsiDescend acc) * 100.0
        )

最初に14日間RSIを求める式(公式1)

2日目以降の14日間RSIを求める式(公式2)
テクニカル分析ABC > 第8回 RSI(Relative Strength Index)

mapAccum関数によってアキュムレータを持ち回りながら(参照透明性を確保するために)
rsiAscend acc / (rsiAscend acc + rsiDescend acc) * 100.0
これを計算するということ。定義通りです。

最初は(公式1)

rsiAscend     = sum [abs v | v<-diffs, v >= 0] / realToFrac period
rsiDescend    = sum [abs v | v<-diffs, v < 0] / realToFrac period

次回からは(公式2)

        let t = case price - rsiLastPrice acc of
                x | x >= 0      -> RsiAccumlator
                                    { rsiAscend     = (a + abs x) / realToFrac period
                                    , rsiDescend    = d / realToFrac period
                                    , rsiLastPrice  = price
                                    }
                  | otherwise   -> RsiAccumlator
                                    { rsiAscend     = a / realToFrac period
                                    , rsiDescend    = (d + abs x) / realToFrac period
                                    , rsiLastPrice  = price
                                    }
% stack ghci
*Main Aggregate Conf DataBase Lib Model Scraper SinkSlack StockQuotesCrawler TechnicalIndicators TickerSymbol TimeFrame WebBot> TechnicalIndicators.rsi 14 [1000, 1020, 1010, 1030, 1040, 1050, 1080, 1070, 1050, 1090, 1100, 1120, 1110, 1120, 1100, 1080]
[Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Just 70.83333333333334,Just 65.0]

70.83, 65.0, 合っているんじゃないかな。

MACD(Moving Average Convergence and Divergence)

テクニカル分析ABC > 第13回 MACD(Moving Average Convergence and Divergence)をよく読んでMACD関数を作る。
まず、MACD関数の前に指数平滑移動平均(EMA)関数を用意する。

引用元 ema – TechnicalIndicators.hs


-- | 指数平滑移動平均(EMA)
--   リストの先頭から計算する版
ema :: Int -> [Double] -> [Maybe Double]
ema period prices
    | period < 1 = error "期間は1以上でね"
    | length prices < period = replicate (length prices) Nothing
    | otherwise =
        let (seeds, ps) = splitAt period prices in
        let seed = sum seeds / realToFrac period in
        let na = replicate (period-1) Nothing in
        let val = ema' period seed ps in
        na ++ map Just val

-- | 指数平滑移動平均(EMA)
--   種を入力と別々に渡して計算する版
ema' :: Int -> Double -> [Double] -> [Double]
ema' period seed prices
    | period < 1 = error "期間は1以上でね"
    | otherwise =
        scanl formula seed prices
    where
    -- 
    alpha = 2.0 / realToFrac (period + 1)
    -- 
    formula :: Double -> Double -> Double
    formula yesterdayEMA todayPrice =
        yesterdayEMA + alpha * (todayPrice - yesterdayEMA)

EMAを求める式は以下の通り。
または テクニカル分析ABC > 第13回 MACD(Moving Average Convergence and Divergence)

移動平均(MA)といいつつこれは移動平均ではないので、scan関数によって

    -- 
    alpha = 2.0 / realToFrac (period + 1)
    -- 
    formula :: Double -> Double -> Double
    formula yesterdayEMA todayPrice =
        yesterdayEMA + alpha * (todayPrice - yesterdayEMA)

これを計算するということ。定義通りです。

短期のEMAと長期のEMAを求めた後にMACDは以下の式で求めます。
テクニカル分析ABC > 第13回 MACD(Moving Average Convergence and Divergence)

引用元 macd – TechnicalIndicators.hs


-- | MACD
--   リストの先頭から計算する版
macd :: Int -> Int -> [Double] -> [Maybe Double]
macd fastPeriod slowPeriod prices =
    zipWith (M.liftM2 formula) fast slow
    where
    fast = ema fastPeriod prices
    slow = ema slowPeriod prices
    formula f s = f - s
fast
短期EMA
slow
長期EMA
formula
短期EMA – 長期EMA

迷いようがないくらいの定義通り。こんな関数を書いていると、関数型言語の関数っていう物は(数学の)関数なんだなと思う。

MACDシグナル(MACDの単純移動平均)

引用元 macdSignal – TechnicalIndicators.hs


-- | MACD signal
--   リストの先頭から計算する版
macdSignal :: Int -> Int -> Int -> [Double] -> [Maybe Double]
macdSignal fastPeriod slowPeriod signalPeriod prices =
    let (na, mbvs) = span Mb.isNothing thisMACD in
    let vs = map (Safe.fromJustNote "SMAの定義により、中間にNothingは無いはず") mbvs in
    na ++ sma signalPeriod vs
    where
    thisMACD = macd fastPeriod slowPeriod prices

上で定義したMACDとSMAを使って定義した。

夕方のバッチ処理

立ち会いが終了して、1時間足が配信されてから収集して集計してテクニカル指標を作る夕方のバッチ処理を用意した。

本来はこの時間に開始するようになっているけれども、利便性のために強制的に外部からバッチ処理エンジンを駆動できるようにした。

使い方

githubからダウンロードして conf.jsonとかの名前で設定ファイルを用意する。(*の所は自分の物を入れておく)

{
    "recordAssetsInterval"  : 10,
    "sendReportInterval"    : 10,
    "loginURL"              : "https://www.deal.matsui.co.jp/ITS/login/MemberLogin.jsp",
    "loginID"               : "********",
    "loginPassword"         : "********",
    "dealingsPassword"      : "*******",
    "userAgent"             : "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0",
    "slack" : {
        "webHookURL"    : "*****************************************************************************",
        "channel"       : "#*******",
        "userName"      : "********"
    },
    "mariaDB" : {
        "host"      : "localhost",
        "port"      : 3306,
        "user"      : "********",
        "password"  : "********",
        "database"  : "stockdb"
    }
}

それからシェルで

% stack build
% stack install
% tractor --batch

--batch付きで強制的に外部からバッチ処理エンジンを駆動する。(立ち会い時間中に動かすと、今日の確定していない株価がデーターベースに入るから注意)

初回起動時はこれでテーブルを作って終了する。(mariaDB上のデーターベースはすでにある物とする。ここでは”stockdb”が有る物とする)

テーブルが出来たら”portfolio”テーブルのtickerに、例えば”TSNI225″とかを入れる。これは日経平均株価のティッカーとここで定義した。他はNULL値でOK

% tractor --batch

とすると今度は収集と集計とテクニカル指標を作る作業を開始するので終わるまで待つとデーターベースに結果が入る。

mariaDBサーバーにアクセスして結果を取り出す

rlogin portforwarding
Rlogin のポートフォワード設定

サーバーのport3306(MySQL / MariaDB)は開けてないので、Rloginでポートフォワードして接続します。

 

HeidiSQL - setup
HeidiSQLの設定

mariaDBクライアントはHeidiSQLをつかっているので、こう設定してRloginでフォワードしたローカルのポートごくろうさんにつなげばリモート側の3306とつながる。

ohlcvt table
OHLCVTテーブル
tech_inds table
テクニカル指標テーブル
portfolio table
portfolioテーブル

Excelでチャートにする

生のデータを見たところで、何の感想もわかない。
ということでODBC接続でMariaDBとつないだExcelでチャートを作る。(ExcelでMariaDBにつなぐ方法は検索してください、すぐに出てきますので。)

はじめにおいたExcelを開くと

ni225 and technical indicator
日経平均株価とテクニカル指標のチャート

日経平均株価とテクニカル指標のチャートとが見える。
ここでODBC接続が有効なら全て更新をクリックするとデーターベースから最新をとり出してくれる。

ni225 and technical
日経平均株価の株価とテクニカル指標

NI225タブをクリックすると テクニカル指標はこんな風になっている
左外部結合(LEFT OUTER JOIN)だから欠損値も見える。
では証拠を

query
JoinKind.LeftOuterとなっているのを確認してください。

この部分で”agg from “になっているのがこのプログラムによって集計されたデータ、そうでないものは配信元データという意味

source
source列
400%
400%で見た

結論

400%に拡大して、このプログラムで計算したSMA10とSMA25をExcelで計算した物とグラフで一致しているから合っているんじゃないかな。(てきとうに目視確認済)

2017/8/12追記

Jupyter Notebookでローソク足とテクニカル指標をチャートに描いてみた

詳細はGITHUB上のJupyter Notebook (ipynb) ファイルを見てもらえたらわかると思いますが、チャートが見にくいのでここに置いておきます。

ローソク足チャート

candlestick-chart
三菱UFJのローソク足チャート

ローソク足チャート & 単純移動平均線

単純移動平均線

ローソク足チャート & 指数平滑移動平均線

指数平滑移動平均線

ローソク足チャート & ボリンジャーバンド

ボリンジャーバンド

RSI&サイコロジカルライン

RSI&サイコロジカルライン

MACD&MACDシグナル

MACD&MACDシグナル

DMI&ADX

DMI&ADX

ガタガタしているのは、データが無い休日もチャートに表されているためなんだろうね。

One thought on “MariaDB(MySQL)から取り出した株価からテクニカル指標を計算してチャートをJupyter NotebookとExcel両方で描いてみた。

コメントを残す

メールアドレスが公開されることはありません。