diff --git a/stock/analysis/AnalyzerSqlite.py b/stock/analysis/AnalyzerSqlite.py index 0b44a83..c548903 100644 --- a/stock/analysis/AnalyzerSqlite.py +++ b/stock/analysis/AnalyzerSqlite.py @@ -25,6 +25,7 @@ from stock.analysis.IchimokuCloud import IchimokuCloud from stock.analysis.RSI import RSI from stock.analysis.MACD import MACD from stock.analysis.Envelope import Envelope +from stock.analysis.MFI import MFI from stock.analysis.MovingAverage import MovingAverage class AnalyzerSqlite: @@ -34,6 +35,7 @@ class AnalyzerSqlite: rsi = None macd = None envelope = None + mfi = None topCompany = None fnguide = None @@ -53,6 +55,7 @@ class AnalyzerSqlite: self.rsi = RSI() self.macd = MACD() self.envelope = Envelope() + self.mfi = MFI() if stockFileName is not None: self.stockFileName = stockFileName @@ -353,6 +356,7 @@ class AnalyzerSqlite: sql += ' stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, ' sql += ' rsi, rsis, ' sql += ' macd, macds, macdo, ' + sql += ' mfi, ' sql += ' trend ' sql += ' FROM ' + TableName + ' where CODE=? order by ymd desc limit 512 ' cursor.execute(sql, (CODE,)) @@ -371,6 +375,7 @@ class AnalyzerSqlite: stochastic_fast_k, stochastic_slow_k, stochastic_slow_d = [], [], [] rsi, rsis = [], [] macd, macds, macdo = [], [], [] + mfi = [] trend = [] for price in prices: @@ -427,7 +432,8 @@ class AnalyzerSqlite: macd.append(price[50]) macds.append(price[51]) macdo.append(price[52]) - trend.append(price[53]) + mfi.append(price[53]) + trend.append(price[54]) stock = { "ymd": ymd, @@ -440,6 +446,7 @@ class AnalyzerSqlite: "fast_k": stochastic_fast_k, "slow_k": stochastic_slow_k, "slow_d": stochastic_slow_d, "rsi": rsi, "rsis": rsis, "macd": macd, "macds": macds, "macdo": macdo, + "mfi": mfi, "trend": trend } @@ -464,7 +471,7 @@ class AnalyzerSqlite: self.makeDir("daily_이전에_없던_거래량") self.makeDir("daily_final_candidate") - self.makeDir("daily_매수_후보") + self.makeDir("daily_bol_candidate") return @@ -571,7 +578,7 @@ class AnalyzerSqlite: check = self.common.buy_stock_candidate(param, stock_daily) if check: count += 1 - dir_name = "daily_매수_후보" + dir_name = "daily_bol_candidate" log = str(count) + "_" + dir_name + "_" self.writeFile(dir_name, CODE, NAME, top, stock_daily, log) return @@ -712,6 +719,7 @@ class AnalyzerSqlite: "macd": -1, "macds": -1, "macdo": -1, + "mfi": -1, "trend": -1 } ) @@ -726,7 +734,7 @@ class AnalyzerSqlite: stockAnalysisTableName = 'stock_analysis_' + type # 테이블 생성 - cursor.execute("CREATE TABLE IF NOT EXISTS " + stockAnalysisTableName + " (CODE text, NAME text, ymd text, close REAL, diff REAL, open REAL, high REAL, low REAL, volume REAL, avg3 REAL, avg4 REAL, avg5 REAL, avg6 REAL, avg10 REAL, avg12 REAL, avg20 REAL, avg36 REAL, avg40 REAL, avg48 REAL, avg60 REAL, avg120 REAL, avg200 REAL, avg240 REAL, avg300 REAL, avg360 REAL, avg480 REAL, avg720 REAL, avg1440 REAL, disparity_avg5 REAL, disparity_avg10 REAL, disparity_avg20 REAL, disparity_avg60 REAL, disparity_avg120 REAL, disparity_avg240 REAL, disparity_avg480, bolingerband_upper REAL, bolingerband_lower REAL, bolingerband_middle REAL, bolingerband_width REAL, bolingerband_pb REAL, envelope_upper REAL, envelope_lower REAL, envelope_middle REAL, ichimokucloud_changeLine REAL, ichimokucloud_baseLine REAL, ichimokucloud_laggingSpan REAL, ichimokucloud_leadingSpan1 REAL, ichimokucloud_leadingSpan2 REAL, stochastic_fast_k REAL, stochastic_slow_k REAL, stochastic_slow_d REAL, rsi REAL, rsis REAL, macd REAL, macds REAL, macdo REAL, trend REAL)") + cursor.execute("CREATE TABLE IF NOT EXISTS " + stockAnalysisTableName + " (CODE text, NAME text, ymd text, close REAL, diff REAL, open REAL, high REAL, low REAL, volume REAL, avg3 REAL, avg4 REAL, avg5 REAL, avg6 REAL, avg10 REAL, avg12 REAL, avg20 REAL, avg36 REAL, avg40 REAL, avg48 REAL, avg60 REAL, avg120 REAL, avg200 REAL, avg240 REAL, avg300 REAL, avg360 REAL, avg480 REAL, avg720 REAL, avg1440 REAL, disparity_avg5 REAL, disparity_avg10 REAL, disparity_avg20 REAL, disparity_avg60 REAL, disparity_avg120 REAL, disparity_avg240 REAL, disparity_avg480, bolingerband_upper REAL, bolingerband_lower REAL, bolingerband_middle REAL, bolingerband_width REAL, bolingerband_pb REAL, envelope_upper REAL, envelope_lower REAL, envelope_middle REAL, ichimokucloud_changeLine REAL, ichimokucloud_baseLine REAL, ichimokucloud_laggingSpan REAL, ichimokucloud_leadingSpan1 REAL, ichimokucloud_leadingSpan2 REAL, stochastic_fast_k REAL, stochastic_slow_k REAL, stochastic_slow_d REAL, rsi REAL, rsis REAL, macd REAL, macds REAL, macdo REAL, mfi REAL, trend REAL)") # 키 생성 create_key = "CREATE INDEX IF NOT EXISTS " + stockAnalysisTableName + "_idx on " + stockAnalysisTableName + " (CODE, ymd) " @@ -745,6 +753,7 @@ class AnalyzerSqlite: self.envelope.analyze(stock) self.rsi.analyze(stock) self.macd.analyze(stock) + self.mfi.analyze(stock) close_list = [price['close'] for price in stock['PRICE']] if 200 < len(close_list): @@ -775,8 +784,9 @@ class AnalyzerSqlite: sql += " ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, " sql += " stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, " sql += " rsi, rsis, macd, macds, macdo, " + sql += " mfi, " sql += " trend) " - sql += " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + sql += " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(sql, ( stock["CODE"], stock["NAME"], price['ymd'], price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'], @@ -787,6 +797,7 @@ class AnalyzerSqlite: price['ichimokucloud_changeLine'], price['ichimokucloud_baseLine'], price['ichimokucloud_laggingSpan'], price['ichimokucloud_leadingSpan1'], price['ichimokucloud_leadingSpan2'], price['stochastic_fast_k'], price['stochastic_slow_k'], price['stochastic_slow_d'], price['rsi'], price['rsis'], price['macd'], price['macds'], price['macdo'], + price['mfi'], price['trend'], )) @@ -800,6 +811,7 @@ class AnalyzerSqlite: sql += " stochastic_fast_k=?, stochastic_slow_k=?, stochastic_slow_d=?, " sql += " rsi=?, rsis=?, " sql += " macd=?, macds=?, macdo=?, " + sql += " mfi=?, " sql += " trend=? " sql += " WHERE CODE=? and ymd=?" @@ -813,6 +825,7 @@ class AnalyzerSqlite: price['stochastic_fast_k'], price['stochastic_slow_k'], price['stochastic_slow_d'], price['rsi'], price['rsis'], price['macd'], price['macds'], price['macdo'], + price['mfi'], price['trend'], stock["CODE"], price['ymd'],)) break @@ -876,6 +889,7 @@ class AnalyzerSqlite: "macd": -1, "macds": -1, "macdo": -1, + "mfi": -1, "trend": -1 } diff --git a/stock/analysis/Common.py b/stock/analysis/Common.py index 4611053..0a03b18 100644 --- a/stock/analysis/Common.py +++ b/stock/analysis/Common.py @@ -616,11 +616,18 @@ class Common: def buy_stock_candidate(self, param, stock): check = False - if len(stock['trend']) < 1: + if len(stock['trend']) < 10: return check - if not (stock['avg60'][1] < stock['avg20'][1] and stock['avg5'][1]) and (stock['avg60'][0] < stock['avg20'][0] and stock['avg5'][0]): - check = True + if stock['upper'][0] < stock['high'][0]: + # 볼린저 밴드 width 로 매수 시점 + for i in range(10): + if stock['width'][i+1] < 20 and 20 < stock['width'][i]: + check = True + break + # 볼린저 밴드 %B와 MFI로 매수 시점 + if 80 < stock['pb'][i+1] and 80 < stock['mfi'][i]: + check = True return check diff --git a/stock/analysis/MFI.py b/stock/analysis/MFI.py new file mode 100644 index 0000000..bb6228c --- /dev/null +++ b/stock/analysis/MFI.py @@ -0,0 +1,63 @@ +import numpy as np +import pandas as pd +from stock.analysis.Common import Common + +# [청송촌놈] 파생을 알아야 시장이 보인다. 청송이 종목 고르는법! https://www.youtube.com/watch?v=weABtgZDeGg +# 6. Pandas와 Plotly를 이용한 MACD 차트 그리기 https://excelsior-cjh.tistory.com/110 +# 첫번째. MACD 지표를 이용한 차트분석: https://post.naver.com/viewer/postView.nhn?volumeNo=7435935&memberNo=32471429 + +# MACD (Moving Average Conver gence Divergence) +# 빨간 네모박스권으로 MACD가 MACD-Sign 을 골든크로스하며, 상승하였을때, 주가는 상승추세를 유지하며, MACD가 MACD-Sign(분홍색)을 데드크로스 할때 주가는 하락의 추세를 보이게 됩니다. +# 즉, MSCD가 0이상에서 MACD-Sign 위에서 상승하는 그림이어야 +class MFI: + + common = None + + def __init__(self): + self.common = Common() + return + + def apply(self, df, period = 14): + typical_price = (df.high + df.low + df.close) / 3 + money_flow = typical_price * df.volume + + positive_flow = [] + negative_flow = [] + + for i in range(1, len(typical_price)): + if typical_price[i] > typical_price[i-1]: + positive_flow.append(money_flow[i-1]) + negative_flow.append(0) + elif typical_price[i] < typical_price[i - 1]: + positive_flow.append(0) + negative_flow.append(money_flow[i - 1]) + else: + positive_flow.append(0) + negative_flow.append(0) + + positive_mf = [] + negative_mf = [] + + for i in range(period-1, len(positive_flow)): + positive_mf.append(sum(positive_flow[i+1-period: i+1])) + for i in range(period - 1, len(negative_flow)): + negative_mf.append(sum(negative_flow[i + 1 - period: i + 1])) + + mfi = list(100 * (np.array(positive_mf) / (np.array(positive_mf) + np.array(negative_mf)))) + mfi = list(np.repeat(np.nan, len(df)-len(mfi))) + mfi + + #df = df.assign(macd=macd, macds=macds, macdo=macdo).dropna() + df = df.assign(mfi=mfi) + + return df + + def analyze(self, stock): + + df = pd.DataFrame() + df = df.from_dict(stock['PRICE']) + df = self.apply(df) + + for i in range(len(df.macd)): + stock['PRICE'][i]['mfi'] = df.mfi.values[i] + + return \ No newline at end of file