LoginSignup
10

More than 3 years have passed since last update.

[python3] matplotlibでパラボリックSARを描画

Last updated at Posted at 2019-02-02

対象者

  • 為替や株、仮想通貨などのデータは自力で取得できる
    (始値、高値、安値、終値を含むデータ)
  • pandas の dataframe は自分で勉強する
  • でも、パラボリックの計算式が難しすぎてソースコードに落とせない....

そんな方のための記事です
いい情報源がなかったので掲載してみました

前提

こんな状態のdataframeを用意しておいて下さい。

chart.py
class FXBase():
    candles = None

# 頑張ってデータを取得して、FXBase.candlesに代入する
...

# 表示
FXBase.candles.head(10)

2019-02-02_15h36_03.png

※ 私が用意したのは5分足のデータですが、別に5分足である必要はありません
※ len(FXBase.candles) > 200 を推奨します

計算式

難しすぎるので私が書くのは今のところやめておきます。。。。
計算式は、以下のサイトを参考にしました。

ソースコード - ver1

パラボリック計算処理

parabolic.py
import pandas as pd
import seaborn
import matplotlib.pyplot as plt
import mplfinance.original_flavor as mpf

class Parabolic():
    INITIAL_AF = 0.02
    MAX_AF = 0.2

    def __parabolic_is_touched(self, bull, current_parabo, current_h, current_l):
        if bull and (current_parabo > current_l):
            return True
        elif not bull and (current_parabo < current_h):
            return True
        return False

    def re_calc(self):
        # 初期状態は上昇トレンドと仮定して計算
        bull = True
        acceleration_factor = Parabolic.INITIAL_AF
        extreme_price = FXBase.candles['high'][0]
        temp_sar_array = [FXBase.candles['low'][0]]

        for i, row in FXBase.candles.iterrows():
            current_high = FXBase.candles['high'][i]
            current_low  = FXBase.candles['low'][i]

            # レートがparabolicに触れたときの処理
            if self.__parabolic_is_touched(
                bull=bull, 
                current_parabo=temp_sar_array[-1],
                current_h=current_high, current_l=current_low
            ):
                temp_sar = extreme_price
                acceleration_factor = Parabolic.INITIAL_AF
                if bull:
                    bull = False
                    extreme_price = current_low
                else:
                    bull = True
                    extreme_price = current_high

            else:
                if bull and extreme_price < current_high:
                    extreme_price = current_high
                    acceleration_factor = min(acceleration_factor + Parabolic.INITIAL_AF, Parabolic.MAX_AF)
                elif not bull and extreme_price > current_low:
                    extreme_price = current_low
                    acceleration_factor = min(acceleration_factor + Parabolic.INITIAL_AF, Parabolic.MAX_AF)
                temp_sar = temp_sar_array[-1] + acceleration_factor * (extreme_price - temp_sar_array[-1])

            if i == 0:
                temp_sar_array[-1] = temp_sar
            else:
                temp_sar_array.append(temp_sar)
        return temp_sar_array

計算処理の呼び出しと描画

parabolic.py(続き)
# 計算
parabo = Parabolic()
SARs = parabo.re_calc()

# チャートの描画
figure, (axis1) = plt.subplots(nrows=1, ncols=1, figsize=(10,5), dpi=200)
mpf.candlestick2_ohlc(
    axis1,
    opens  = FXBase.candles.open.values,
    highs  = FXBase.candles.high.values,
    lows   = FXBase.candles.low.values,
    closes = FXBase.candles.close.values,
    width=0.6, colorup='#77d879', colordown='#db3f3f'
)
plot1 = axis1.scatter(FXBase.candles.index, SARs[:len(FXBase.candles)], marker='o', color='purple', s=10)

# グラフの見た目を整形
### X軸の見た目を整える
xticks_number  = 12 # 12本(60分)刻みに目盛りを書く 5分足だからこの設定にしてるよ
xticks_index   = range(0, len(FXBase.candles), xticks_number)
xticks_display = [FXBase.candles.time.values[i][11:16] for i in xticks_index] # 時間を切り出すため、先頭12文字目から取る

# axis1を装飾
plt.sca(axis1)
plt.xticks( xticks_index, xticks_display )
plt.legend([plot1], ['parabolic'], loc='upper right')
plt.show()

