###前回は、「なんちゃって株価シュミレーター」の前半部分を、出来上がりのGUIイメージと生成されたデータ可視化の例で、取り急ぎの紹介をさせて頂きました。
今回は、引き続きPythonコードの中盤部分で、今回の肝とも言える部分の公開・紹介等を進めて行きます。
今回のツールを取り急ぎ作成しなければならない状況で、むむむむ・・・・と唯一考え込んだ「なんちゃって株価の計算」をどうするか?について、苦しいときのGoogle先生頼み!で行き着いた公開ブログ情報が下記になります。(ご参考まで)
幾つかのパターンをシュミレーションした所、最も変化が解り易くコード自体もシンプルでしたので、今回のツールで参考にさせて頂きました。(その他の部分も含めて、知らない事が「ドン!」と書いてありましたので、非常に有益な技術情報(株式系技術ノウハウ)だと思います)
また、この場をお借りしてブログの作者様に厚く御礼申し上げます。
因みにこの式・・・多分他のシュミレーションにも使えると思うので、この後も少し弄り倒してみようかと。
あと、内部的に定義してあるdtの取り扱いをどうするか・・・・これは、引き続き検討課題として作業を継続している所です。
# モンテカルロ法による株価シュミレータ
#
# s0 : 開始価格
# n : 横軸方向の分割数
# mu : ドリフト項(期待収益率)
# sigma : 価格変動の度合い(ボラティリティ)
#
def montecalro(s0, n, mu, sigma):
# データフレームの初期化
Result = np.zeros(n)
# 一番最初のデータを設定
Result[0] = s0
# 年間稼働日を260日と仮定
dt = 1/ 260
# モンテカルロ法で指定されたデータ個数の計算を行う
for i in range(1, n): Result[i] = Result[i-1] * np.exp((mu-sigma**2/2) * dt + sigma * np.random.normal(0, 1, 1) * np.sqrt(dt))
# 計算結果を戻す
return Result
##データの生成時間情報の取り扱い・・・・・
今回の仕組みは
(1) 取り急ぎ必要個数の「なんちゃって株価データ」を与えられた条件で生成する
(2) 生成されたデータに対して「後付け」で時間情報を付与する
という段取りで作業を行っています。
そこで、少し手前味噌&自己満足的な時間情報のパターンを幾つか選択出来るようにしてみました。
(1) PythonのSleep機能を使うパターン
(2) データは連続して作成し、時間データの秒以下を微妙に乱数調整して使うパターン
(3) 強制的に指定された秒数間隔の情報を使うパターン
(4) 強制的に指定された分間隔の情報を使うパターン
(5) 強制的に指定された時間間隔の情報を使うパターン
(6) 強制的に指定された日数間隔の情報を使うパターン
になります。
但し、それぞれのデータベースが付与するタイムスタンプ情報も同時に確保する仕様にしました。(これは、以前に検証報告させて頂いた、LogiCOMPOSERで可視化する際に、タイプスタンプ情報をKeyに含める事で、リアルタイム可視化機能のライブモードを使えるようにする為です)
#
# 選択された処理をコンソールに出力
#
def Message_Out(dt_Pattern, Sleep_Wait):
if dt_Pattern == 0:
if Sleep_Wait !=0: print(str(Sleep_Wait) + " 秒間隔でデータを出力します")
else: print("単純処理時間でデータを出力します")
elif dt_Pattern == 1:
print("秒以下を乱数生成してデータを出力します")
elif dt_Pattern == 2:
print("指定された秒単位を加算してデータを出力します")
elif dt_Pattern == 3:
print("指定された分単位を加算してデータを出力します")
elif dt_Pattern == 4:
print("指定された時間単位を加算してデータを出力します")
elif dt_Pattern == 5:
print("指定された日数単位を加算してデータを出力します")
#
# 生成したデータを再生してデータベースに格納する
#
def Playback(DB_Type, Data1, Data2, Data3, Pattern, Wait):
# 時間情報を生成する起点を確保
dt_now = datetime.datetime.now()
# 処理するリスト内のデータ個数を抽出
Data_Count = len(Data1)
# 選択された処理を表示
Message_Out(Pattern, Wait)
# データベースに接続して、テーブルの初期化と作成を行う
db = Open_DB(DB_Type)
# データを再生しながら指示された時間操作を行いながらデータベースに格納する
with db.cursor() as cursor:
for i in range(0,Data_Count):
# ライブシュミレーション系の処理(COMPOSER等で利用)
if Pattern == 0:
# 待ち時間が0以外の場合は指定された秒数Sleepする
if Wait != 0: time.sleep(Wait)
ts_now = datetime.datetime.now()
elif Pattern == 1: # 秒以下を乱数計算で調整して出力
# ts用のデータをアプリ側で生成
Sec = fakegen.random_digit()
MIL_Sec = fakegen.random_digit()
MIC_Sec = fakegen.random_digit()
# 現実的なタイムスタンプ情報として利用します(秒単位でデータをズラしてBI等で使い易くする)
ts_now = dt_now + datetime.timedelta(seconds = Sec, milliseconds = MIL_Sec, microseconds = MIC_Sec)
elif Pattern == 2: # 指定された秒単位を加算して時間情報を生成
if Wait <= 1: Wait = 1 # 1未満の設定を最小単位に調整
ts_now = dt_now + datetime.timedelta(seconds = Wait)
elif Pattern == 3: # 指定された分単位を加算して時間情報を生成
if Wait <= 1: Wait = 1 # 1未満の設定を最小単位に調整
ts_now = dt_now + datetime.timedelta(minutes = Wait)
elif Pattern == 4: # 指定された時間単位を加算して時間情報を生成
if Wait <= 1: Wait = 1 # 1未満の設定を最小単位に調整
ts_now = dt_now + datetime.timedelta(hours = Wait)
elif Pattern == 5: # 指定された日数単位を加算して時間情報を生成
if Wait <= 1: Wait = 1 # 1未満の設定を最小単位に調整
ts_now = dt_now + datetime.timedelta(days = Wait)
if Pattern !=0: dt_now = ts_now # 生成データを次回の起点に変更
dt_Data = str(ts_now) # 時間情報を文字列で作成
SQL1 = ")" + DB_VALUE + "('" + dt_Data + "','" + str(Data1[i]) + "','" + str(Data2[i]) + "','" + str(Data3[i]) + "')"
if DB_Type == SingleStore: SQL = DB_Insert + T_Name + "(" + SS_DL1 + SQL1
elif DB_Type == MySQL_CDC: SQL = DB_Insert + T_Name + "(" + CDC_DL1 + SQL1
# 途中経過の出力指示が有る場合
if (int(checkbtn.get()) == ON ): print(SQL)
# データベースへの書き込み
cursor.execute(SQL)
db.commit()
# SQLプログレスバーの処理
progressbar1.after(Delay, var_start1(i+1))
progressbar1.update()
# データベースを閉じる
db.close()
# 処理したデータ数の表示
print("生成したデータ数 : " + str(i+1) + "個")
データベースに格納テーブルを作る・・・
今回の検証テーブルは非常にシンプルなので、特に説明は不要かと思いますが「時間情報」については、数パターンを記録しておく仕組みになっていますので、BI等で時系列データを回す際には、基準となる時間軸を適宜選択する形になります。
データ生成に関しては、GUI上の設定情報を取り出して前述の計算式に送り込む形式になります。
結果はリストの形式で戻ってきますので、その結果に対して時間情報を付与しながら順次SQL文を内部生成して、指定したデータベースに順番に格納する作業を生成データの個数分だけ実行します。
#
# 指定されたテーブルを作成(オリジナル側)
#
def Table_Init():
# ステータスバーに処理開始を表示
statusbar["text"] = " " + CT_Message1
DB_Type = int(combo_dict2[cb2.get()])
Create_Table(DB_Type)
# ステータスバーに処理終了を表示
statusbar["text"] = " " + CT_Message2
#
# ターゲット側のテーブルを作成(Equalum向け)
#
def TGT_Init():
# ステータスバーに処理開始を表示
statusbar["text"] = " " + TGT_Message1
Create_Table(MySQL_TGT)
# ステータスバーに処理終了を表示
statusbar["text"] = " " + TGT_Message2
#
# なんちゃって株価を算出する
#
def Data_Gen():
# ステータスバーに処理開始を表示
statusbar["text"] = " " + GEN_Message1
# 計算するデータ数を取得
Time_Frame = int(Gen_Count.get("1.0", "end"))
progressbar1.configure(maximum=Time_Frame) # SQL処理状況
# 株式会社 松の計算パラメータを取得
C1_Init = int(Stock1.get("1.0", "end"))
mu1 = float(Stock1_P1.get("1.0", "end"))
sigma1 = float(Stock1_P2.get("1.0", "end"))
# 株式会社 竹の計算パラメータを取得
C2_Init = int(Stock2.get("1.0", "end"))
mu2 = float(Stock2_P1.get("1.0", "end"))
sigma2 = float(Stock2_P2.get("1.0", "end"))
# 株式会社 梅の計算パラメータを取得
C3_Init = int(Stock3.get("1.0", "end"))
mu3 = float(Stock3_P1.get("1.0", "end"))
sigma3 = float(Stock3_P2.get("1.0", "end"))
# 処理に必要なパラメータを取得
DB_Type = int(combo_dict2[cb2.get()])
Pattern = int(combo_dict1[cb1.get()])
Wait_Time = float(Wait.get("1.0", "end"))
# 株価の計算
Result1 = montecalro(C1_Init, Time_Frame, mu1, sigma1) # 株式会社松のシュミレーション
Result2 = montecalro(C2_Init, Time_Frame, mu2, sigma2) # 株式会社竹のシュミレーション
Result3 = montecalro(C3_Init, Time_Frame, mu3, sigma3) # 株式会社梅のシュミレーション
# 計算結果を再生してデータベースに格納する
Playback(DB_Type, Result1, Result2, Result3, Pattern, Wait_Time)
# ステータスバーに処理終了を表示
statusbar["text"] = " " + GEN_Message2
##今回のまとめ
今回も、NDA(ノン・ダメ出し・アグリーメント)を条件に、相変わらず癖の強い書き方で「ひたすら動けば良い!」系の力技で押し倒してきましたが、次回はいよいよGUI部分(とは言っても、基本形を書き並べる作業ですが・・・)を仕上げて今回のツール全体を完成させたいと思います。
株価シュミレーター(?)を作る(1)はこちら
株価シュミレーター(?)を作る(3)はこちら
株価シュミレーターの計算結果確認ツール作成はこちら