完成品

2019-02-02_15h45_21.png
これでも、MT4で生成されるparabolicSARと大体一致するパラボリックを描画可能です

ソースコード - ver2 (20191013追記)

ver1だと、MT4と食い違う場合もちょこちょこ出てくることに、最近気が付きました。
というわけで、チャートとにらめっこしたり、下記参考サイトを見ながら修正した。

パラボリックSARの計算方法とZigZagの作成・詳細解説
この記事を参考に、「5. 仮計算したSARの調整」まで加味して実装したものが以下のソースコード。
※「6. SARの反転処理・その2」は実装できていない...

  • 事前準備
    まず、この状態のチャートデータを用意しておく。 image.png
# parabolicSAR生成
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
#                   Parabolic SAR                     #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
INITIAL_AF = 0.02
MAX_AF = 0.2

def calc_next_parabolic(last_sar, ep, acceleration_f=INITIAL_AF):
    return last_sar + acceleration_f * (ep - last_sar)

def parabolic_is_touched(bull, current_parabo, current_h, current_l):
    if bull and (current_parabo > current_l):
        return True
    elif not bull and (current_parabo < current_h):
        return True
    return False

def calc_parabolic(candles):
    # 初期値
    acceleration_factor = INITIAL_AF
    # INFO: 初期状態は上昇トレンドと仮定して計算
    bull = True
    extreme_price = candles.high[0]
    temp_sar_array = [candles.low[0]]

    # HACK: dataframeのまま処理するより、to_dictで辞書配列化した方が処理が早い
    candles_array = candles.to_dict('records')
    for i, row in enumerate(candles_array):
        current_high = row['high']
        current_low = row['low']
        last_sar = temp_sar_array[-1]

        # レートがparabolicに触れたときの処理
        if parabolic_is_touched(
            bull=bull,
            current_parabo=last_sar,
            current_h=current_high, current_l=current_low
        ):
            temp_sar = extreme_price
            acceleration_factor = INITIAL_AF
            if bull:
                bull = False
                extreme_price = current_low
            else:
                bull = True
                extreme_price = current_high
        else:
            # SARの仮決め
            temp_sar = calc_next_parabolic(
                last_sar=last_sar, ep=extreme_price, acceleration_f=acceleration_factor
            )

            # AFの更新
            if (bull and extreme_price < current_high) \
                or not bull and extreme_price > current_low:
                acceleration_factor = min(
                    acceleration_factor + INITIAL_AF,
                    MAX_AF
                )

            # SARの調整
            if bull:
                temp_sar = min(
                    temp_sar, candles_array[i-1]['low'], candles_array[i-2]['low']
                )
                extreme_price = max(extreme_price, current_high)
            else:
                temp_sar = max(
                    temp_sar, candles_array[i-1]['high'], candles_array[i-2]['high']
                )
                extreme_price = min(extreme_price, current_low)

        if i == 0:
            temp_sar_array[-1] = temp_sar
        else:
            temp_sar_array.append(temp_sar)
    return pd.DataFrame(data=temp_sar_array, columns=['SAR'])

candles['SAR'] = calc_parabolic(candles)
# チャートの描画
figure, (axis1) = plt.subplots(nrows=1, ncols=1, figsize=(20, 10), dpi=200)
mpf.candlestick2_ohlc(
    axis1,
    opens  = candles.open.values,
    highs  = candles.high.values,
    lows   = candles.low.values,
    closes = candles.close.values,
    width=0.6, colorup='#77d879', colordown='#db3f3f'
)

# SAR描画
for key, column in candles[['SAR']].iteritems():
    axis1.scatter(x=candles[['SAR']].index, y=column.values, label=key, c='orangered', marker='d', s=2)

# グラフの見た目を整形
### X軸の見た目を整える
xticks_number = 12 # 12本(60分)刻みに目盛りを書く
xticks_index = range(0, len(candles), xticks_number)
xticks_display = [candles.time.values[i][:16] for i in xticks_index] # 時間を切り出すため、先頭12文字目から取る
plt.sca(axis1)
plt.xticks(xticks_index, xticks_display, rotation=30)

# 画像生成
plt.show()

完成品 - ver2

このコードで、このような結果になる
image.png
ひとまず、これで十分かな...(力尽きたorz)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10