diff --git a/StockCrawler.py b/StockCrawler.py index a97661d..c74e1cc 100644 --- a/StockCrawler.py +++ b/StockCrawler.py @@ -129,13 +129,6 @@ class StockCrawlerDaily: analyzerSqlite = AnalyzerSqlite(stockFileName) - print("\n[종목 분석]") - # S: 분석까지 진행 - analyzerSqlite.analyzeDaily() - analyzerSqlite.analyzeGrouping("weekly") - analyzerSqlite.analyzeGrouping("monthly") - self.bot.sendMsg("10. analyze...") - print("\n[종목 결정]") # HTML 출력 outPath = os.path.join(self.PROJECT_HOME, "resources", "analysis") @@ -154,7 +147,7 @@ class StockCrawlerDaily: os.mkdir(outPath) analyzerSqlite.findCandidates(outPath) - self.bot.sendMsg("11. done decision...") + self.bot.sendMsg("10. done decision...") return diff --git a/hts/BuySell_Daily.py b/hts/BuySell_Daily.py new file mode 100644 index 0000000..807de97 --- /dev/null +++ b/hts/BuySell_Daily.py @@ -0,0 +1,178 @@ +import numpy as np +from datetime import datetime + +class BuySell_Daily: + + def countYangBong(self, data, i): + count = 0 + for c in range(5): + if data['open'][i-c] < data['close'][i-c]: + count += 1 + return count + + def getBuyPrice(self, data, i, BS=None): + buy_ymd, buy_price, buy_count, buy_weight, buy_type, buy_cut = None, 0, 0, 1, '', None + + tmp_buy_ymd, tmp_buy_price, tmp_buy_count, tmp_buy_type, tmp_buy_cut = self.getBuyPrice_ichimok_changeLine(data, i, BS) + if 0 < tmp_buy_count: + buy_ymd = tmp_buy_ymd; buy_price = tmp_buy_price; buy_count = tmp_buy_count; buy_type = tmp_buy_type; buy_cut = tmp_buy_cut + tmp_buy_ymd, tmp_buy_price, tmp_buy_count, tmp_buy_type, tmp_buy_cut = self.getBuyPrice_ichimok_baseLine(data, i, BS) + if 0 < tmp_buy_count: + buy_ymd = tmp_buy_ymd; buy_price = tmp_buy_price; buy_count = tmp_buy_count; buy_type = tmp_buy_type; buy_cut = tmp_buy_cut + tmp_buy_ymd, tmp_buy_price, tmp_buy_count, tmp_buy_type, tmp_buy_cut = self.getBuyPrice_ichimok_laggingSpan(data, i, BS) + if 0 < tmp_buy_count: + buy_ymd = tmp_buy_ymd; buy_price = tmp_buy_price; buy_count = tmp_buy_count; buy_type = tmp_buy_type; buy_cut = tmp_buy_cut + + return buy_ymd, buy_price, buy_count, buy_type, buy_cut + + def getSellPrice(self, data, i, BS=None): + sell_price, sell_count, sell_type = 0, 1, '' + sell_type_list = [] + + tmp_sell_price, tmp_sell_type_list = self.getSellPrice_ichimok_baseLine(data, i, BS) + sell_type_list += tmp_sell_type_list + + sell_price = tmp_sell_price + + if 0 < len(sell_type_list) or 0 < sell_price: + sell_type = ','.join(list(set(sell_type_list))) + + return sell_price, sell_count, sell_type + + def getBuyPrice_ichimok_changeLine(self, data, i, BS=None): + + buy_ymd, buy_price, buy_count, buy_weight, buy_type, buy_cut = None, 0, 0, 1, '', None + + check = False + + id9, id26, id33, id52 = 8, 25, 32, 51 + if 5 < i: + # 신저가를 갱신하지 않으면서 전환선이 떨어질 때 주가는 올라감 (기준선은 횡보, 현재 봉이 신저가가 이닐 때) + # --> 기준선이 계속 횡보하거나 떨어지면 상승하지는 않는다. + # https://www.youtube.com/watch?v=KZMP0Ssv8WI&t=432s (8:45) + if data['new_low_9'][i] == 0: + if data['changeLine'][i] < data['baseLine'][i]: + if data['changeLine'][i] < data['changeLine'][i-1] and np.min(data['close'][i-8:i]) < data['close'][i]: + if data['baseLine'][i-1] == data['baseLine'][i] < data['baseLine'][i-2]: + if 3 < self.countYangBong(data, i): + check = True + buy_type = "ichimok_changeLine_1" + buy_weight = 5 + buy_cut = min(np.min(data['open'][i - 60:i]), np.min(data['close'][i - 60:i])) + + if data['new_high_9'][i] == 1: + if data['changeLine'][i-1] < data['changeLine'][i] and data['baseLine'][i-1] < data['baseLine'][i]: + if data['baseLine'][i - 1] != data['baseLine'][i]: + if 0.2 < data['leadingSpan1_leadingSpan2_diff_rate'][i+id52]: + check = True + buy_type = "ichimok_changeLine_2" + buy_weight = 10 + buy_cut = min(np.min(data['open'][i - 60:i]), np.min(data['close'][i - 60:i])) + + if data['new_high_26'][i] == 1: + for c in range(i-15, i): + if data['changeLine'][c-1] < data['baseLine'][c] and data['baseLine'][i-1] < data['changeLine'][i]: + if 0.2 < data['leadingSpan1_leadingSpan2_diff_rate'][i + id52]: + check = True + buy_type = "ichimok_changeLine_3" + buy_weight = 10 + buy_cut = min(np.min(data['open'][i - 60:i]), np.min(data['close'][i - 60:i])) + break + + if check: + buy_ymd = data['ymd'][i] + buy_price = data['close'][i] + buy_count = buy_weight + + return buy_ymd, buy_price, buy_count, buy_type, buy_cut + + + """""""""""""""""" + """""""""""""""""" + + def getBuyPrice_ichimok_baseLine(self, data, i, BS=None): + + buy_ymd, buy_price, buy_count, buy_weight, buy_type, buy_cut = None, 0, 0, 1, '', None + + check = False + + id9, id26, id33, id52 = 9, 26, 33, 52 + if 5 < i: + # 기준선이 하락할 때, 전환선이 상승하는 경우를 중기 추세의 변곡이라 한다 + if data['changeLine'][i-1] < data['changeLine'][i] and data['baseLine'][i] < data['baseLine'][i-1]: + if data['changeLine'][i - 1] < data['baseLine'][i-1] and data['baseLine'][i] < data['changeLine'][i]: + if data['open'][i] < data['close'][i]: + if 3 < self.countYangBong(data, i): + check = True + buy_type = "ichimok_baseLine_1" + buy_weight = 5 + buy_cut = min(np.min(data['open'][i - 60:i]), np.min(data['close'][i - 60:i])) + + # 기준선이 평행 때, 전환선이 상승하는 경우를 중기 추세의 변곡이라 한다 + if data['changeLine'][i-1] < data['changeLine'][i] and data['baseLine'][i-3] == data['baseLine'][i-2] == data['baseLine'][i-1] == data['baseLine'][i]: + if data['changeLine'][i - 1] < data['baseLine'][i-1] and data['baseLine'][i] < data['changeLine'][i]: + if data['open'][i] < data['close'][i]: + if 3 < self.countYangBong(data, i): + check = True + buy_type = "ichimok_baseLine_1" + buy_weight = 5 + buy_cut = min(np.min(data['open'][i - 60:i]), np.min(data['close'][i - 60:i])) + + if check: + buy_ymd = data['ymd'][i] + buy_price = data['close'][i] + buy_count = buy_weight + + return buy_ymd, buy_price, buy_count, buy_type, buy_cut + + """""""""""""""""" + """""""""""""""""" + + def getBuyPrice_ichimok_laggingSpan(self, data, i, BS): + + buy_ymd, buy_price, buy_count, buy_weight, buy_type, buy_cut = None, 0, 0, 1, '', None + + check = False + + if 5 < i: + + if data['laggingSpan_close_diff_rate'][i-1] <= 0 and 0 < data['laggingSpan_close_diff_rate'][i]: + check = True + buy_price = data['close'][i] + buy_count = buy_weight + buy_weight = 2 + buy_type = 'laggingSpan1' + + if 0 <= data['laggingSpan_avg60_diff_rate'][i-1] and data['laggingSpan_avg60_diff_rate'][i] < 0: + check = True + buy_price = data['close'][i] + buy_count = buy_weight + buy_weight = 2 + buy_type = 'laggingSpan2' + + if check: + buy_price = data['close'][i] + buy_count = buy_weight + + return buy_ymd, buy_price, buy_count, buy_type, buy_cut + + """""""""""""""""" + """""""""""""""""" + + def getSellPrice_ichimok_baseLine(self, data, i, BS=None): + sell_price = 0 + sell_type_list = [] + + check = False + + id26, id52 = 26, 52 + + if data['new_high_9'][i] == 0: + if data['baseLine'][i-1] < data['baseLine'][i] and data['changeLine'][i] < data['changeLine'][i-1]: + check = True + sell_type_list.append('ichimok_baseLine') + + if check: + sell_price = data['close'][i] + + return sell_price, sell_type_list \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 88b9fe9..370f538 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ matplotlib pandas_datareader bs4 python-dateutil -pywin32 +#pywin32 #datasets #ipywidgets==7.0.0 @@ -17,7 +17,9 @@ pywin32 #transformers yfinance finterstellar +pandas-datareader pybithumb ccxt -slack_sdk -scikit-learn \ No newline at end of file +slack-sdk +scikit-learn +ta-lib diff --git a/stock/analysis/AnalyzerSqlite.py b/stock/analysis/AnalyzerSqlite.py index 0532a90..2833490 100644 --- a/stock/analysis/AnalyzerSqlite.py +++ b/stock/analysis/AnalyzerSqlite.py @@ -4,11 +4,15 @@ import shutil import matplotlib.pyplot as plt import datetime import sqlite3 -from datetime import datetime +import math +from math import nan +from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta from matplotlib import rc import pandas as pd import numpy as np +from stock.analysis.JSDPattern_simulation import JSDPattern_simulation +from hts.BuySell_Daily import BuySell_Daily rc('font', family='AppleGothic') plt.rcParams['axes.unicode_minus'] = False @@ -17,27 +21,11 @@ import plotly.graph_objs as go from plotly import subplots import plotly.io as po -from statsmodels.tsa.seasonal import seasonal_decompose - from stock.analysis.Common import Common -from stock.analysis.Stochastic import Stochastic -from stock.analysis.BolingerBand import BolingerBand -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: - stochastic = None - bolingerBand = None - ichimokuCloud = None - rsi = None - macd = None - envelope = None - mfi = None - + jSDPattern = None + buySell_Daily = None topCompany = None fnguide = None @@ -47,21 +35,15 @@ class AnalyzerSqlite: moving_avg = None - def __init__(self, stockFileName=None): + def __init__(self, RESOURCE_PATH): self.common = Common() - self.stochastic = Stochastic() - self.bolingerBand = BolingerBand() - self.ichimokuCloud = IchimokuCloud() - self.rsi = RSI() - self.macd = MACD() - self.envelope = Envelope() - self.mfi = MFI() - - if stockFileName is not None: - self.stockFileName = stockFileName - self.topCompany = self.getTopCompany(stockFileName, 2000) - self.fnguide = self.readFnguide(stockFileName) + self.stockFileName = os.path.join(RESOURCE_PATH, 'stock.db') + self.jSDPattern = JSDPattern_simulation(self.stockFileName) + self.buySell_Daily = BuySell_Daily() + self.stockFileName = self.stockFileName + self.topCompany = self.getTopCompany(self.stockFileName, 2000) + self.fnguide = self.readFnguide(self.stockFileName) return @@ -119,155 +101,378 @@ class AnalyzerSqlite: conn.close() return fnguide - def draw(self, stock): - # 참고) https://sjblog1.tistory.com/45 - ymd = list(stock['ymd']) - open = list(stock['open']) - close = list(stock['close']) - high = list(stock['high']) - low = list(stock['low']) - volume = list(stock['volume']) - avg3 = list(stock['avg3']) - avg4 = list(stock['avg4']) - avg5 = list(stock['avg5']) - avg6 = list(stock['avg6']) - avg10 = list(stock['avg10']) - avg12 = list(stock['avg12']) - avg20 = list(stock['avg20']) - avg36 = list(stock['avg36']) - avg40 = list(stock['avg40']) - avg48 = list(stock['avg48']) - avg60 = list(stock['avg60']) - avg120 = list(stock['avg120']) - avg240 = list(stock['avg240']) - avg480 = list(stock['avg480']) - disparity_avg5 = list(stock['disparity_avg5']) - disparity_avg10 = list(stock['disparity_avg10']) - disparity_avg20 = list(stock['disparity_avg20']) - disparity_avg60 = list(stock['disparity_avg60']) - disparity_avg120 = list(stock['disparity_avg120']) - disparity_avg240 = list(stock['disparity_avg240']) - disparity_avg480 = list(stock['disparity_avg480']) - macd = list(stock['macd']) - macdo = list(stock['macdo']) - macds = list(stock['macds']) - rsi = list(stock['rsi']) - rsis = list(stock['rsis']) - stochastic_slow_k = list(stock['slow_k']) - stochastic_slow_d = list(stock['slow_d']) - bolingerband_upper = list(stock['upper']) - bolingerband_lower = list(stock['lower']) - bolingerband_middle = list(stock['middle']) - envelope_upper = list(stock['envelope_upper']) - envelope_lower = list(stock['envelope_lower']) - ichimokucloud_changeLine = list(stock['ichimokucloud_changeLine']) - ichimokucloud_baseLine = list(stock['ichimokucloud_baseLine']) - ichimokucloud_laggingSpan = [laggingSpan if -1 < laggingSpan else None for laggingSpan in stock['ichimokucloud_laggingSpan']] - ichimokucloud_laggingSpan = list(ichimokucloud_laggingSpan) - ichimokucloud_leadingSpan1 = list(stock['ichimokucloud_leadingSpan1']) - ichimokucloud_leadingSpan2 = list(stock['ichimokucloud_leadingSpan2']) - trend = list(stock['trend']) - trend_s = list(stock['trend_s']) - trend_k = list(stock['trend_k']) - last_min = list(stock['last_min']) - last_middle = list(stock['last_middle']) - last_max = list(stock['last_max']) + def cz(self, value): + if value is None or math.isnan(value): + return 0 - # general - candle_stick = go.Candlestick(x=ymd, - open=open, high=high, low=low, close=close, - increasing_line_color='red', decreasing_line_color='blue', name='candle') - #avg3 = go.Scatter(x=ymd, y=avg3, name="avg3", line_color='#085F1B') - #avg4 = go.Scatter(x=ymd, y=avg4, name="avg4", line_color='#085F1B') - avg5 = go.Scatter(x=ymd, y=avg5, name="avg5", line_color='#ff0000') - #avg6 = go.Scatter(x=ymd, y=avg6, name="avg6", line_color='#698D09') - avg10 = go.Scatter(x=ymd, y=avg10, name="avg10", line_color='#8013ED') - #avg12 = go.Scatter(x=ymd, y=avg12, name="avg12", line_color='#000000') - avg20 = go.Scatter(x=ymd, y=avg20, name="avg20", line_color='#0000ff') - #avg36 = go.Scatter(x=ymd, y=avg36, name="avg36", line_color='#370557') - #avg40 = go.Scatter(x=ymd, y=avg40, name="avg40", line_color='#041366') - #avg48 = go.Scatter(x=ymd, y=avg48, name="avg48", line_color='#7A1E66') - avg60 = go.Scatter(x=ymd, y=avg60, name="avg60", line_color='#ff00ff') - avg120 = go.Scatter(x=ymd, y=avg120, name="avg120", line_color='#cd4025') - avg240 = go.Scatter(x=ymd, y=avg240, name="avg240", line_color='#38761d') - avg480 = go.Scatter(x=ymd, y=avg480, name="avg480", line_color='#656565') - bolinger_upper = go.Scatter(x=ymd, y=bolingerband_upper, name="bol_upper", line_color='#8B4513') - bolinger_lower = go.Scatter(x=ymd, y=bolingerband_lower, name="bol_lower", line_color='#8B4513') - env_upper = go.Scatter(x=ymd, y=envelope_upper, name="env_upper", line_color='#FF33A2') - env_lower = go.Scatter(x=ymd, y=envelope_lower, name="env_lower", line_color='#FF33A2') - changeLine = go.Scatter(x=ymd, y=ichimokucloud_changeLine, name="changeLine", line_color='#000000') - baseLine = go.Scatter(x=ymd, y=ichimokucloud_baseLine, name="baseLine", line_color='#FF0000') - laggingSpan = go.Scatter(x=ymd, y=ichimokucloud_laggingSpan, name='laggingSpan', line_color='#B50ABB') - leadingSpan1 = go.Scatter(x=ymd, y=ichimokucloud_leadingSpan1, name='leadingSpan1', line_color='black') - leadingSpan2 = go.Scatter(x=ymd, y=ichimokucloud_leadingSpan2, name='leadingSpan2', line_color='black') - trend = go.Scatter(x=ymd, y=trend, name="trend", line_color='#574e4c') - trend_k = go.Scatter(x=ymd, y=trend_k, name="trend_k", line_color='#ff0000') - trend_s = go.Scatter(x=ymd, y=trend_s, name="trend_s", line_color='#0000ff') - last_min = go.Scatter(x=ymd, y=last_min, name="last_min", line_color='#ff0000') - last_middle = go.Scatter(x=ymd, y=last_middle, name="last_middle", line_color='#574e4c') - last_max = go.Scatter(x=ymd, y=last_max, name="last_max", line_color='#0000ff') + return value - candle_data = [trend, trend_k, trend_s, avg5, avg20, avg60, avg120, avg240, avg480, bolinger_upper, bolinger_lower, changeLine, baseLine, laggingSpan, last_min, last_max, last_middle, candle_stick] - #candle_data = [candle_stick, trend, trend_k, trend_s, avg5, avg10, avg20, avg60, avg120, avg240, bolinger_upper, bolinger_lower, env_upper, env_lower, changeLine, baseLine] - #candle_data = [avg5, avg20, trend, trend_k, trend_s, changeLine, baseLine, laggingSpan, candle_stick] + def clear_BSLINE(self, BUY_LIST, sell_type=None): + if sell_type is None or sell_type == '': + BUY_LIST['avg_buy_price'] = 0 + BUY_LIST['buy_count'] = 0 + BUY_LIST['buy_list'].clear() + else: + BUY_LIST['avg_buy_price'] = 0 + BUY_LIST['buy_count'] = 0 - volume = go.Bar(x=ymd, y=volume, marker_color='red', name="volume") - volume_data = [volume] + tmp_sell_type = sell_type.split(',') + for i, buy_list in reversed(list(enumerate(BUY_LIST['buy_list']))): + for t_sell_type in tmp_sell_type: + if buy_list['buy_type'].strip() == t_sell_type.strip(): + del BUY_LIST['buy_list'][i] + break + return - disparity_avg5 = go.Scatter(x=ymd, y=disparity_avg5, name="disparity_avg5", line_color='#ff0000') - disparity_avg20 = go.Scatter(x=ymd, y=disparity_avg20, name="disparity_avg20", line_color='#0000ff') - disparity_avg60 = go.Scatter(x=ymd, y=disparity_avg60, name="disparity_avg60", line_color='#ff00ff') - disparity_avg120 = go.Scatter(x=ymd, y=disparity_avg120, name="disparity_avg120", line_color='#cd4025') - disparity_avg240 = go.Scatter(x=ymd, y=disparity_avg240, name="disparity_avg240", line_color='#38761d') - disparity_avg480 = go.Scatter(x=ymd, y=disparity_avg480, name="disparity_avg480", line_color='#656565') - disparity_data = [disparity_avg5, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480] + def draw(self, stock_code, data, bsLine=None): - # macd - macd_line = go.Scatter(x=ymd, y=macd, line=dict(color='red', width=2), name='macd') - macd_s_line = go.Scatter(x=ymd, y=macds, line=dict(dash='dashdot', color='black', width=2), name='macds') - macd_o_line = go.Bar(x=ymd, y=macdo, marker_color='purple', name='macdo') - macd_data = [macd_line, macd_s_line, macd_o_line] + # 어제 데이터는 지운다. + #data = data.loc[pd.DatetimeIndex(data.index).day == int(given_day[6:])] + buy_price_line, buy_count_line, buy_type, buy_count_line, sell_price_line, sell_count_line, sell_type = [], [], [], [], [], [], [] + buy_sell_size, buy_colors, sell_colors, buy_colors = [], [], [], [] - # stochastic - rsi_line = go.Scatter(x=ymd, y=rsi, line=dict(color='red', width=2), name='rsi') - rsis_line = go.Scatter(x=ymd, y=rsis, line=dict(dash='dashdot', color='black', width=2), name='rsis') - rsi_data = [rsi_line, rsis_line] + if bsLine is not None: + buy_price_line = bsLine['buy_price'] + buy_count_line = bsLine['buy_count'] + sell_price_line = bsLine['sell_price'] + sell_count_line = bsLine['sell_count'] + buy_type = bsLine['buy_type'] + sell_type = bsLine['sell_type'] - # stochastic - stochastic_slow_k_line = go.Scatter(x=ymd, y=stochastic_slow_k, line=dict(color='red', width=2), name='slow_k') - stochastic_slow_d_line = go.Scatter(x=ymd, y=stochastic_slow_d, line=dict(dash='dashdot', color='black', width=2), name='slow_d') - stochastic_data = [stochastic_slow_k_line, stochastic_slow_d_line] + for i in range(len(data)): + if buy_price_line[i] < 1: + buy_colors.append("#ffffff") + buy_price_line[i] = nan + buy_sell_size.append(0) + else: + buy_colors.append("#0C752E") + buy_sell_size.append(14) + for i in range(len(data)): + if sell_price_line[i] < 1: + sell_colors.append("#ffffff") + sell_price_line[i] = nan + else: + sell_colors.append("#00ced1") + volume_colors = [] + for i in range(len(data)): + if data['open'][i] > data['close'][i]: + volume_colors.append("#FF0000") + elif data['open'][i] < data['close'][i]: + volume_colors.append("#FF0000") + else: + volume_colors.append("#000000") + + # 그래프를 설정한다. + if bsLine is not None: + buy_text_list, sell_text_list = [], [] + for i in range(len(data['ymd'])): + buy_text_list.append( + "{}, {}, {} ({:,.2f})

" + "avg5: {:.2f}, avg10: {:.2f}, avg20: {:.2f}, avg60: {:.2f}, avg90: {:.2f}, avg120: {:.2f}, avg240: {:.2f}
" + "avg360: {:.2f}, avg480: {:.2f}, avg720: {:.2f}, avg1440: {:.2f}, avg2880: {:.2f}

" + "loc_k: {:.2f}, loc_d: {:.2f}, loc_s: {:.2f}

" + "laggingSpan_close_diff: {:.4f} ({:.4f})
" + "laggingSpan_changeLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_baseLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan1_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan2_diff: {:.4f} ({:.4f})
" + "laggingSpan_avg60_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower10_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle10_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper10_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower20_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle20_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper20_diff: {:.4f} ({:.4f})
" + "baseLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_baseLine_diff: {:.4f} ({:.4f})
" + "leadingSpan1_leadingSpan2_diff: {:.4f} ({:.4f})
" + .format(data['ymd'][i].strftime('%Y-%m-%d %H:%M'), buy_type[i], self.cz(buy_price_line[i]), self.cz(buy_price_line[i])*self.cz(buy_count_line[i]), + self.cz(data["avg5"][i]), self.cz(data["avg10"][i]), self.cz(data["avg20"][i]), self.cz(data["avg60"][i]), self.cz(data["avg90"][i]), self.cz(data["avg120"][i]), self.cz(data["avg240"][i]), self.cz(data["avg360"][i]), self.cz(data["avg480"][i]), self.cz(data["avg720"][i]), self.cz(data["avg1440"][i]), self.cz(data["avg2880"][i]), + self.cz(data['loc_240_k'][i]), self.cz(data['loc_240_d'][i]), self.cz(data['loc_240_s'][i]), + self.cz(data['laggingSpan_close_diff'][i]), self.cz(data['laggingSpan_close_diff_rate'][i]), + self.cz(data['laggingSpan_changeLine_diff'][i]), self.cz(data['laggingSpan_changeLine_diff_rate'][i]), + self.cz(data['laggingSpan_baseLine_diff'][i]), self.cz(data['laggingSpan_baseLine_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan1_diff'][i]), self.cz(data['laggingSpan_leadingSpan1_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan2_diff'][i]), self.cz(data['laggingSpan_leadingSpan2_diff_rate'][i]), + self.cz(data['laggingSpan_avg60_diff'][i]), self.cz(data['laggingSpan_avg60_diff_rate'][i]), + self.cz(data['laggingSpan_lower10_diff'][i]), self.cz(data['laggingSpan_lower10_diff_rate'][i]), + self.cz(data['laggingSpan_middle10_diff'][i]), self.cz(data['laggingSpan_middle10_diff_rate'][i]), + self.cz(data['laggingSpan_upper10_diff'][i]), self.cz(data['laggingSpan_upper10_diff_rate'][i]), + self.cz(data['laggingSpan_lower20_diff'][i]), self.cz(data['laggingSpan_lower20_diff_rate'][i]), + self.cz(data['laggingSpan_middle20_diff'][i]), self.cz(data['laggingSpan_middle20_diff_rate'][i]), + self.cz(data['laggingSpan_upper20_diff'][i]), self.cz(data['laggingSpan_upper20_diff_rate'][i]), + self.cz(data['baseLine_close_diff'][i]), self.cz(data['baseLine_close_diff_rate'][i]), + self.cz(data['changeLine_close_diff'][i]), self.cz(data['changeLine_close_diff_rate'][i]), + self.cz(data['changeLine_baseLine_diff'][i]), self.cz(data['changeLine_baseLine_diff_rate'][i]), + self.cz(data['leadingSpan1_leadingSpan2_diff'][i]), self.cz(data['leadingSpan1_leadingSpan2_diff_rate'][i]) + )) + sell_text_list.append( + "{}, {}, {} ({:,.2f})

" + "avg5: {:.2f}, avg10: {:.2f}, avg20: {:.2f}, avg60: {:.2f}, avg90: {:.2f}, avg120: {:.2f}, avg240: {:.2f}
" + "avg360: {:.2f}, avg480: {:.2f}, avg720: {:.2f}, avg1440: {:.2f}, avg2880: {:.2f}

" + "loc_k: {:.2f}, loc_d: {:.2f}, loc_s: {:.2f}

" + "laggingSpan_close_diff: {:.4f} ({:.4f})
" + "laggingSpan_changeLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_baseLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan1_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan2_diff: {:.4f} ({:.4f})
" + "laggingSpan_avg60_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower10_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle10_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper10_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower20_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle20_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper20_diff: {:.4f} ({:.4f})
" + "baseLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_baseLine_diff: {:.4f} ({:.4f})
" + "leadingSpan1_leadingSpan2_diff: {:.4f} ({:.4f})
" + .format( + data['ymd'][i].strftime('%Y-%m-%d %H:%M'), sell_type[i], self.cz(sell_price_line[i]), self.cz(sell_price_line[i])*self.cz(sell_count_line[i]), + self.cz(data["avg5"][i]), self.cz(data["avg10"][i]), self.cz(data["avg20"][i]), self.cz(data["avg60"][i]), self.cz(data["avg90"][i]), self.cz(data["avg120"][i]), self.cz(data["avg240"][i]), self.cz(data["avg360"][i]), self.cz(data["avg480"][i]), self.cz(data["avg720"][i]), self.cz(data["avg1440"][i]), self.cz(data["avg2880"][i]), + self.cz(data['loc_240_k'][i]), self.cz(data['loc_240_d'][i]), self.cz(data['loc_240_s'][i]), + self.cz(data['laggingSpan_close_diff'][i]), self.cz(data['laggingSpan_close_diff_rate'][i]), + self.cz(data['laggingSpan_changeLine_diff'][i]), self.cz(data['laggingSpan_changeLine_diff_rate'][i]), + self.cz(data['laggingSpan_baseLine_diff'][i]), self.cz(data['laggingSpan_baseLine_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan1_diff'][i]), self.cz(data['laggingSpan_leadingSpan1_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan2_diff'][i]), self.cz(data['laggingSpan_leadingSpan2_diff_rate'][i]), + self.cz(data['laggingSpan_avg60_diff'][i]), self.cz(data['laggingSpan_avg60_diff_rate'][i]), + self.cz(data['laggingSpan_lower10_diff'][i]), self.cz(data['laggingSpan_lower10_diff_rate'][i]), + self.cz(data['laggingSpan_middle10_diff'][i]), self.cz(data['laggingSpan_middle10_diff_rate'][i]), + self.cz(data['laggingSpan_upper10_diff'][i]), self.cz(data['laggingSpan_upper10_diff_rate'][i]), + self.cz(data['laggingSpan_lower20_diff'][i]), self.cz(data['laggingSpan_lower20_diff_rate'][i]), + self.cz(data['laggingSpan_middle20_diff'][i]), self.cz(data['laggingSpan_middle20_diff_rate'][i]), + self.cz(data['laggingSpan_upper20_diff'][i]), self.cz(data['laggingSpan_upper20_diff_rate'][i]), + self.cz(data['baseLine_close_diff'][i]), self.cz(data['baseLine_close_diff_rate'][i]), + self.cz(data['changeLine_close_diff'][i]), self.cz(data['changeLine_close_diff_rate'][i]), + self.cz(data['changeLine_baseLine_diff'][i]), self.cz(data['changeLine_baseLine_diff_rate'][i]), + self.cz(data['leadingSpan1_leadingSpan2_diff'][i]), self.cz(data['leadingSpan1_leadingSpan2_diff_rate'][i]) + )) + buy_check = go.Scatter(x=data['ymd'], y=buy_price_line, mode='markers', name="buy_price", marker=dict(size=buy_sell_size, color=buy_colors, line_width=0), text=buy_text_list, hoverinfo="text") + sell_check = go.Scatter(x=data['ymd'], y=sell_price_line, mode='markers', name="sell_price", marker=dict(size=14, color=sell_colors, line_width=0), text=sell_text_list, hoverinfo="text") + + volume_line = go.Bar(x=data['ymd'], y=data["volume"], marker_color=volume_colors, name='volume') + + avg5 = go.Scatter(x=data['ymd'], y=data["avg5"], name="avg5", line_color='#079118') + avg10 = go.Scatter(x=data['ymd'], y=data["avg10"], name="avg10", line_color='grey') + avg20 = go.Scatter(x=data['ymd'], y=data["avg20"], name="avg20", line_color='#d755e8') + avg60 = go.Scatter(x=data['ymd'], y=data["avg60"], name="avg60", line_color='#099B92') + avg90 = go.Scatter(x=data['ymd'], y=data["avg90"], name="avg90", line_color='#2a9c0c') + avg120 = go.Scatter(x=data['ymd'], y=data["avg120"], name="avg120", line_color='#079118') + avg240 = go.Scatter(x=data['ymd'], y=data["avg240"], name="avg240", line_color='#e68456') + avg360 = go.Scatter(x=data['ymd'], y=data["avg360"], name="avg360", line_color='#e6b55c') + avg480 = go.Scatter(x=data['ymd'], y=data["avg480"], name="avg480", line_color='#2a9c0c') + avg720 = go.Scatter(x=data['ymd'], y=data["avg720"], name="avg720", line_color='#e75d53') + avg1440 = go.Scatter(x=data['ymd'], y=data["avg1440"], name="avg1440", line_color='#2a9c0c') + avg2880 = go.Scatter(x=data['ymd'], y=data["avg2880"], name="avg2880", line_color='#46406c') + + laggingSpan_0_8_limit = [0.8 for i in data['ymd']] + laggingSpan_0_2_limit = [0.2 for i in data['ymd']] + laggingSpan_0_limit = [0 for i in data['ymd']] + laggingSpan__0_2_limit = [-0.2 for i in data['ymd']] + laggingSpan__0_8_limit = [-0.8 for i in data['ymd']] + laggingSpan_0_8_limit_line = go.Scatter(x=data['ymd'], y=laggingSpan_0_8_limit, line=dict(color='grey', width=1), name='laggingSpan_0_8_limit') + laggingSpan_0_2_limit_line = go.Scatter(x=data['ymd'], y=laggingSpan_0_2_limit, line=dict(color='grey', width=1), name='laggingSpan_0_2_limit') + laggingSpan_0_limit_line = go.Scatter(x=data['ymd'], y=laggingSpan_0_limit, line=dict(color='grey', width=1), name='laggingSpan_0_limit') + laggingSpan__0_2_limit_line = go.Scatter(x=data['ymd'], y=laggingSpan__0_2_limit, line=dict(color='grey', width=1), name='laggingSpan__0_2_limit') + laggingSpan__0_8_limit_line = go.Scatter(x=data['ymd'], y=laggingSpan__0_8_limit, line=dict(color='grey', width=1), name='laggingSpan__0_8_limit') + + laggingSpan_close_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_close_diff"], name="laggingSpan_close_diff", line_color='#079118') + laggingSpan_changeLine_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_changeLine_diff"], name="laggingSpan_changeLine_diff", line_color='grey') + laggingSpan_baseLine_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_baseLine_diff"], name="laggingSpan_baseLine_diff", line_color='#d755e8') + laggingSpan_leadingSpan1_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_leadingSpan1_diff"], name="laggingSpan_leadingSpan1_diff", line_color='#d755e8') + laggingSpan_leadingSpan2_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_leadingSpan2_diff"], name="laggingSpan_leadingSpan2_diff", line_color='#d755e8') + laggingSpan_avg60_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_avg60_diff"], name="laggingSpan_avg60_diff", line_color='#d755e8') + laggingSpan_lower10_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_lower10_diff"], name="laggingSpan_lower10_diff", line_color='#d755e8') + laggingSpan_middle10_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_middle10_diff"], name="laggingSpan_middle10_diff", line_color='#d755e8') + laggingSpan_upper10_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_upper10_diff"], name="laggingSpan_upper10_diff", line_color='#d755e8') + laggingSpan_lower20_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_lower20_diff"], name="laggingSpan_lower20_diff", line_color='#d755e8') + laggingSpan_middle20_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_middle20_diff"], name="laggingSpan_middle20_diff", line_color='#d755e8') + laggingSpan_upper20_diff = go.Scatter(x=data['ymd'], y=data["laggingSpan_upper20_diff"], name="laggingSpan_upper20_diff", line_color='#d755e8') + baseLine_close_diff = go.Scatter(x=data['ymd'], y=data["baseLine_close_diff"], name="baseLine_close_diff", line_color='#d755e8') + changeLine_close_diff = go.Scatter(x=data['ymd'], y=data["changeLine_close_diff"], name="changeLine_close_diff", line_color='#d755e8') + changeLine_baseLine_diff = go.Scatter(x=data['ymd'], y=data["changeLine_baseLine_diff"], name="changeLine_baseLine_diff", line_color='#d755e8') + changeLine_leadingSpan1_diff = go.Scatter(x=data['ymd'], y=data["changeLine_leadingSpan1_diff"], name="changeLine_leadingSpan1_diff", line_color='#d755e8') + leadingSpan1_leadingSpan2_diff = go.Scatter(x=data['ymd'], y=data["leadingSpan1_leadingSpan2_diff"], name="leadingSpan1_leadingSpan2_diff", line_color='#d755e8') + + laggingSpan_close_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_close_diff_rate"], name="laggingSpan_close_diff_rate", line_color='#d755e8') + laggingSpan_changeLine_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_changeLine_diff_rate"], name="laggingSpan_changeLine_diff_rate", line_color='#d755e8') + laggingSpan_baseLine_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_baseLine_diff_rate"], name="laggingSpan_baseLine_diff_rate", line_color='#d755e8') + laggingSpan_leadingSpan1_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_leadingSpan1_diff_rate"], name="laggingSpan_leadingSpan1_diff_rate", line_color='#d755e8') + laggingSpan_leadingSpan2_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_leadingSpan2_diff_rate"], name="laggingSpan_leadingSpan2_diff_rate", line_color='#d755e8') + laggingSpan_avg60_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_avg60_diff_rate"], name="laggingSpan_avg60_diff_rate", line_color='#d755e8') + laggingSpan_lower10_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_lower10_diff_rate"], name="laggingSpan_lower10_diff_rate", line_color='#d755e8') + laggingSpan_middle10_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_middle10_diff_rate"], name="laggingSpan_middle10_diff_rate", line_color='#d755e8') + laggingSpan_upper10_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_upper10_diff_rate"], name="laggingSpan_upper10_diff_rate", line_color='#d755e8') + laggingSpan_lower20_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_lower20_diff_rate"], name="laggingSpan_lower20_diff_rate", line_color='#d755e8') + laggingSpan_middle20_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_middle20_diff_rate"], name="laggingSpan_middle20_diff_rate", line_color='#d755e8') + laggingSpan_upper20_diff_rate = go.Scatter(x=data['ymd'], y=data["laggingSpan_upper20_diff_rate"], name="laggingSpan_upper20_diff_rate", line_color='#d755e8') + baseLine_close_diff_rate = go.Scatter(x=data['ymd'], y=data["baseLine_close_diff_rate"], name="baseLine_close_diff_rate", line_color='#d755e8') + changeLine_close_diff_rate = go.Scatter(x=data['ymd'], y=data["changeLine_close_diff_rate"], name="changeLine_close_diff_rate", line_color='#d755e8') + changeLine_baseLine_diff_rate = go.Scatter(x=data['ymd'], y=data["changeLine_baseLine_diff_rate"], name="changeLine_baseLine_diff_rate", line_color='#d755e8') + changeLine_leadingSpan1_diff_rate = go.Scatter(x=data['ymd'], y=data["changeLine_leadingSpan1_diff_rate"], name="changeLine_leadingSpan1_diff_rate", line_color='#d755e8') + leadingSpan1_leadingSpan2_diff_rate = go.Scatter(x=data['ymd'], y=data["leadingSpan1_leadingSpan2_diff_rate"], name="leadingSpan1_leadingSpan2_diff_rate", line_color='#d755e8') + + changeLine = go.Scatter(x=data['ymd'], y=data["changeLine"], name="changeLine", line_color='#0196ff') + baseLine = go.Scatter(x=data['ymd'], y=data["baseLine"], name="baseLine", line_color='#991515') + laggingSpan = go.Scatter(x=data['ymd'], y=data["laggingSpan"], name="laggingSpan", line_color='#12A524') + leadingSpan1 = go.Scatter(x=data['ymd'], y=data["leadingSpan1"], name="leadingSpan1", line_color='#008001') + leadingSpan2 = go.Scatter(x=data['ymd'], y=data["leadingSpan2"], name="leadingSpan2", line_color='#830fd4') + + upper_10_Line = go.Scatter(x=data['ymd'], y=data["upper_10"], name="upper_10", line_color='#0196ff') + lower_10_Line = go.Scatter(x=data['ymd'], y=data["lower_10"], name="lower_10", line_color='#991515') + middle_10_line = go.Scatter(x=data['ymd'], y=data["middle_10"], name="middle_10", line_color='#12A524') + upper_20_Line = go.Scatter(x=data['ymd'], y=data["upper_20"], name="upper_20", line_color='#0196ff') + lower_20_Line = go.Scatter(x=data['ymd'], y=data["lower_20"], name="lower_20", line_color='#991515') + middle_20_line = go.Scatter(x=data['ymd'], y=data["middle_20"], name="middle_20", line_color='#12A524') + + loc_240_k = go.Scatter(x=data['ymd'], y=data["loc_240_k"], name="loc_240_k", line_color='#0196ff') + loc_240_d = go.Scatter(x=data['ymd'], y=data["loc_240_d"], name="loc_240_d", line_color='#991515') + loc_240_s = go.Scatter(x=data['ymd'], y=data["loc_240_s"], name="loc_240_s", line_color='#12A524') + + new_high_9 = go.Scatter(x=data['ymd'], y=data["new_high_9"], name="new_high_9", line_color='#0196ff') + new_high_26 = go.Scatter(x=data['ymd'], y=data["new_high_26"], name="new_high_26", line_color='#991515') + new_high_33 = go.Scatter(x=data['ymd'], y=data["new_high_33"], name="new_high_33", line_color='#12A524') + new_high_52 = go.Scatter(x=data['ymd'], y=data["new_high_52"], name="new_high_52", line_color='#099B92') + new_low_9 = go.Scatter(x=data['ymd'], y=data["new_low_9"], name="new_low_9", line_color='#0196ff') + new_low_26 = go.Scatter(x=data['ymd'], y=data["new_low_26"], name="new_low_26", line_color='#991515') + new_low_33 = go.Scatter(x=data['ymd'], y=data["new_low_33"], name="new_low_33", line_color='#12A524') + new_low_52 = go.Scatter(x=data['ymd'], y=data["new_low_52"], name="new_low_52", line_color='#099B92') + + + slowk_up_limit = [80 for i in data['ymd']] + slowk_middle_limit = [50 for i in data['ymd']] + slowk_down_limit = [20 for i in data['ymd']] + slowk_up_limit = go.Scatter(x=data['ymd'], y=slowk_up_limit, line=dict(color='grey', width=1), name='slowk_up_limit') + slowk_middle_limit = go.Scatter(x=data['ymd'], y=slowk_middle_limit, line=dict(color='grey', width=1), name='slowk_middle_limit') + slowk_down_limit = go.Scatter(x=data['ymd'], y=slowk_down_limit, line=dict(color='grey', width=1), name='slowk_down_limit') + + slowk_12 = go.Scatter(x=data['ymd'], y=data["slowk_12"], line=dict(color='#079118', width=2), name='slowk_12') + slowd_12 = go.Scatter(x=data['ymd'], y=data["slowd_12"], line=dict(dash='dashdot', color='#079118', width=2), name='slowd_12') + slowk_26 = go.Scatter(x=data['ymd'], y=data["slowk_26"], line=dict(color='grey', width=2), name='slowk_26') + slowd_26 = go.Scatter(x=data['ymd'], y=data["slowd_26"], line=dict(dash='dashdot', color='grey', width=2), name='slowd_26') + slowk_52 = go.Scatter(x=data['ymd'], y=data["slowk_52"], line=dict(color='#d755e8', width=2), name='slowk_52') + slowd_52 = go.Scatter(x=data['ymd'], y=data["slowd_52"], line=dict(dash='dashdot', color='#d755e8', width=2), name='slowd_52') + + + text_list = [] + for i in range(len(data['ymd'])): + text_list.append( + "{}

" + "avg5: {:.2f}, avg10: {:.2f}, avg20: {:.2f}, avg60: {:.2f}, avg90: {:.2f}, avg120: {:.2f}, avg240: {:.2f}
" + "avg360: {:.2f}, avg480: {:.2f}, avg720: {:.2f}, avg1440: {:.2f}, avg2880: {:.2f}

" + "loc_k: {:.2f}, loc_d: {:.2f}, loc_s: {:.2f}

" + "laggingSpan_close_diff: {:.4f} ({:.4f})
" + "laggingSpan_changeLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_baseLine_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan1_diff: {:.4f} ({:.4f})
" + "laggingSpan_leadingSpan2_diff: {:.4f} ({:.4f})
" + "laggingSpan_avg60_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower10_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle10_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper10_diff: {:.4f} ({:.4f})
" + "laggingSpan_lower20_diff: {:.4f} ({:.4f})
" + "laggingSpan_middle20_diff: {:.4f} ({:.4f})
" + "laggingSpan_upper20_diff: {:.4f} ({:.4f})
" + "baseLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_close_diff: {:.4f} ({:.4f})
" + "changeLine_baseLine_diff: {:.4f} ({:.4f})
" + "leadingSpan1_leadingSpan2_diff: {:.4f} ({:.4f})
" + .format( + data['ymd'][i].strftime('%Y-%m-%d %H:%M'), + self.cz(data["avg5"][i]), self.cz(data["avg10"][i]), self.cz(data["avg20"][i]), self.cz(data["avg60"][i]), self.cz(data["avg90"][i]), self.cz(data["avg120"][i]), self.cz(data["avg240"][i]), self.cz(data["avg360"][i]), self.cz(data["avg480"][i]), self.cz(data["avg720"][i]), self.cz(data["avg1440"][i]), self.cz(data["avg2880"][i]), + self.cz(data['loc_240_k'][i]), self.cz(data['loc_240_d'][i]), self.cz(data['loc_240_s'][i]), + self.cz(data['laggingSpan_close_diff'][i]), self.cz(data['laggingSpan_close_diff_rate'][i]), + self.cz(data['laggingSpan_changeLine_diff'][i]), self.cz(data['laggingSpan_changeLine_diff_rate'][i]), + self.cz(data['laggingSpan_baseLine_diff'][i]), self.cz(data['laggingSpan_baseLine_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan1_diff'][i]), self.cz(data['laggingSpan_leadingSpan1_diff_rate'][i]), + self.cz(data['laggingSpan_leadingSpan2_diff'][i]), self.cz(data['laggingSpan_leadingSpan2_diff_rate'][i]), + self.cz(data['laggingSpan_avg60_diff'][i]), self.cz(data['laggingSpan_avg60_diff_rate'][i]), + self.cz(data['laggingSpan_lower10_diff'][i]), self.cz(data['laggingSpan_lower10_diff_rate'][i]), + self.cz(data['laggingSpan_middle10_diff'][i]), self.cz(data['laggingSpan_middle10_diff_rate'][i]), + self.cz(data['laggingSpan_upper10_diff'][i]), self.cz(data['laggingSpan_upper10_diff_rate'][i]), + self.cz(data['laggingSpan_lower20_diff'][i]), self.cz(data['laggingSpan_lower20_diff_rate'][i]), + self.cz(data['laggingSpan_middle20_diff'][i]), self.cz(data['laggingSpan_middle20_diff_rate'][i]), + self.cz(data['laggingSpan_upper20_diff'][i]), self.cz(data['laggingSpan_upper20_diff_rate'][i]), + self.cz(data['baseLine_close_diff'][i]), self.cz(data['baseLine_close_diff_rate'][i]), + self.cz(data['changeLine_close_diff'][i]), self.cz(data['changeLine_close_diff_rate'][i]), + self.cz(data['changeLine_baseLine_diff'][i]), self.cz(data['changeLine_baseLine_diff_rate'][i]), + self.cz(data['leadingSpan1_leadingSpan2_diff'][i]), self.cz(data['leadingSpan1_leadingSpan2_diff_rate'][i]) + )) + + candle_stick = go.Candlestick(x=data['ymd'], + open=data['open'], high=data['high'], low=data['low'], close=data['close'], + increasing_line_color='red', decreasing_line_color='blue', + name='candle', text=text_list, hoverinfo="text" + ) + + if bsLine is not None: + candle_data = [avg5, avg10, avg20, avg60, avg90, avg120, avg240, avg360, avg480, avg720, avg1440, avg2880, buy_check, sell_check, candle_stick, changeLine, baseLine, laggingSpan, leadingSpan1, leadingSpan2, upper_10_Line, lower_10_Line, middle_10_line, upper_20_Line, lower_20_Line, middle_20_line] + else: + candle_data = [avg5, avg10, avg20, avg60, avg90, avg120, avg240, avg360, avg480, avg720, avg1440, avg2880, candle_stick,changeLine, baseLine, laggingSpan, leadingSpan1, leadingSpan2] + + volume_data = [volume_line] + disparity_data = [laggingSpan_close_diff, laggingSpan_changeLine_diff, laggingSpan_baseLine_diff, laggingSpan_leadingSpan1_diff, laggingSpan_leadingSpan2_diff, laggingSpan_avg60_diff, laggingSpan_lower10_diff, laggingSpan_middle10_diff, laggingSpan_upper10_diff, laggingSpan_lower20_diff, laggingSpan_middle20_diff, laggingSpan_upper20_diff, baseLine_close_diff, changeLine_close_diff, changeLine_baseLine_diff, changeLine_leadingSpan1_diff, leadingSpan1_leadingSpan2_diff] + loc_disparity_data = [laggingSpan_0_8_limit_line, laggingSpan_0_2_limit_line, laggingSpan_0_limit_line, laggingSpan__0_2_limit_line, laggingSpan__0_8_limit_line, + laggingSpan_close_diff_rate, laggingSpan_changeLine_diff_rate, laggingSpan_baseLine_diff_rate, laggingSpan_leadingSpan1_diff_rate, laggingSpan_leadingSpan2_diff_rate, laggingSpan_avg60_diff_rate, laggingSpan_lower10_diff_rate, laggingSpan_middle10_diff_rate, laggingSpan_upper10_diff_rate, laggingSpan_lower20_diff_rate, laggingSpan_middle20_diff_rate, laggingSpan_upper20_diff_rate, baseLine_close_diff_rate, changeLine_close_diff_rate, changeLine_baseLine_diff_rate,changeLine_leadingSpan1_diff_rate, leadingSpan1_leadingSpan2_diff_rate, + loc_240_k, loc_240_d, loc_240_s, + new_high_9 ,new_high_26, new_high_33, new_high_52,new_low_9 ,new_low_26, new_low_33, new_low_52 + ] + stochastic_data = [ + slowk_up_limit, slowk_middle_limit, slowk_down_limit, + slowk_12, slowd_12, + slowk_26, slowd_26, + slowk_52, slowd_52 + ] + # 그래프를 그린다. + """ + fig = go.Figure(data=candle_data) + fig.update_layout(title=stock_code) + fig.show() + """ fig = subplots.make_subplots( - rows=6, cols=1, - subplot_titles=("MACD", "스토캐스틱", "RSI", "이격도", "거래량", '캔들', ), - # specs=[[{}], [{}], [{}], [{}], [{}], [{}]], + rows=5, cols=1, + subplot_titles=("이격도", "이격도 위치", "캔들", "slowkd", "거래량"), shared_xaxes=True, horizontal_spacing=0.03, vertical_spacing=0.01, - row_heights=[200, 200, 200, 200, 200, 800] + row_heights=[200, 200, 700, 200, 200] ) - for trace in macd_data: - fig.append_trace(trace, 1, 1) - for trace in stochastic_data: - fig.append_trace(trace, 2, 1) - for trace in rsi_data: - fig.append_trace(trace, 3, 1) for trace in disparity_data: + fig.append_trace(trace, 1, 1) + for trace in loc_disparity_data: + fig.append_trace(trace, 2, 1) + for trace in candle_data: + fig.append_trace(trace, 3, 1) + for trace in stochastic_data: fig.append_trace(trace, 4, 1) for trace in volume_data: fig.append_trace(trace, 5, 1) - for trace in candle_data: - fig.append_trace(trace, 6, 1) - fig.update_layout(height=1900, + #fig.update_xaxes(nticks=5) + #fig.update_layout(height=2400, title=stock_code, xaxis_rangeslider_visible=False) + + df = pd.DataFrame(bsLine) + df = df.fillna(-1) + + + buy_count = 0 + if bsLine is not None: + buy_count = len(df.loc[df["buy_price"] > 0]) + fig.update_layout(height=1400, + title="{}, buy: {}번 ".format(stock_code, buy_count), xaxis_rangeslider_visible=False, - xaxis4_rangeslider_visible=False, - xaxis5_rangeslider_visible=False + xaxis2_rangeslider_visible=False, + xaxis3_rangeslider_visible=False, + xaxis4_rangeslider_visible=False ) + # 화면으로 출력함 return fig + def getPositionalEnergy(self, close): # 260 (= 52 * 5)일 중 가장 찾은 금액과 가장 높았던 금액 중 현재가의 위치 계산 @@ -322,7 +527,7 @@ class AnalyzerSqlite: return - def writeFile(self, dir_name, CODE, NAME, top, stock, state): + def writeFile(self, outPath, CODE, NAME, top, stock, bsLine): # 3년 이내 한번이라도 영업이익이 났는지 체크를 함 fnguide = None if CODE in self.fnguide: @@ -335,12 +540,11 @@ class AnalyzerSqlite: check = True if check: - fig = self.draw(stock) - title = "%s (%s), %d, %s 차트 (URL1, URL2, URL3)" % (NAME, CODE, stock['close'][0], dir_name, CODE, CODE, CODE) + fig = self.draw(CODE, stock, bsLine) + title = "%s (%s), 차트 (URL1, URL2, URL3)" % (NAME, CODE, CODE, CODE, CODE) fig['layout'].update(title=title) - fileName = self.outPath + "/" + dir_name - fileName = "%s/%s_%s_%s_%s_%s.html" % (fileName, datetime.today().strftime("%Y%m%d"), state, top, NAME.replace(" ", ""), CODE) + fileName = outPath + "/%s_%s_%s_%s.html" % (datetime.today().strftime("%Y%m%d"), top, NAME.replace(" ", ""), CODE) po.write_html(fig, file=fileName, auto_open=False) return @@ -363,122 +567,10 @@ class AnalyzerSqlite: return True return False - def getStockData(self, TableName, CODE): - conn = sqlite3.connect(self.stockFileName) - cursor = conn.cursor() + def getStockData(self, CODE): + data_daily, ci_daily = self.jSDPattern.getData(CODE, ymd=(datetime.now()+timedelta(days=1)).strftime('%Y%m%d'), get_days=1500) - sql = 'SELECT ymd, close, open, high, low, volume, ' - sql += ' avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440, ' - sql += ' disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480, ' - sql += ' bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb, ' - sql += ' envelope_upper, envelope_lower, envelope_middle, ' - sql += ' ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, ' - sql += ' stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, ' - sql += ' rsi, rsis, ' - sql += ' macd, macds, macdo, ' - sql += ' mfi, ' - sql += ' last_min, last_max, last_middle, ' - sql += ' trend, trend_k, trend_s ' - sql += ' FROM ' + TableName + ' where CODE=? order by ymd limit 1024 ' - cursor.execute(sql, (CODE,)) - prices = cursor.fetchall() - - cursor.close() - conn.close() - - ymd = [] - close, open, high, low, volume = [], [], [], [], [] - avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440 = [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [] - disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480 = [], [], [], [], [], [], [] - bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb = [], [], [], [], [] - envelope_upper, envelope_lower, envelope_middle = [], [], [] - ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2 = [], [], [], [], [] - stochastic_fast_k, stochastic_slow_k, stochastic_slow_d = [], [], [] - rsi, rsis = [], [] - macd, macds, macdo = [], [], [] - mfi = [] - last_min, last_max, last_middle = [], [], [] - trend, trend_k, trend_s = [], [], [] - - for price in prices: - ymd.append(price[0]) - close.append(price[1]) - open.append(price[2]) - high.append(price[3]) - low.append(price[4]) - volume.append(price[5]) - avg3.append(price[6]) - avg4.append(price[7]) - avg5.append(price[8]) - avg6.append(price[9]) - avg10.append(price[10]) - avg12.append(price[11]) - avg20.append(price[12]) - avg36.append(price[13]) - avg40.append(price[14]) - avg48.append(price[15]) - avg60.append(price[16]) - avg120.append(price[17]) - avg200.append(price[18]) - avg240.append(price[19]) - avg300.append(price[20]) - avg360.append(price[21]) - avg480.append(price[22]) - avg720.append(price[23]) - avg1440.append(price[24]) - disparity_avg5.append(price[25]) - disparity_avg10.append(price[26]) - disparity_avg20.append(price[27]) - disparity_avg60.append(price[28]) - disparity_avg120.append(price[29]) - disparity_avg240.append(price[30]) - disparity_avg480.append(price[31]) - bolingerband_upper.append(price[32]) - bolingerband_lower.append(price[33]) - bolingerband_middle.append(price[34]) - bolingerband_width.append(price[35]) - bolingerband_pb.append(price[36]) - envelope_upper.append(price[37]) - envelope_lower.append(price[38]) - envelope_middle.append(price[39]) - ichimokucloud_changeLine.append(price[40]) - ichimokucloud_baseLine.append(price[41]) - ichimokucloud_laggingSpan.append(price[42]) - ichimokucloud_leadingSpan1.append(price[43]) - ichimokucloud_leadingSpan2.append(price[44]) - stochastic_fast_k.append(price[45]) - stochastic_slow_k.append(price[46]) - stochastic_slow_d.append(price[47]) - rsi.append(price[48]) - rsis.append(price[49]) - macd.append(price[50]) - macds.append(price[51]) - macdo.append(price[52]) - mfi.append(price[53]) - last_min.append(price[54]) - last_max.append(price[55]) - last_middle.append(price[56]) - trend.append(price[57]) - trend_k.append(price[58]) - trend_s.append(price[59]) - - stock = { - "ymd": ymd, - "close": close, "open": open, "high": high, "low": low, "volume": volume, - "avg3": avg3, "avg4": avg4, "avg5": avg5, "avg6": avg6, "avg10": avg10, "avg12": avg12, "avg20": avg20, "avg36": avg36, "avg40": avg40, "avg48": avg48, "avg60": avg60, "avg120": avg120, "avg200": avg200, "avg240": avg240, "avg300": avg300, "avg360": avg360, "avg480": avg480, "avg720": avg720, "avg1440": avg1440, - "disparity_avg5": disparity_avg5, "disparity_avg10": disparity_avg10, "disparity_avg20": disparity_avg20, "disparity_avg60": disparity_avg60, "disparity_avg120": disparity_avg120, "disparity_avg240": disparity_avg240, "disparity_avg480": disparity_avg480, - "upper": bolingerband_upper, "lower": bolingerband_lower, "middle": bolingerband_middle, "width": bolingerband_width, "pb": bolingerband_pb, - "envelope_upper": envelope_upper, "envelope_lower": envelope_lower, "envelope_middle": envelope_middle, - "ichimokucloud_changeLine": ichimokucloud_changeLine, "ichimokucloud_baseLine": ichimokucloud_baseLine, "ichimokucloud_laggingSpan": ichimokucloud_laggingSpan, "ichimokucloud_leadingSpan1": ichimokucloud_leadingSpan1, "ichimokucloud_leadingSpan2": ichimokucloud_leadingSpan2, - "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, - "last_min": last_min, "last_max": last_max, "last_middle": last_middle, - "trend": trend, "trend_k": trend_k, "trend_s": trend_s - } - - return stock + return data_daily, ci_daily def makeDir(self, dir_name): @@ -487,30 +579,50 @@ class AnalyzerSqlite: os.mkdir(self.outPath + "/" + dir_name) return - def makeDirectory(self, outPath): - self.outPath = outPath - if os.path.isdir(outPath): - shutil.rmtree(outPath) - os.mkdir(outPath) + def checkTransaction(self, ticker, data, ci): - self.makeDir("monthly_env_하단_rsi_50") + # 어제 오늘 데이터로 분석 + bsLine = {} - self.makeDir("weekly_BB하단_내려옴") + if data is not None and 'close' in data.columns: + size = len(data["close"]) + bsLine['buy_ymd'] = [None for i in range(size)] + bsLine['buy_price'] = [0 for i in range(size)] + bsLine['buy_count'] = [0 for i in range(size)] + bsLine['buy_type'] = ['' for i in range(size)] + bsLine['buy_cut'] = [None for i in range(size)] + bsLine['sell_price'] = [0 for i in range(size)] + bsLine['sell_count'] = [0 for i in range(size)] + bsLine['sell_type'] = ['' for i in range(size)] + bsLine['sell_cut'] = [0 for i in range(size)] - self.makeDir("daily_이전에_없던_거래량") - self.makeDir("daily_final_candidate") + size = ci + start = 0 + for i in range(start, size): + + # 매도 확인 + sell_price, sell_count, sell_type = self.buySell_Daily.getSellPrice(data, i, bsLine) + bsLine['sell_price'][i] = sell_price + bsLine['sell_count'][i] = sell_count + bsLine['sell_type'][i] = sell_type + bsLine['sell_cut'][i] = 0 + + if sell_price < 1: + buy_ymd, buy_price, buy_count, buy_type, buy_cut = self.buySell_Daily.getBuyPrice(data, i, bsLine) + + bsLine['buy_ymd'][i] = buy_ymd + bsLine['buy_price'][i] = buy_price + bsLine['buy_count'][i] = buy_count + bsLine['buy_type'][i] = buy_type + bsLine['buy_cut'][i] = buy_cut + + return bsLine - return # 후보 찾기 def findCandidates(self, outPath): - self.makeDirectory(outPath) - stockTableName = 'stock' fnguideTableName = 'fnguide' - stockAnalysisTableName = 'stock_analysis' - stockAnalysisWeeklyTableName = 'stock_analysis_weekly' - stockAnalysisMonthlyTableName = 'stock_analysis_monthly' conn = sqlite3.connect(self.stockFileName) cursor = conn.cursor() @@ -522,522 +634,26 @@ class AnalyzerSqlite: cursor.close() conn.close() - # 상승 종목 개수 - param = {'bull': [], 'bear': [], 'even': []} - for i in range(60): - param['bull'].append(0) - param['bear'].append(0) - param['even'].append(0) - for idx, item in enumerate(items): - CODE = item[0] - stock_daily = self.getStockData(stockAnalysisTableName, CODE) - for c in range(len(stock_daily['open'])): - if c >= 60: - break - if stock_daily['open'][c] < stock_daily['close'][c]: - param['bull'][c] += 1 - elif stock_daily['close'][c] < stock_daily['open'][c]: - param['bear'][c] += 1 - else: - param['even'][c] += 1 - self.writeSummary(param) - for idx, item in enumerate(items): CODE = item[0] NAME = item[1] print("#", idx, ", CODE: ", CODE, ", NAME: ", NAME) + CODE = item[0] + stock_daily, ci = self.getStockData(CODE) - top = "0" - if CODE in self.topCompany: - top = str(self.topCompany[CODE][0]) + bsLine = self.checkTransaction(CODE, stock_daily, ci) - stock_daily = self.getStockData(stockAnalysisTableName, CODE) - stock_weekly = self.getStockData(stockAnalysisWeeklyTableName, CODE) - stock_monthly = self.getStockData(stockAnalysisMonthlyTableName, CODE) + if bsLine['buy_ymd'][ci-1] is not None: + top = "0" + if CODE in self.topCompany: + top = str(self.topCompany[CODE][0]) - count = 0 - # 거래량이 10만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94) - if stock_daily['volume'][-1] > 100000 and stock_daily['close'][-1] > 1000: - # 종목 상태 체크 분석 - - # Monthly 체크 - if len(stock_monthly['volume']) > 40: - - # ENV 하단 상향 돌파 - check = self.common.check_env_lower_rsi(stock_monthly) - if check: - count += 1 - dir_name = "monthly_env_하단_rsi_50" - log = "RSI_" + "{:.2f}".format(stock_monthly['rsi'][-1]) - self.writeFile(dir_name, CODE, NAME, top, stock_monthly, log) - - - # Weekly 체크 - if len(stock_weekly['volume']) > 40: - - # 볼린저 밴드 하단 아래 - check = self.common.check_under_BB_Low(stock_weekly) - if check: - count += 1 - dir_name = "weekly_BB하단_내려옴" - log = "BB_" + str(top) - self.writeFile(dir_name, CODE, NAME, top, stock_weekly, log) - - # 2) daily - if len(stock_daily['volume']) > 100: - - # 52주 200일 기준 평균 + 50% 보다 높은 거래량의 경우 - check, log = self.common.check_volume(stock_daily) - if check: - count += 1 - dir_name = "daily_이전에_없던_거래량" - log = "이전없던거래량_" + log - self.writeFile(dir_name, CODE, NAME, top, stock_daily, log) - - check = self.common.check_optimal_buy_timeing(param, stock_daily) - if check: - count += 1 - dir_name = "daily_final_candidate" - log = str(count) + "_" + dir_name + "_" - self.writeFile(dir_name, CODE, NAME, top, stock_daily, log) - return - - def get_moving_average(self, stock): - q_3 = MovingAverage(3) - q_4 = MovingAverage(4) - q_5 = MovingAverage(5) - q_6 = MovingAverage(6) - q_10 = MovingAverage(10) - q_12 = MovingAverage(12) - q_20 = MovingAverage(20) - q_30 = MovingAverage(30) - q_36 = MovingAverage(36) - q_40 = MovingAverage(40) - q_48 = MovingAverage(48) - q_60 = MovingAverage(60) - q_120 = MovingAverage(120) - q_200 = MovingAverage(200) - q_240 = MovingAverage(240) - q_300 = MovingAverage(300) - q_360 = MovingAverage(360) - q_480 = MovingAverage(480) - q_720 = MovingAverage(720) - q_1440 = MovingAverage(1440) - - for i in range(len(stock)): - q_3.enqueue(stock[i]['close']) - q_4.enqueue(stock[i]['close']) - q_5.enqueue(stock[i]['close']) - q_6.enqueue(stock[i]['close']) - q_10.enqueue(stock[i]['close']) - q_12.enqueue(stock[i]['close']) - q_20.enqueue(stock[i]['close']) - q_30.enqueue(stock[i]['close']) - q_36.enqueue(stock[i]['close']) - q_40.enqueue(stock[i]['close']) - q_48.enqueue(stock[i]['close']) - q_60.enqueue(stock[i]['close']) - q_120.enqueue(stock[i]['close']) - q_200.enqueue(stock[i]['close']) - q_240.enqueue(stock[i]['close']) - q_300.enqueue(stock[i]['close']) - q_360.enqueue(stock[i]['close']) - q_480.enqueue(stock[i]['close']) - q_720.enqueue(stock[i]['close']) - q_1440.enqueue(stock[i]['close']) - - stock[i]['avg3'] = q_3.avg() - stock[i]['avg4'] = q_4.avg() - stock[i]['avg5'] = q_5.avg() - stock[i]['avg6'] = q_6.avg() - stock[i]['avg10'] = q_10.avg() - stock[i]['avg12'] = q_12.avg() - stock[i]['avg20'] = q_20.avg() - stock[i]['avg30'] = q_30.avg() - stock[i]['avg36'] = q_36.avg() - stock[i]['avg40'] = q_40.avg() - stock[i]['avg48'] = q_48.avg() - stock[i]['avg60'] = q_60.avg() - stock[i]['avg120'] = q_120.avg() - stock[i]['avg200'] = q_200.avg() - stock[i]['avg240'] = q_240.avg() - stock[i]['avg300'] = q_300.avg() - stock[i]['avg360'] = q_360.avg() - stock[i]['avg480'] = q_480.avg() - stock[i]['avg720'] = q_720.avg() - stock[i]['avg1440'] = q_1440.avg() - - return - - def get_disparity(self, stock): - for i in range(len(stock)): - stock[i]['disparity_avg5'] = 100 * (stock[i]["open"] / stock[i]["avg5"]) - stock[i]['disparity_avg10'] = 100 * (stock[i]["open"] / stock[i]["avg10"]) - stock[i]['disparity_avg20'] = 100 * (stock[i]["open"] / stock[i]["avg20"]) - stock[i]['disparity_avg60'] = 100 * (stock[i]["open"] / stock[i]["avg60"]) - stock[i]['disparity_avg120'] = 100 * (stock[i]["open"] / stock[i]["avg120"]) - stock[i]['disparity_avg240'] = 100 * (stock[i]["open"] / stock[i]["avg240"]) - stock[i]['disparity_avg480'] = 100 * (stock[i]["open"] / stock[i]["avg480"]) - - return - - def convertFormat(self, weekDict): - previous_close = 0 - stock_price = [] - for ts in weekDict['open']: - stock_price.append( - { - "ymd": ts.strftime("%Y.%m.%d"), - "close": weekDict['close'][ts], - "diff": weekDict['close'][ts] - previous_close, - "open": weekDict['open'][ts], - "high": weekDict['high'][ts], - "low": weekDict['low'][ts], - "volume": weekDict['volume'][ts], - "avg3": -1, - "avg4": -1, - "avg5": -1, - "avg6": -1, - "avg10": -1, - "avg12": -1, - "avg20": -1, - "avg30": -1, - "avg36": -1, - "avg40": -1, - "avg48": -1, - "avg60": -1, - "avg120": -1, - "avg200": -1, - "avg240": -1, - "avg300": -1, - "avg360": -1, - "avg480": -1, - "avg720": -1, - "avg1440": -1, - "disparity_avg5": -1, - "disparity_avg10": -1, - "disparity_avg20": -1, - "disparity_avg60": -1, - "disparity_avg120": -1, - "disparity_avg240": -1, - "disparity_avg480": -1, - "bolingerband_upper": -1, - "bolingerband_lower": -1, - "bolingerband_middle": -1, - "bolingerband_width": -1, - "bolingerband_pb": -1, - "ichimokucloud_changeLine": -1, - "ichimokucloud_baseLine": -1, - "ichimokucloud_leadingSpan1": -1, - "ichimokucloud_leadingSpan2": -1, - "stochastic_fast_k": -1, - "stochastic_slow_k": -1, - "stochastic_slow_d": -1, - "rsi": -1, - "rsis": -1, - "macd": -1, - "macds": -1, - "macdo": -1, - "mfi": -1, - "last_min": -1, - "last_max": -1, - "last_middle": -1, - "trend": -1, - "trend_k": -1, - "trend_s": -1 - } - ) - previous_close = weekDict['close'][ts] - - return stock_price - - def analyzeAdditionalInfo(self, stock, cursor, type=None): - if type==None: - stockAnalysisTableName = 'stock_analysis' - else: - 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, mfi REAL, last_min REAL, last_max REAL, last_middle REAL, trend REAL, trend_k REAL, trend_s REAL)") - - # 키 생성 - create_key = "CREATE INDEX IF NOT EXISTS " + stockAnalysisTableName + "_idx on " + stockAnalysisTableName + " (CODE, ymd) " - cursor.execute(create_key) - - # 이동 평균 계산 - stock["PRICE"] = sorted(stock["PRICE"], key=lambda x: x['ymd']) - self.get_moving_average(stock["PRICE"]) - - # 이동 평균을 이용한 이격도 계산 - self.get_disparity(stock["PRICE"]) - - self.ichimokuCloud.analyze(stock) - self.stochastic.analyze(stock) - self.bolingerBand.analyze(stock) - 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): - # 트렌드: 실시간 정상 작동 안됨 - size = int(len(close_list) / 100) - pos = round(size / 2) - close_temp = close_list + [close_list[-1]] * pos - decomposition_results = seasonal_decompose(close_temp, model='multiplicative', period=size) - trend = decomposition_results.trend[:-pos] - trend_df = pd.DataFrame(trend).fillna(close_list[0]) - trend_list = trend_df.values.tolist() - for i, price in enumerate(stock['PRICE']): - price['trend'] = trend_list[i][0] - - trend_s_df = trend_df.rolling(120).mean() - trend_s_list = trend_s_df.values.tolist() - for i, price in enumerate(stock['PRICE']): - price['trend_s'] = trend_s_list[i][0] - - trend_k_df = trend_df.rolling(5).mean() - trend_k_list = trend_k_df.values.tolist() - for i, price in enumerate(stock['PRICE']): - price['trend_k'] = trend_k_list[i][0] - - - size = int(len(close_list) / 25) - pos = round(size / 2) - close_temp = close_list + [close_list[-1]] * pos - decomposition_results = seasonal_decompose(close_temp, model='multiplicative', period=size) - trend = decomposition_results.trend[:-pos] - trend_df = pd.DataFrame(trend).fillna(close_list[0]) - trend_list = trend_df.values.tolist() - for i, price in enumerate(stock['PRICE']): - price['trend'] = trend_list[i][0] - else: - for i, price in enumerate(stock['PRICE']): - price['trend'] = stock['PRICE'][i]['avg120'] - price['trend_s'] = stock['PRICE'][i]['avg120'] - price['trend_k'] = stock['PRICE'][i]['avg120'] - - - last_min = np.array([np.min(close_list[i - 60:i]) if 19 < i else np.min(close_list[:i+1]) for i in range(len(close_list))]) - last_max = np.array([np.max(close_list[i - 60:i]) if 19 < i else np.max(close_list[:i+1]) for i in range(len(close_list))]) - last_middle = (last_max + last_min) / 2 - for i, price in enumerate(stock['PRICE']): - price['last_min'] = last_min[i] - price['last_max'] = last_max[i] - price['last_middle'] = last_middle[i] - - sorted_stock = sorted(stock["PRICE"], key=lambda x: x['ymd'], reverse=True) - for price in sorted_stock: - cursor.execute('SELECT * FROM ' + stockAnalysisTableName + ' WHERE CODE=? and ymd=?', (stock['CODE'], price['ymd'],)) - result = cursor.fetchone() - if result == None: - sql = "INSERT INTO " + stockAnalysisTableName + "(CODE, NAME, ymd, close, diff, open, high, low, volume, " - sql += " avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440, " - sql += " disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480, " - sql += " bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb, " - sql += " envelope_upper, envelope_lower, envelope_middle, " - 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 += " last_min, last_max, last_middle, " - sql += " trend, trend_k, trend_s) " - sql += " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - - cursor.execute(sql, ( - stock["CODE"], stock["NAME"], price['ymd'], price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'], - price['avg3'], price['avg4'], price['avg5'], price['avg6'], price['avg10'], price['avg12'], price['avg20'], price['avg36'], price['avg40'], price['avg48'], price['avg60'], price['avg120'], price['avg200'], price['avg240'], price['avg300'], price['avg360'], price['avg480'], price['avg720'], price['avg1440'], - price['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'], price['disparity_avg240'], price['disparity_avg480'], - price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'], price['bolingerband_width'], price['bolingerband_pb'], - price['envelope_upper'], price['envelope_lower'], price['envelope_middle'], - 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['last_min'], price['last_max'], price['last_middle'], - price['trend'],price['trend_k'],price['trend_s'], - )) - - else: - sql = "UPDATE " + stockAnalysisTableName + " SET close=?, diff=?, open=?, high=?, low=?, volume=?, " - sql += " avg3=?, avg4=?, avg5=?, avg6=?, avg10=?, avg12=?, avg20=?, avg36=?, avg40=?, avg48=?, avg60=?, avg120=?, avg200=?, avg240=?, avg300=?, avg360=?, avg480=?, avg720=?, avg1440=?, " - sql += " disparity_avg5=?, disparity_avg10=?, disparity_avg20=?, disparity_avg60=?, disparity_avg120=?, disparity_avg240=?, disparity_avg480=?, " - sql += " bolingerband_upper=?, bolingerband_lower=?, bolingerband_middle=?, bolingerband_width=?, bolingerband_pb=?," - sql += " envelope_upper=?, envelope_lower=?, envelope_middle=?, " - sql += " ichimokucloud_changeLine=?, ichimokucloud_baseLine=?, ichimokucloud_laggingSpan=?, ichimokucloud_leadingSpan1=?, ichimokucloud_leadingSpan2=?, " - sql += " stochastic_fast_k=?, stochastic_slow_k=?, stochastic_slow_d=?, " - sql += " rsi=?, rsis=?, " - sql += " macd=?, macds=?, macdo=?, " - sql += " mfi=?, " - sql += " last_min=?, last_max=?, last_middle=?, " - sql += " trend=?, trend_k=?, trend_s=? " - sql += " WHERE CODE=? and ymd=?" - - cursor.execute(sql, - (price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'], - price['avg3'], price['avg4'], price['avg5'], price['avg6'], price['avg10'], price['avg12'], price['avg20'], price['avg36'], price['avg40'], price['avg48'], price['avg60'], price['avg120'], price['avg200'], price['avg240'], price['avg300'], price['avg360'], price['avg480'], price['avg720'], price['avg1440'], - price['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'], price['disparity_avg240'], price['disparity_avg480'], - price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'], price['bolingerband_width'], price['bolingerband_pb'], - price['envelope_upper'], price['envelope_lower'], price['envelope_middle'], - 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['last_min'], price['last_max'], price['last_middle'], - price['trend'], price['trend_k'], price['trend_s'], - stock["CODE"], price['ymd'],)) - break - - cursor.execute("commit",) - return - - def setItem(self, item): - return { - "ymd": item[0], - "close": item[1], - "diff": item[2], - "open": item[3], - "high": item[4], - "low": item[5], - "volume": item[6], - "avg3": -1, - "avg4": -1, - "avg5": -1, - "avg6": -1, - "avg10": -1, - "avg12": -1, - "avg20": -1, - "avg36": -1, - "avg40": -1, - "avg48": -1, - "avg60": -1, - "avg120": -1, - "avg200": -1, - "avg240": -1, - "avg300": -1, - "avg360": -1, - "avg480": -1, - "avg720": -1, - "avg1440": -1, - "disparity_avg5": -1, - "disparity_avg10": -1, - "disparity_avg20": -1, - "disparity_avg60": -1, - "disparity_avg120": -1, - "disparity_avg240": -1, - "disparity_avg480": -1, - "bolingerband_upper": -1, - "bolingerband_lower": -1, - "bolingerband_middle": -1, - "bolingerband_width": -1, - "bolingerband_pb": -1, - "envelope_upper": -1, - "envelope_lower": -1, - "envelope_middle": -1, - "ichimokucloud_changeLine": -1, - "ichimokucloud_baseLine": -1, - "ichimokucloud_laggingSpan": -1, - "ichimokucloud_leadingSpan1": -1, - "ichimokucloud_leadingSpan2": -1, - "stochastic_fast_k": -1, - "stochastic_slow_k": -1, - "stochastic_slow_d": -1, - "rsi": -1, - "rsis": -1, - "macd": -1, - "macds": -1, - "macdo": -1, - "mfi": -1, - "last_min": -1, - "last_max": -1, - "last_middle": -1, - "trend": -1, - "trend_k": -1, - "trend_s": -1 - } - - def analyzeDaily(self): - stockTableName = 'stock' - - conn = sqlite3.connect(self.stockFileName) - cursor = conn.cursor() - - cursor.execute('SELECT distinct code, name FROM ' + stockTableName + ' order by code') - items = cursor.fetchall() - - for rowid, item in enumerate(items): - - stock = {"CODE": item[0], "NAME": item[1], "PRICE":[]} - print("Daily # :", rowid, ", CODE: ", stock['CODE'], ", NAME: ", stock['NAME']) - - sql = 'SELECT ymd, close, diff, open, high, low, volume FROM ' + stockTableName + ' where CODE=? order by ymd ' - sql += ' limit 500' - cursor.execute(sql, (stock['CODE'],)) - items = cursor.fetchall() - - for item in items: - stock['PRICE'].append( self.setItem(item) ) - - self.analyzeAdditionalInfo(stock, cursor) - - conn.commit() - cursor.close() - conn.close() - - return - - - def analyzeGrouping(self, type): - stockTableName = 'stock' - - conn = sqlite3.connect(self.stockFileName) - cursor = conn.cursor() - - cursor.execute('SELECT distinct code, name FROM ' + stockTableName + ' order by code') - items = cursor.fetchall() - - for rowid, item in enumerate(items): - stock = {"CODE": item[0], "NAME": item[1], "PRICE": []} - print(type, "# :", rowid, ", CODE: ", stock['CODE'], ", NAME: ", stock['NAME']) - - sql = 'SELECT ymd, close, diff, open, high, low, volume FROM ' + stockTableName + ' where CODE=? order by ymd ' - #sql += ' limit 350' - cursor.execute(sql, (stock['CODE'],)) - items = cursor.fetchall() - - for item in items: - stock['PRICE'].append( - self.setItem(item) - ) - - agg_dict = {'open': 'first', - 'high': 'max', - 'low': 'min', - 'close': 'last', - 'volume': 'sum'} - - df = pd.DataFrame(stock['PRICE']) - df['ymd'] = pd.to_datetime(df['ymd']) - df.set_index('ymd', inplace=True) - - if type == "weekly": - condition="W" - else: - condition='M' - df_group = df.resample(condition).agg(agg_dict) - df_group = df_group.dropna() - df_group.merge(df, ) - stock['PRICE'] = self.convertFormat(df_group.to_dict()) - - self.analyzeAdditionalInfo(stock, cursor, type) - - conn.commit() - cursor.close() - conn.close() + # 거래량이 10만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94) + if stock_daily['volume'][ci-1] > 100000 and stock_daily['close'][ci-1] > 1000: + # 종목 상태 체크 분석 + self.writeFile(outPath, CODE, NAME, top, stock_daily, bsLine) return @@ -1047,12 +663,8 @@ if __name__ == "__main__": PROJECT_HOME = os.path.join(os.path.dirname(os.path.join(os.path.dirname(os.path.join(os.path.dirname(__file__)))))) RESOURCE_PATH = os.path.join(PROJECT_HOME, 'resources') - stockFileName = os.path.join(RESOURCE_PATH, 'stock.db') - analyzer = AnalyzerSqlite(stockFileName) + analyzer = AnalyzerSqlite(RESOURCE_PATH) - analyzer.analyzeDaily() - analyzer.analyzeGrouping("weekly") - analyzer.analyzeGrouping("monthly") # HTML 출력 outPath = os.path.join(PROJECT_HOME, "resources", "analysis") diff --git a/stock/analysis/JSDPattern.py b/stock/analysis/JSDPattern.py new file mode 100644 index 0000000..d2951da --- /dev/null +++ b/stock/analysis/JSDPattern.py @@ -0,0 +1,571 @@ +# https://bibot.tistory.com/63 +# https://nonmeyet.tistory.com/entry/Python-TALib%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8%EC%A3%BC%EA%B0%80%EA%B8%B0%EC%88%A0%EB%B6%84%EC%84%9D-%EB%B3%B4%EC%A1%B0%EC%A7%80%ED%91%9C-%EC%B6%94%EA%B0%80 +# https://lunadaddy.tistory.com/122 +# https://wikidocs.net/186885 + +import os +import numpy as np +np.seterr(divide='ignore', invalid='ignore') +import sqlite3 +# https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib +# https://lunadaddy.tistory.com/122 +import talib +import pandas as pd +from datetime import datetime, timedelta + +from stock.analysis.IchimokuCloud import IchimokuCloud +from sklearn.preprocessing import StandardScaler + +class JSDPattern: + stockFileName = None + ichimokuCloud = None + scaler = None + + def __init__(self, stockFileName=None): + self.stockFileName = stockFileName + + self.ichimokuCloud = IchimokuCloud() + self.scaler = StandardScaler() + return + + def makeTickData(self, data, mins=1): + result = { + "ymd": [], + "open": [], "close": [], "high": [], "low": [], "volume": [], "volume_up": [], "volume_down": [], "volume_updown_diff": [] + } + + for i in range(mins, len(data['ymd'])+1, mins): + result["ymd"].append(data['ymd'][i-1]) + + result["open"].append(data['open'][i-mins]) + result["close"].append(data['close'][i-1]) + result["high"].append(max(data['high'][i - mins: i])) + result["low"].append(min(data['low'][i - mins: i])) + result["volume"].append(data['volume'][i-1]) + + if data['open'][i-1] < data['close'][i-1]: + result["volume_up"].append(data['volume'][i-1]) + result["volume_down"].append(0) + elif data['close'][i-1] < data['open'][i-1]: + result["volume_down"].append(-1*data['volume'][i-1]) + result["volume_up"].append(0) + else: + result["volume_up"].append(0) + result["volume_down"].append(0) + + up = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['close'][i - mins + c] < data['open'][i - mins + c]] + down = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['close'][i - mins + c] < data['open'][i - mins + c]] + result["volume_updown_diff"].append(sum(up) - sum(down)) + + return result + + def append(self, df=None, result=None): + data = { + "ymd": [], + "open": [], "close": [], "high": [], "low": [], "volume": [] + } + + if result is not None: + for i in range(len(result['ymd'])): + data['ymd'].append(result['ymd'][i]) + data['open'].append(result['open'][i]) + data['close'].append(result['close'][i]) + data['high'].append(result['high'][i]) + data['low'].append(result['low'][i]) + data['volume'].append(result['volume'][i]) + + if df is not None: + for i in range(len(df)): + data['ymd'].append(df.index[i]) + data['open'].append(df['open'][i]) + data['close'].append(df['close'][i]) + data['high'].append(df['high'][i]) + data['low'].append(df['low'][i]) + data['volume'].append(df['volume'][i]) + + return data + + def getDBData(self, stock_code, day, get_days=14): + + table = 'stock' + + conn = sqlite3.connect(self.stockFileName) + cursor = conn.cursor() + + result = {"ymd": [], "open": [], "close": [], "high": [], "low": [], "volume": []} + for i in range(get_days, -1, -1): + this_day = (datetime.strptime(day, '%Y%m%d') - timedelta(i)).strftime('%Y.%m.%d') + cursor.execute('SELECT ymd, open, high, low, close, volume FROM ' + table + ' WHERE CODE=? and ymd=? order by ymd', (stock_code, this_day,)) + + db_result = cursor.fetchall() + for rows in db_result: + ymd = datetime.strptime(rows[0], '%Y.%m.%d') # hts.날짜 + open = rows[1] # hts.시가 + high = rows[2] # hts.고가 + low = rows[3] # hts.저가 + close = rows[4] # hts.종가 + vol = rows[5] # hts.거래량 + + result["ymd"].append(ymd) + result["open"].append(float(open)) + result["close"].append(float(close)) + result["high"].append(float(high)) + result["low"].append(float(low)) + result["volume"].append(float(vol)) + + cursor.close() + conn.close() + + return result + + def getCoinData(self, ticker, ymd=None, get_days=14): + result = self.getDBData(ticker, ymd, get_days=get_days) + data = self.append(df=None, result=result) + + return data + + def is_Support(self, low, i, observation_time=5): + # https://sine-qua-none.tistory.com/198 + + # c1 = df.Low[i] < df.Low[i - 1] < df.Low[i - 2] < df.Low[i - 3] + # c2 = df.Low[i] < df.Low[i + 1] < df.Low[i + 2] < df.Low[i + 3] + # return c1 & c2 + + #if low[i] == np.min(low[i - 2*self.observation_time:i + 1]): + if low[i] == np.min(low[i - observation_time:i + observation_time + 1]): + return True + else: + return False + + def is_Resistance(self, high, i, observation_time=5): + # https://sine-qua-none.tistory.com/198 + + # c1 = df.High[i] > df.High[i - 1] > df.High[i - 2] > df.High[i - 3] + # c2 = df.High[i] > df.High[i + 1] > df.High[i + 2] > df.High[i + 3] + # return c1 & c2 + # if df['high'][i] == np.max(df['high'][i - self.observation_time:i + self.observation_time + 1]): + #if high[i] == np.max(high[i - 2*self.observation_time:i + 1]): + if high[i] == np.max(high[i - observation_time:i + observation_time + 1]): + return True + else: + return False + + + def getDiff_Rate(self, price1, price2, duration=1440, move=None): + # price1: close, price2: laggingSpan_27 + diff = [0 for i in range(len(price1))] + diff_rate = [0 for i in range(len(price1))] + + for i in range(0, len(price1)): + if price1[i] is not None and price2[i] is not None: + diff[i] = price1[i] - price2[i] + else: + diff[i] = None + + if len(price1) < duration: + duration = 52 + + for i in range(0, len(price1)): + + if duration <= i: + l = [d for d in diff[i - duration:i + 1] if d is not None and 0 < d] + if 0 < len(l): + min_v_p = np.min(l) + else: + min_v_p = 0 + l = [d for d in diff[i - duration:i + 1] if d is not None and 0 < d] + if 0 < len(l): + max_v_p = np.max(l) + else: + max_v_p = 0 + l = [d for d in diff[i - duration:i + 1] if d is not None and d < 0] + if 0 < len(l): + min_v_m = np.min(l) + else: + min_v_m = 0 + l = [d for d in diff[i - duration:i + 1] if d is not None and d < 0] + if 0 < len(l): + max_v_m = np.max(l) + else: + max_v_m = 0 + + if diff[i] is not None: + if 0 <= diff[i]: + if max_v_p - min_v_p == 0: + diff_rate[i] = 0 + else: + diff_rate[i] = (diff[i] - min_v_p) / (max_v_p - min_v_p) + else: + if max_v_m - min_v_m == 0: + diff_rate[i] = 0 + else: + diff_rate[i] = ((diff[i] - min_v_m) / (max_v_m - min_v_m)) - 1 + else: + diff_rate[i] = None + + return diff, diff_rate + + def analyze(self, result, mins=1): + result["volume"] = [result["volume"][i] if 0 < result["volume"][i] else 1 for i in range(len(result["volume"]))] + # 기본 캔들 정보 + open_df = pd.DataFrame(result["open"]) + close_df = pd.DataFrame(result["close"]) + high_df = pd.DataFrame(result["high"]) + low_df = pd.DataFrame(result["low"]) + volume_df = pd.DataFrame(result["volume"]) + + # 중복 제거 + ymd_df = pd.DataFrame(result["ymd"]) + data_dup = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + data_dup.columns = ["ymd", "open", "close", "high", "low", "volume"] + data_dup.index = pd.DatetimeIndex(result["ymd"]) + data_dup_sorted = data_dup.sort_index(ascending=True) + data_dup_sorted = data_dup_sorted.drop_duplicates() + + ymd_df = data_dup_sorted["ymd"] + open_df = data_dup_sorted["open"] + close_df = data_dup_sorted["close"] + high_df = data_dup_sorted["high"] + low_df = data_dup_sorted["low"] + volume_df = data_dup_sorted["volume"] + + ymd = ymd_df.tolist() + open = open_df.tolist() + close = close_df.tolist() + high = high_df.tolist() + low = low_df.tolist() + volume = volume_df.tolist() + + # ichimokuCloud + df = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + column_names = ['DATE', 'open', 'close', 'high', 'low', 'volume'] + df.columns = column_names + c, b, l, s = 9, 26, 52, 26 + # 1. 전환선 = (과거 9일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 9일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + changeLine = (df.high.rolling(c).max() + df.low.rolling(c).min()) / 2 + # 2. 기준선 = 과거 26일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 26일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + baseLine = (df.high.rolling(b).max() + df.low.rolling(b).min()) / 2 + + # 3. 후행스팬 = 현재 close가격의 26일전 반영 + laggingSpan = [df.close.values[i + s] for i in range(len(df.close) - s)] + laggingSpan += [None for i in range(s)] + laggingSpan = np.array(laggingSpan) + + # 4. 선행스팬 1 = ((기준선 + 전환선) / 2)를 26일 선행하여 배치 + # 전환선과 기준선의 평균값을 구해 당일 포함 26일 앞으로 이동시킨 선 (중-단기 구간의 힘을 보여줌) + tmp_leadingSpan1 = (changeLine + baseLine) / 2 + """ S: 26일 선행시킴 """ + leadingSpan1 = list(tmp_leadingSpan1.values) + for i in range(b - 1): + leadingSpan1.insert(0, None) + """ E: 26일 선행시킴 """ + + # 5. 선행스팬 2 = ((최근 52일 동안 최고가 + 최저가) / 2)를 26일 선행하여 배치 + # 당일을 포함한 52일 동안의 최고가와 최저가의 평균을 26일 앞으로 이동시킨 선 (장기으로 형성된 선이기 때문에 가장 느리게 변함) + tmp_leadingSpan2 = (df.high.rolling(l).max() + df.low.rolling(l).min()) / 2 + """ S: 52일 선행시킴 """ + leadingSpan2 = list(tmp_leadingSpan2.values) + for i in range(l - 1): + leadingSpan2.insert(0, None) + """ S: 52일 선행시킴 """ + + baseLine = baseLine.tolist() + changeLine = changeLine.tolist() + laggingSpan = list(laggingSpan) + current_index = len(ymd) + for i in range(51): + if len(ymd) < len(leadingSpan2): + if mins==1440: + ymd.append(ymd[-1] + timedelta(days=1)) + else: + ymd.append(ymd[-1] + timedelta(minutes=1)) + if len(open) < len(leadingSpan2): + open.append(None) + if len(close) < len(leadingSpan2): + close.append(None) + if len(high) < len(leadingSpan2): + high.append(None) + if len(low) < len(leadingSpan2): + low.append(None) + if len(volume) < len(leadingSpan2): + volume.append(None) + if len(baseLine) < len(leadingSpan2): + baseLine.append(None) + if len(changeLine) < len(leadingSpan2): + changeLine.append(None) + if len(laggingSpan) < len(leadingSpan2): + laggingSpan.append(None) + for i in range(26): + if len(leadingSpan1) < len(leadingSpan2): + leadingSpan1.append(leadingSpan1[-1]) + + # 9일 신고가 + new_high_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and max(close[c-8:c]) < close[c] else 0 for c in range(8, len(close))] + # 26일 신고가 + new_high_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and max(close[c-25:c]) < close[c] else 0 for c in range(25, len(close))] + # 33일 신고가 + new_high_33 = [0 for c in range(32)] + [1 if (leadingSpan1[c-1] is not None and leadingSpan1[c] is not None and leadingSpan1[c-1] < leadingSpan1[c]) and None not in close[c-8:c+1] and max(close[c-32:c]) < close[c] else 0 for c in range(32, len(close))] + # 52일 신고가 + new_high_52 = [0 for c in range(51)] + [1 if (leadingSpan2[c-1] is not None and leadingSpan2[c] is not None and leadingSpan2[c-1] < leadingSpan2[c]) and None not in close[c-8:c+1] and max(close[c-51:c]) < close[c] else 0 for c in range(51, len(close))] + + # 9일 신저가 + new_low_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and close[c-9] < min(close[c-8:c+1]) else 0 for c in range(8, len(close))] + # 26일 신저가 + new_low_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and close[c-26] < min(close[c-25:c+1]) else 0 for c in range(25, len(close))] + # 33일 신저가 + new_low_33 = [0 for c in range(32)] + [1 if (leadingSpan1[c-1] is not None and leadingSpan1[c] is not None and leadingSpan1[c-1] < leadingSpan1[c]) and None not in close[c-8:c+1] and close[c-33] < min(close[c-32:c+1]) else 0 for c in range(32, len(close))] + # 52일 신저가 + new_low_52 = [0 for c in range(51)] + [1 if (leadingSpan2[c-1] is not None and leadingSpan2[c] is not None and leadingSpan2[c-1] < leadingSpan2[c]) and None not in close[c-8:c+1] and close[c-52] < min(close[c-51:c+1]) else 0 for c in range(51, len(close))] + + + # 이동 평균 + close_df = pd.DataFrame(close) + avg5 = list(np.reshape(close_df.ewm(5).mean().values, -1)) + avg10 = list(np.reshape(close_df.ewm(10).mean().values, -1)) + avg20 = list(np.reshape(close_df.ewm(20).mean().values, -1)) + avg60 = list(np.reshape(close_df.ewm(60).mean().values, -1)) + avg90 = list(np.reshape(close_df.ewm(90).mean().values, -1)) + avg120 = list(np.reshape(close_df.ewm(120).mean().values, -1)) + avg240 = list(np.reshape(close_df.ewm(240).mean().values, -1)) + avg360 = list(np.reshape(close_df.ewm(360).mean().values, -1)) + avg480 = list(np.reshape(close_df.ewm(480).mean().values, -1)) + avg720 = list(np.reshape(close_df.ewm(720).mean().values, -1)) + avg1440 = list(np.reshape(close_df.ewm(1440).mean().values, -1)) + avg2880 = list(np.reshape(close_df.ewm(2880).mean().values, -1)) + + np_high, np_low, np_close = np.array(high, dtype=np.float64), np.array(low, dtype=np.float64), np.array(close, dtype=np.float64) + slowk_12_df, slowd_12_df = talib.STOCH(np_high, np_low, np_close, fastk_period=12, slowk_period=5, slowk_matype=0, slowd_period=5, slowd_matype=0) + slowk_26_df, slowd_26_df = talib.STOCH(np_high, np_low, np_close, fastk_period=26, slowk_period=16, slowk_matype=0, slowd_period=16, slowd_matype=0) + slowk_52_df, slowd_52_df = talib.STOCH(np_high, np_low, np_close, fastk_period=52, slowk_period=32, slowk_matype=0, slowd_period=32, slowd_matype=0) + + + # 최고/최저 위치 + loc_240 = [None for i in range(len(close))] + for i in range(240, len(close)): + min_v = np.min(result["close"][i-239:i+1]) + max_v = np.max(result["close"][i-239:i+1]) + if close[i] is not None: + loc_240[i] = ((close[i] - min_v) / (max_v - min_v)) + else: + loc_240[i] = None + + loc_240 = pd.DataFrame(loc_240) + loc_240_k = loc_240.to_numpy().reshape(-1) + loc_240_d = loc_240.rolling(20).mean() + loc_240_s = loc_240.rolling(60).mean() + loc_240_d = loc_240_d.to_numpy().reshape(-1) + loc_240_s = loc_240_s.to_numpy().reshape(-1) + + # 볼린저 밴드 + n, t = 10, 2 + max_10 = close_df.rolling(window=n).mean() + stddev_10 = close_df.rolling(window=n).std() + upper_10 = max_10 + (stddev_10 * t) # 상단 볼리저 밴드 + lower_10 = max_10 - (stddev_10 * t) # 하단 볼리저 밴드 + middle_10 = (upper_10 + lower_10) / 2 + upper_10 = list(np.reshape(upper_10.values, -1)) + lower_10 = list(np.reshape(lower_10.values, -1)) + middle_10 = list(np.reshape(middle_10.values, -1)) + + n, t = 20, 2 + max_20 = close_df.rolling(window=n).mean() + stddev_20 = close_df.rolling(window=n).std() + upper_20 = max_20 + (stddev_20 * t) # 상단 볼리저 밴드 + lower_20 = max_20 - (stddev_20 * t) # 하단 볼리저 밴드 + middle_20 = (upper_20 + lower_20) / 2 + upper_20 = list(np.reshape(upper_20.values, -1)) + lower_20 = list(np.reshape(lower_20.values, -1)) + middle_20 = list(np.reshape(middle_20.values, -1)) + + duration = 1440 + if mins == 1440: + duration = 360 + laggingSpan_close_diff, laggingSpan_close_diff_rate = self.getDiff_Rate(laggingSpan, close, duration=duration) + laggingSpan_changeLine_diff, laggingSpan_changeLine_diff_rate = self.getDiff_Rate(laggingSpan, changeLine, duration=duration) + laggingSpan_baseLine_diff, laggingSpan_baseLine_diff_rate = self.getDiff_Rate(laggingSpan, baseLine, duration=duration) + laggingSpan_leadingSpan1_diff, laggingSpan_leadingSpan1_diff_rate = self.getDiff_Rate(laggingSpan, leadingSpan1, duration=duration) + laggingSpan_leadingSpan2_diff, laggingSpan_leadingSpan2_diff_rate = self.getDiff_Rate(laggingSpan, leadingSpan2, duration=duration) + laggingSpan_avg60_diff, laggingSpan_avg60_diff_rate = self.getDiff_Rate(laggingSpan, avg60, duration=duration) + laggingSpan_lower10_diff, laggingSpan_lower10_diff_rate = self.getDiff_Rate(laggingSpan, lower_10, duration=duration) + laggingSpan_middle10_diff, laggingSpan_middle10_diff_rate = self.getDiff_Rate(laggingSpan, middle_10, duration=duration) + laggingSpan_upper10_diff, laggingSpan_upper10_diff_rate = self.getDiff_Rate(laggingSpan, upper_10, duration=duration) + laggingSpan_lower20_diff, laggingSpan_lower20_diff_rate = self.getDiff_Rate(laggingSpan, lower_20, duration=duration) + laggingSpan_middle20_diff, laggingSpan_middle20_diff_rate = self.getDiff_Rate(laggingSpan, middle_20, duration=duration) + laggingSpan_upper20_diff, laggingSpan_upper20_diff_rate = self.getDiff_Rate(laggingSpan, upper_20, duration=duration) + baseLine_close_diff, baseLine_close_diff_rate = self.getDiff_Rate(baseLine, close, duration=duration) + changeLine_close_diff, changeLine_close_diff_rate = self.getDiff_Rate(changeLine, close, duration=duration) + changeLine_baseLine_diff, changeLine_baseLine_diff_rate = self.getDiff_Rate(changeLine, baseLine, duration=duration) + changeLine_leadingSpan1_diff, changeLine_leadingSpan1_diff_rate = self.getDiff_Rate(changeLine, leadingSpan1, duration=duration) + leadingSpan1_leadingSpan2_diff, leadingSpan1_leadingSpan2_diff_rate = self.getDiff_Rate(leadingSpan1, leadingSpan2, duration=duration) + + + df_list = [ + pd.DataFrame(ymd), + pd.DataFrame(open), pd.DataFrame(close), pd.DataFrame(high), pd.DataFrame(low), pd.DataFrame(volume), + pd.DataFrame(changeLine), pd.DataFrame(baseLine), pd.DataFrame(laggingSpan), pd.DataFrame(leadingSpan1), pd.DataFrame(leadingSpan2), + + pd.DataFrame(laggingSpan_close_diff), + pd.DataFrame(laggingSpan_changeLine_diff), + pd.DataFrame(laggingSpan_baseLine_diff), + pd.DataFrame(laggingSpan_leadingSpan1_diff), + pd.DataFrame(laggingSpan_leadingSpan2_diff), + pd.DataFrame(laggingSpan_avg60_diff), + pd.DataFrame(laggingSpan_lower10_diff), + pd.DataFrame(laggingSpan_middle10_diff), + pd.DataFrame(laggingSpan_upper10_diff), + pd.DataFrame(laggingSpan_lower20_diff), + pd.DataFrame(laggingSpan_middle20_diff), + pd.DataFrame(laggingSpan_upper20_diff), + pd.DataFrame(baseLine_close_diff), + pd.DataFrame(changeLine_close_diff), + pd.DataFrame(changeLine_baseLine_diff), + pd.DataFrame(changeLine_leadingSpan1_diff), + pd.DataFrame(leadingSpan1_leadingSpan2_diff), + + pd.DataFrame(laggingSpan_close_diff_rate), + pd.DataFrame(laggingSpan_changeLine_diff_rate), + pd.DataFrame(laggingSpan_baseLine_diff_rate), + pd.DataFrame(laggingSpan_leadingSpan1_diff_rate), + pd.DataFrame(laggingSpan_leadingSpan2_diff_rate), + pd.DataFrame(laggingSpan_avg60_diff_rate), + pd.DataFrame(laggingSpan_lower10_diff_rate), + pd.DataFrame(laggingSpan_middle10_diff_rate), + pd.DataFrame(laggingSpan_upper10_diff_rate), + pd.DataFrame(laggingSpan_lower20_diff_rate), + pd.DataFrame(laggingSpan_middle20_diff_rate), + pd.DataFrame(laggingSpan_upper20_diff_rate), + pd.DataFrame(baseLine_close_diff_rate), + pd.DataFrame(changeLine_close_diff_rate), + pd.DataFrame(changeLine_baseLine_diff_rate), + pd.DataFrame(changeLine_leadingSpan1_diff_rate), + pd.DataFrame(leadingSpan1_leadingSpan2_diff_rate), + + pd.DataFrame(loc_240_k), pd.DataFrame(loc_240_d), pd.DataFrame(loc_240_s), + + pd.DataFrame(avg5), pd.DataFrame(avg10), pd.DataFrame(avg20), pd.DataFrame(avg60), pd.DataFrame(avg90), pd.DataFrame(avg120), pd.DataFrame(avg240), pd.DataFrame(avg360), pd.DataFrame(avg480), pd.DataFrame(avg720), pd.DataFrame(avg1440), pd.DataFrame(avg2880), + pd.DataFrame(upper_10), pd.DataFrame(lower_10), pd.DataFrame(middle_10), + pd.DataFrame(upper_20), pd.DataFrame(lower_20), pd.DataFrame(middle_20), + + pd.DataFrame(new_high_9), pd.DataFrame(new_high_26), pd.DataFrame(new_high_33), pd.DataFrame(new_high_52), + pd.DataFrame(new_low_9), pd.DataFrame(new_low_26), pd.DataFrame(new_low_33), pd.DataFrame(new_low_52), + pd.DataFrame(slowk_12_df), pd.DataFrame(slowd_12_df), + pd.DataFrame(slowk_26_df), pd.DataFrame(slowd_26_df), + pd.DataFrame(slowk_52_df), pd.DataFrame(slowd_52_df), + + ] + + data = pd.concat(df_list, axis=1) + column_names = [ + 'ymd', + 'open', 'close', 'high', 'low', 'volume', + 'changeLine', 'baseLine', 'laggingSpan', 'leadingSpan1', 'leadingSpan2', + + 'laggingSpan_close_diff', + 'laggingSpan_changeLine_diff', + 'laggingSpan_baseLine_diff', + 'laggingSpan_leadingSpan1_diff', + 'laggingSpan_leadingSpan2_diff', + 'laggingSpan_avg60_diff', + 'laggingSpan_lower10_diff', + 'laggingSpan_middle10_diff', + 'laggingSpan_upper10_diff', + 'laggingSpan_lower20_diff', + 'laggingSpan_middle20_diff', + 'laggingSpan_upper20_diff', + 'baseLine_close_diff', + 'changeLine_close_diff', + 'changeLine_baseLine_diff', + 'changeLine_leadingSpan1_diff', + 'leadingSpan1_leadingSpan2_diff', + + 'laggingSpan_close_diff_rate', + 'laggingSpan_changeLine_diff_rate', + 'laggingSpan_baseLine_diff_rate', + 'laggingSpan_leadingSpan1_diff_rate', + 'laggingSpan_leadingSpan2_diff_rate', + 'laggingSpan_avg60_diff_rate', + 'laggingSpan_lower10_diff_rate', + 'laggingSpan_middle10_diff_rate', + 'laggingSpan_upper10_diff_rate', + 'laggingSpan_lower20_diff_rate', + 'laggingSpan_middle20_diff_rate', + 'laggingSpan_upper20_diff_rate', + 'baseLine_close_diff_rate', + 'changeLine_close_diff_rate', + 'changeLine_baseLine_diff_rate', + 'changeLine_leadingSpan1_diff_rate', + 'leadingSpan1_leadingSpan2_diff_rate', + + 'loc_240_k', 'loc_240_d', 'loc_240_s', + + 'avg5', 'avg10', 'avg20', 'avg60', 'avg90', 'avg120', 'avg240', 'avg360', 'avg480', 'avg720', 'avg1440', 'avg2880', + + 'upper_10', 'lower_10', 'middle_10', + 'upper_20', 'lower_20', 'middle_20', + + 'new_high_9', 'new_high_26', 'new_high_33', 'new_high_52', + 'new_low_9', 'new_low_26', 'new_low_33', 'new_low_52', + + 'slowk_12', 'slowd_12', + 'slowk_26', 'slowd_26', + 'slowk_52', 'slowd_52', + ] + data.columns = column_names + data.index = pd.DatetimeIndex(ymd) + + return data, current_index + + def getData(self, ticker, mins=None, ymd=None, get_days=14): + if ymd is None: + result = self.getCoinData(ticker, mins=mins, get_days=get_days) + else: + result = self.getCoinData(ticker, mins=mins, ymd=ymd, get_days=get_days) + + if len(result['ymd']) < 1: + return None, None + + #result_tic = self.makeTickData(result_m1, mins=minute) + data, current_index = self.analyze(result, mins=mins) + return data, current_index + + def analyzePattern(self, data): + # jSDPattern.analyzePattern(data) + + data = data[['open', 'high', 'low', 'close', 'volume']].astype(float) + pattern_names = talib.get_function_groups()['Pattern Recognition'] + pattern_results = {} + for pattern in pattern_names: + pattern_function = getattr(talib, pattern) + result = pattern_function(data['open'].values, data['high'].values, data['low'].values, data['close'].values) + if result[-1] != 0: + pattern_results[pattern] = result[-1] + + if len(pattern_results) > 0: + for pattern, result in pattern_results.items(): + if result > 0: + direction = "상승" + else: + direction = "하락" + print(f"{pattern}: {direction}") + else: + print("인식된 차트 패턴이 없습니다.") + + return + +if __name__ == "__main__": + def min_max_normalize(data): + min_val = min(data) + max_val = max(data) + normalized_data = [(x - min_val) / (max_val - min_val) for x in data] + return normalized_data + + + # 예시 데이터 + original_data = [-4, -3, -2, -1, 0] + normalized_data = min_max_normalize(original_data) + print(np.asarray(normalized_data)-1) + original_data = [0, 2,4,6,8,10] + normalized_data = min_max_normalize(original_data) + print(normalized_data) \ No newline at end of file diff --git a/stock/analysis/JSDPattern_realtime.py b/stock/analysis/JSDPattern_realtime.py new file mode 100644 index 0000000..39b5973 --- /dev/null +++ b/stock/analysis/JSDPattern_realtime.py @@ -0,0 +1,255 @@ +# https://bibot.tistory.com/63 +# https://nonmeyet.tistory.com/entry/Python-TALib%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8%EC%A3%BC%EA%B0%80%EA%B8%B0%EC%88%A0%EB%B6%84%EC%84%9D-%EB%B3%B4%EC%A1%B0%EC%A7%80%ED%91%9C-%EC%B6%94%EA%B0%80 +# https://lunadaddy.tistory.com/122 +# https://wikidocs.net/186885 + +import os +from scipy.signal import savgol_filter +import numpy as np +np.seterr(divide='ignore', invalid='ignore') +# https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib +# https://lunadaddy.tistory.com/122 +import talib +import pandas as pd +from datetime import datetime, timedelta + +from JSDPattern import JSDPattern + +class JSDPattern_realtime (JSDPattern): + + def __init__(self, stockFileName=None): + super().__init__(stockFileName) + return + + def analyze(self, result): + result["volume"] = [result["volume"][i] if 0 < result["volume"][i] else 1 for i in range(len(result["volume"]))] + # 기본 캔들 정보 + open_df = pd.DataFrame(result["open"]) + close_df = pd.DataFrame(result["close"]) + high_df = pd.DataFrame(result["high"]) + low_df = pd.DataFrame(result["low"]) + volume_df = pd.DataFrame(result["volume"]) + + # 중복 제거 + ymd_df = pd.DataFrame(result["ymd"]) + data_dup = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + data_dup.columns = ["ymd", "open", "close", "high", "low", "volume"] + data_dup.index = pd.DatetimeIndex(result["ymd"]) + data_dup_sorted = data_dup.sort_index(ascending=True) + data_dup_sorted = data_dup_sorted.drop_duplicates() + + ymd_df = data_dup_sorted["ymd"] + open_df = data_dup_sorted["open"] + close_df = data_dup_sorted["close"] + high_df = data_dup_sorted["high"] + low_df = data_dup_sorted["low"] + volume_df = data_dup_sorted["volume"] + + ymd = ymd_df.tolist() + open = open_df.tolist() + close = close_df.tolist() + high = high_df.tolist() + low = low_df.tolist() + volume = volume_df.tolist() + + # ichimokuCloud + df = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + column_names = ['DATE', 'open', 'close', 'high', 'low', 'volume'] + df.columns = column_names + c, b, l, s = 9, 26, 52, 26 + # 1. 전환선 = (과거 9일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 9일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + changeLine = (df.high.rolling(c).max() + df.low.rolling(c).min()) / 2 + # 2. 기준선 = 과거 26일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 26일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + baseLine = (df.high.rolling(b).max() + df.low.rolling(b).min()) / 2 + + # 3. 후행스팬 = 현재 close가격의 26일전 반영 + laggingSpan = [df.close.values[i + s] for i in range(len(df.close) - s)] + laggingSpan += [None for i in range(s)] + laggingSpan = np.array(laggingSpan) + + # 4. 선행스팬 1 = ((기준선 + 전환선) / 2)를 26일 선행하여 배치 + # 전환선과 기준선의 평균값을 구해 당일 포함 26일 앞으로 이동시킨 선 (중-단기 구간의 힘을 보여줌) + tmp_leadingSpan1 = (changeLine + baseLine) / 2 + """ S: 26일 선행시킴 """ + leadingSpan1 = list(tmp_leadingSpan1.values) + for i in range(b - 1): + leadingSpan1.insert(0, None) + """ E: 26일 선행시킴 """ + + # 5. 선행스팬 2 = ((최근 52일 동안 최고가 + 최저가) / 2)를 26일 선행하여 배치 + # 당일을 포함한 52일 동안의 최고가와 최저가의 평균을 26일 앞으로 이동시킨 선 (장기으로 형성된 선이기 때문에 가장 느리게 변함) + tmp_leadingSpan2 = (df.high.rolling(l).max() + df.low.rolling(l).min()) / 2 + """ S: 52일 선행시킴 """ + leadingSpan2 = list(tmp_leadingSpan2.values) + for i in range(l - 1): + leadingSpan2.insert(0, None) + """ S: 52일 선행시킴 """ + + baseLine = baseLine.tolist() + changeLine = changeLine.tolist() + laggingSpan = list(laggingSpan) + current_index = len(ymd) + for i in range(51): + if len(ymd) < len(leadingSpan2): + ymd.append(ymd[-1] + timedelta(days=1)) + if len(open) < len(leadingSpan2): + open.append(None) + if len(close) < len(leadingSpan2): + close.append(None) + if len(high) < len(leadingSpan2): + high.append(None) + if len(low) < len(leadingSpan2): + low.append(None) + if len(volume) < len(leadingSpan2): + volume.append(None) + if len(baseLine) < len(leadingSpan2): + baseLine.append(None) + if len(changeLine) < len(leadingSpan2): + changeLine.append(None) + if len(laggingSpan) < len(leadingSpan2): + laggingSpan.append(None) + for i in range(26): + if len(leadingSpan1) < len(leadingSpan2): + leadingSpan1.append(leadingSpan1[-1]) + + # 9일 신고가 + new_high_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and max(close[c-8:c]) < close[c] else 0 for c in range(8, len(close))] + # 26일 신고가 + new_high_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and max(close[c-25:c]) < close[c] else 0 for c in range(25, len(close))] + + # 9일 신저가 + new_low_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and close[c-9] < min(close[c-8:c+1]) else 0 for c in range(8, len(close))] + # 26일 신저가 + new_low_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and close[c-26] < min(close[c-25:c+1]) else 0 for c in range(25, len(close))] + + + # 이동 평균 + close_df = pd.DataFrame(close) + avg5 = list(np.reshape(close_df.ewm(5).mean().values, -1)) + avg10 = list(np.reshape(close_df.ewm(10).mean().values, -1)) + avg20 = list(np.reshape(close_df.ewm(20).mean().values, -1)) + avg60 = list(np.reshape(close_df.ewm(60).mean().values, -1)) + avg90 = list(np.reshape(close_df.ewm(90).mean().values, -1)) + avg120 = list(np.reshape(close_df.ewm(120).mean().values, -1)) + avg240 = list(np.reshape(close_df.ewm(240).mean().values, -1)) + avg360 = list(np.reshape(close_df.ewm(360).mean().values, -1)) + avg480 = list(np.reshape(close_df.ewm(480).mean().values, -1)) + + np_high, np_low, np_close = np.array(high, dtype=np.float64), np.array(low, dtype=np.float64), np.array(close, dtype=np.float64) + slowk_12_df, slowd_12_df = talib.STOCH(np_high, np_low, np_close, fastk_period=12, slowk_period=5, slowk_matype=0, slowd_period=5, slowd_matype=0) + slowk_26_df, slowd_26_df = talib.STOCH(np_high, np_low, np_close, fastk_period=26, slowk_period=16, slowk_matype=0, slowd_period=16, slowd_matype=0) + slowk_52_df, slowd_52_df = talib.STOCH(np_high, np_low, np_close, fastk_period=52, slowk_period=32, slowk_matype=0, slowd_period=32, slowd_matype=0) + + + # 볼린저 밴드 + n, t = 10, 2 + max_10 = close_df.rolling(window=n).mean() + stddev_10 = close_df.rolling(window=n).std() + upper_10 = max_10 + (stddev_10 * t) # 상단 볼리저 밴드 + lower_10 = max_10 - (stddev_10 * t) # 하단 볼리저 밴드 + middle_10 = (upper_10 + lower_10) / 2 + upper_10 = list(np.reshape(upper_10.values, -1)) + lower_10 = list(np.reshape(lower_10.values, -1)) + middle_10 = list(np.reshape(middle_10.values, -1)) + + n, t = 20, 2 + max_20 = close_df.rolling(window=n).mean() + stddev_20 = close_df.rolling(window=n).std() + upper_20 = max_20 + (stddev_20 * t) # 상단 볼리저 밴드 + lower_20 = max_20 - (stddev_20 * t) # 하단 볼리저 밴드 + middle_20 = (upper_20 + lower_20) / 2 + upper_20 = list(np.reshape(upper_20.values, -1)) + lower_20 = list(np.reshape(lower_20.values, -1)) + middle_20 = list(np.reshape(middle_20.values, -1)) + + duration = 360 + laggingSpan_close_diff, laggingSpan_close_diff_rate = self.getDiff_Rate(laggingSpan, close, duration=duration) + laggingSpan_avg60_diff, laggingSpan_avg60_diff_rate = self.getDiff_Rate(laggingSpan, avg60, duration=duration) + leadingSpan1_leadingSpan2_diff, leadingSpan1_leadingSpan2_diff_rate = self.getDiff_Rate(leadingSpan1, leadingSpan2, duration=duration) + + df_list = [ + pd.DataFrame(ymd), + pd.DataFrame(open), pd.DataFrame(close), pd.DataFrame(high), pd.DataFrame(low), pd.DataFrame(volume), + pd.DataFrame(changeLine), pd.DataFrame(baseLine), pd.DataFrame(laggingSpan), pd.DataFrame(leadingSpan1), pd.DataFrame(leadingSpan2), + + pd.DataFrame(laggingSpan_close_diff), + pd.DataFrame(laggingSpan_avg60_diff), + pd.DataFrame(leadingSpan1_leadingSpan2_diff), + + pd.DataFrame(laggingSpan_close_diff_rate), + pd.DataFrame(laggingSpan_avg60_diff_rate), + pd.DataFrame(leadingSpan1_leadingSpan2_diff_rate), + + pd.DataFrame(avg5), pd.DataFrame(avg10), pd.DataFrame(avg20), pd.DataFrame(avg60), pd.DataFrame(avg90), pd.DataFrame(avg120), pd.DataFrame(avg240), pd.DataFrame(avg360), pd.DataFrame(avg480), + pd.DataFrame(upper_10), pd.DataFrame(lower_10), pd.DataFrame(middle_10), + pd.DataFrame(upper_20), pd.DataFrame(lower_20), pd.DataFrame(middle_20), + + pd.DataFrame(new_high_9), pd.DataFrame(new_high_26), + pd.DataFrame(new_low_9), pd.DataFrame(new_low_26), + pd.DataFrame(slowk_12_df), pd.DataFrame(slowd_12_df), + pd.DataFrame(slowk_26_df), pd.DataFrame(slowd_26_df), + pd.DataFrame(slowk_52_df), pd.DataFrame(slowd_52_df), + + ] + + data = pd.concat(df_list, axis=1) + column_names = [ + 'ymd', + 'open', 'close', 'high', 'low', 'volume', + 'changeLine', 'baseLine', 'laggingSpan', 'leadingSpan1', 'leadingSpan2', + + 'laggingSpan_close_diff', + 'laggingSpan_avg60_diff', + 'leadingSpan1_leadingSpan2_diff', + + 'laggingSpan_close_diff_rate', + 'laggingSpan_avg60_diff_rate', + 'leadingSpan1_leadingSpan2_diff_rate', + + 'avg5', 'avg10', 'avg20', 'avg60', 'avg90', 'avg120', 'avg240', 'avg360', 'avg480', + + 'upper_10', 'lower_10', 'middle_10', + 'upper_20', 'lower_20', 'middle_20', + + 'new_high_9', 'new_high_26', + 'new_low_9', 'new_low_26', + + 'slowk_12', 'slowd_12', + 'slowk_26', 'slowd_26', + 'slowk_52', 'slowd_52', + ] + data.columns = column_names + data.index = pd.DatetimeIndex(ymd) + + return data, current_index + + def getData(self, ticker, ymd=None, get_days=14): + if ymd is None: + result = self.getCoinData(ticker, get_days=get_days) + else: + result = self.getCoinData(ticker, ymd=ymd, get_days=get_days) + + if len(result['ymd']) < 1: + return None, None + + #result_tic = self.makeTickData(result_m1, mins=minute) + data, current_index = self.analyze(result) + return data, current_index + +if __name__ == "__main__": + def min_max_normalize(data): + min_val = min(data) + max_val = max(data) + normalized_data = [(x - min_val) / (max_val - min_val) for x in data] + return normalized_data + + + # 예시 데이터 + original_data = [-4, -3, -2, -1, 0] + normalized_data = min_max_normalize(original_data) + print(np.asarray(normalized_data)-1) + original_data = [0, 2,4,6,8,10] + normalized_data = min_max_normalize(original_data) + print(normalized_data) \ No newline at end of file diff --git a/stock/analysis/JSDPattern_simulation.py b/stock/analysis/JSDPattern_simulation.py new file mode 100644 index 0000000..e57b5d9 --- /dev/null +++ b/stock/analysis/JSDPattern_simulation.py @@ -0,0 +1,366 @@ +# https://bibot.tistory.com/63 +# https://nonmeyet.tistory.com/entry/Python-TALib%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8%EC%A3%BC%EA%B0%80%EA%B8%B0%EC%88%A0%EB%B6%84%EC%84%9D-%EB%B3%B4%EC%A1%B0%EC%A7%80%ED%91%9C-%EC%B6%94%EA%B0%80 +# https://lunadaddy.tistory.com/122 +# https://wikidocs.net/186885 + +import os +from scipy.signal import savgol_filter +import numpy as np +np.seterr(divide='ignore', invalid='ignore') +# https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib +# https://lunadaddy.tistory.com/122 +import talib +import pandas as pd +from datetime import datetime, timedelta + +from stock.analysis.IchimokuCloud import IchimokuCloud +from sklearn.preprocessing import StandardScaler +from JSDPattern import JSDPattern + +class JSDPattern_simulation (JSDPattern): + + def __init__(self, RESOURCE_PATH=None): + super().__init__(RESOURCE_PATH) + return + + def analyze(self, result, mins=1): + result["volume"] = [result["volume"][i] if 0 < result["volume"][i] else 1 for i in range(len(result["volume"]))] + # 기본 캔들 정보 + open_df = pd.DataFrame(result["open"]) + close_df = pd.DataFrame(result["close"]) + high_df = pd.DataFrame(result["high"]) + low_df = pd.DataFrame(result["low"]) + volume_df = pd.DataFrame(result["volume"]) + + # 중복 제거 + ymd_df = pd.DataFrame(result["ymd"]) + data_dup = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + data_dup.columns = ["ymd", "open", "close", "high", "low", "volume"] + data_dup.index = pd.DatetimeIndex(result["ymd"]) + data_dup_sorted = data_dup.sort_index(ascending=True) + data_dup_sorted = data_dup_sorted.drop_duplicates() + + ymd_df = data_dup_sorted["ymd"] + open_df = data_dup_sorted["open"] + close_df = data_dup_sorted["close"] + high_df = data_dup_sorted["high"] + low_df = data_dup_sorted["low"] + volume_df = data_dup_sorted["volume"] + + ymd = ymd_df.tolist() + open = open_df.tolist() + close = close_df.tolist() + high = high_df.tolist() + low = low_df.tolist() + volume = volume_df.tolist() + + # ichimokuCloud + df = pd.concat([ymd_df, open_df, close_df, high_df, low_df, volume_df], axis=1) + column_names = ['DATE', 'open', 'close', 'high', 'low', 'volume'] + df.columns = column_names + c, b, l, s = 9, 26, 52, 26 + # 1. 전환선 = (과거 9일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 9일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + changeLine = (df.high.rolling(c).max() + df.low.rolling(c).min()) / 2 + # 2. 기준선 = 과거 26일 동안 최고가 + 최저가) / 2 + # 당일을 포함한 26일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다. + baseLine = (df.high.rolling(b).max() + df.low.rolling(b).min()) / 2 + + # 3. 후행스팬 = 현재 close가격의 26일전 반영 + laggingSpan = [df.close.values[i + s] for i in range(len(df.close) - s)] + laggingSpan += [None for i in range(s)] + laggingSpan = np.array(laggingSpan) + + # 4. 선행스팬 1 = ((기준선 + 전환선) / 2)를 26일 선행하여 배치 + # 전환선과 기준선의 평균값을 구해 당일 포함 26일 앞으로 이동시킨 선 (중-단기 구간의 힘을 보여줌) + tmp_leadingSpan1 = (changeLine + baseLine) / 2 + """ S: 26일 선행시킴 """ + leadingSpan1 = list(tmp_leadingSpan1.values) + for i in range(b - 1): + leadingSpan1.insert(0, None) + """ E: 26일 선행시킴 """ + + # 5. 선행스팬 2 = ((최근 52일 동안 최고가 + 최저가) / 2)를 26일 선행하여 배치 + # 당일을 포함한 52일 동안의 최고가와 최저가의 평균을 26일 앞으로 이동시킨 선 (장기으로 형성된 선이기 때문에 가장 느리게 변함) + tmp_leadingSpan2 = (df.high.rolling(l).max() + df.low.rolling(l).min()) / 2 + """ S: 52일 선행시킴 """ + leadingSpan2 = list(tmp_leadingSpan2.values) + for i in range(l - 1): + leadingSpan2.insert(0, None) + """ S: 52일 선행시킴 """ + + baseLine = baseLine.tolist() + changeLine = changeLine.tolist() + laggingSpan = list(laggingSpan) + current_index = len(ymd) + for i in range(51): + if len(ymd) < len(leadingSpan2): + if mins==1440: + ymd.append(ymd[-1] + timedelta(days=1)) + else: + ymd.append(ymd[-1] + timedelta(minutes=1)) + if len(open) < len(leadingSpan2): + open.append(None) + if len(close) < len(leadingSpan2): + close.append(None) + if len(high) < len(leadingSpan2): + high.append(None) + if len(low) < len(leadingSpan2): + low.append(None) + if len(volume) < len(leadingSpan2): + volume.append(None) + if len(baseLine) < len(leadingSpan2): + baseLine.append(None) + if len(changeLine) < len(leadingSpan2): + changeLine.append(None) + if len(laggingSpan) < len(leadingSpan2): + laggingSpan.append(None) + for i in range(26): + if len(leadingSpan1) < len(leadingSpan2): + leadingSpan1.append(leadingSpan1[-1]) + + # 9일 신고가 + new_high_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and max(close[c-8:c]) < close[c] else 0 for c in range(8, len(close))] + # 26일 신고가 + new_high_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and max(close[c-25:c]) < close[c] else 0 for c in range(25, len(close))] + # 33일 신고가 + new_high_33 = [0 for c in range(32)] + [1 if (leadingSpan1[c-1] is not None and leadingSpan1[c] is not None and leadingSpan1[c-1] < leadingSpan1[c]) and None not in close[c-8:c+1] and max(close[c-32:c]) < close[c] else 0 for c in range(32, len(close))] + # 52일 신고가 + new_high_52 = [0 for c in range(51)] + [1 if (leadingSpan2[c-1] is not None and leadingSpan2[c] is not None and leadingSpan2[c-1] < leadingSpan2[c]) and None not in close[c-8:c+1] and max(close[c-51:c]) < close[c] else 0 for c in range(51, len(close))] + + # 9일 신저가 + new_low_9 = [0 for c in range(8)] + [1 if (changeLine[c-1] is not None and changeLine[c] is not None and changeLine[c-1] < changeLine[c]) and None not in close[c-8:c+1] and close[c-9] < min(close[c-8:c+1]) else 0 for c in range(8, len(close))] + # 26일 신저가 + new_low_26 = [0 for c in range(25)] + [1 if (baseLine[c-1] is not None and baseLine[c] is not None and baseLine[c-1] < baseLine[c]) and None not in close[c-8:c+1] and close[c-26] < min(close[c-25:c+1]) else 0 for c in range(25, len(close))] + # 33일 신저가 + new_low_33 = [0 for c in range(32)] + [1 if (leadingSpan1[c-1] is not None and leadingSpan1[c] is not None and leadingSpan1[c-1] < leadingSpan1[c]) and None not in close[c-8:c+1] and close[c-33] < min(close[c-32:c+1]) else 0 for c in range(32, len(close))] + # 52일 신저가 + new_low_52 = [0 for c in range(51)] + [1 if (leadingSpan2[c-1] is not None and leadingSpan2[c] is not None and leadingSpan2[c-1] < leadingSpan2[c]) and None not in close[c-8:c+1] and close[c-52] < min(close[c-51:c+1]) else 0 for c in range(51, len(close))] + + + # 이동 평균 + close_df = pd.DataFrame(close) + avg5 = list(np.reshape(close_df.ewm(5).mean().values, -1)) + avg10 = list(np.reshape(close_df.ewm(10).mean().values, -1)) + avg20 = list(np.reshape(close_df.ewm(20).mean().values, -1)) + avg60 = list(np.reshape(close_df.ewm(60).mean().values, -1)) + avg90 = list(np.reshape(close_df.ewm(90).mean().values, -1)) + avg120 = list(np.reshape(close_df.ewm(120).mean().values, -1)) + avg240 = list(np.reshape(close_df.ewm(240).mean().values, -1)) + avg360 = list(np.reshape(close_df.ewm(360).mean().values, -1)) + avg480 = list(np.reshape(close_df.ewm(480).mean().values, -1)) + avg720 = list(np.reshape(close_df.ewm(720).mean().values, -1)) + avg1440 = list(np.reshape(close_df.ewm(1440).mean().values, -1)) + avg2880 = list(np.reshape(close_df.ewm(2880).mean().values, -1)) + + np_high, np_low, np_close = np.array(high, dtype=np.float64), np.array(low, dtype=np.float64), np.array(close, dtype=np.float64) + slowk_12_df, slowd_12_df = talib.STOCH(np_high, np_low, np_close, fastk_period=12, slowk_period=5, slowk_matype=0, slowd_period=5, slowd_matype=0) + slowk_26_df, slowd_26_df = talib.STOCH(np_high, np_low, np_close, fastk_period=26, slowk_period=16, slowk_matype=0, slowd_period=16, slowd_matype=0) + slowk_52_df, slowd_52_df = talib.STOCH(np_high, np_low, np_close, fastk_period=52, slowk_period=32, slowk_matype=0, slowd_period=32, slowd_matype=0) + + + # 최고/최저 위치 + loc_240 = [None for i in range(len(close))] + for i in range(240, len(close)): + min_v = np.min(result["close"][i-239:i+1]) + max_v = np.max(result["close"][i-239:i+1]) + if close[i] is not None: + loc_240[i] = ((close[i] - min_v) / (max_v - min_v)) + else: + loc_240[i] = None + + loc_240 = pd.DataFrame(loc_240) + loc_240_k = loc_240.to_numpy().reshape(-1) + loc_240_d = loc_240.rolling(20).mean() + loc_240_s = loc_240.rolling(60).mean() + loc_240_d = loc_240_d.to_numpy().reshape(-1) + loc_240_s = loc_240_s.to_numpy().reshape(-1) + + # 볼린저 밴드 + n, t = 10, 2 + max_10 = close_df.rolling(window=n).mean() + stddev_10 = close_df.rolling(window=n).std() + upper_10 = max_10 + (stddev_10 * t) # 상단 볼리저 밴드 + lower_10 = max_10 - (stddev_10 * t) # 하단 볼리저 밴드 + middle_10 = (upper_10 + lower_10) / 2 + upper_10 = list(np.reshape(upper_10.values, -1)) + lower_10 = list(np.reshape(lower_10.values, -1)) + middle_10 = list(np.reshape(middle_10.values, -1)) + + n, t = 20, 2 + max_20 = close_df.rolling(window=n).mean() + stddev_20 = close_df.rolling(window=n).std() + upper_20 = max_20 + (stddev_20 * t) # 상단 볼리저 밴드 + lower_20 = max_20 - (stddev_20 * t) # 하단 볼리저 밴드 + middle_20 = (upper_20 + lower_20) / 2 + upper_20 = list(np.reshape(upper_20.values, -1)) + lower_20 = list(np.reshape(lower_20.values, -1)) + middle_20 = list(np.reshape(middle_20.values, -1)) + + duration = 1440 + if mins == 1440: + duration = 360 + laggingSpan_close_diff, laggingSpan_close_diff_rate = self.getDiff_Rate(laggingSpan, close, duration=duration) + laggingSpan_changeLine_diff, laggingSpan_changeLine_diff_rate = self.getDiff_Rate(laggingSpan, changeLine, duration=duration) + laggingSpan_baseLine_diff, laggingSpan_baseLine_diff_rate = self.getDiff_Rate(laggingSpan, baseLine, duration=duration) + laggingSpan_leadingSpan1_diff, laggingSpan_leadingSpan1_diff_rate = self.getDiff_Rate(laggingSpan, leadingSpan1, duration=duration) + laggingSpan_leadingSpan2_diff, laggingSpan_leadingSpan2_diff_rate = self.getDiff_Rate(laggingSpan, leadingSpan2, duration=duration) + laggingSpan_avg60_diff, laggingSpan_avg60_diff_rate = self.getDiff_Rate(laggingSpan, avg60, duration=duration) + laggingSpan_lower10_diff, laggingSpan_lower10_diff_rate = self.getDiff_Rate(laggingSpan, lower_10, duration=duration) + laggingSpan_middle10_diff, laggingSpan_middle10_diff_rate = self.getDiff_Rate(laggingSpan, middle_10, duration=duration) + laggingSpan_upper10_diff, laggingSpan_upper10_diff_rate = self.getDiff_Rate(laggingSpan, upper_10, duration=duration) + laggingSpan_lower20_diff, laggingSpan_lower20_diff_rate = self.getDiff_Rate(laggingSpan, lower_20, duration=duration) + laggingSpan_middle20_diff, laggingSpan_middle20_diff_rate = self.getDiff_Rate(laggingSpan, middle_20, duration=duration) + laggingSpan_upper20_diff, laggingSpan_upper20_diff_rate = self.getDiff_Rate(laggingSpan, upper_20, duration=duration) + baseLine_close_diff, baseLine_close_diff_rate = self.getDiff_Rate(baseLine, close, duration=duration) + changeLine_close_diff, changeLine_close_diff_rate = self.getDiff_Rate(changeLine, close, duration=duration) + changeLine_baseLine_diff, changeLine_baseLine_diff_rate = self.getDiff_Rate(changeLine, baseLine, duration=duration) + changeLine_leadingSpan1_diff, changeLine_leadingSpan1_diff_rate = self.getDiff_Rate(changeLine, leadingSpan1, duration=duration) + leadingSpan1_leadingSpan2_diff, leadingSpan1_leadingSpan2_diff_rate = self.getDiff_Rate(leadingSpan1, leadingSpan2, duration=duration) + + + df_list = [ + pd.DataFrame(ymd), + pd.DataFrame(open), pd.DataFrame(close), pd.DataFrame(high), pd.DataFrame(low), pd.DataFrame(volume), + pd.DataFrame(changeLine), pd.DataFrame(baseLine), pd.DataFrame(laggingSpan), pd.DataFrame(leadingSpan1), pd.DataFrame(leadingSpan2), + + pd.DataFrame(laggingSpan_close_diff), + pd.DataFrame(laggingSpan_changeLine_diff), + pd.DataFrame(laggingSpan_baseLine_diff), + pd.DataFrame(laggingSpan_leadingSpan1_diff), + pd.DataFrame(laggingSpan_leadingSpan2_diff), + pd.DataFrame(laggingSpan_avg60_diff), + pd.DataFrame(laggingSpan_lower10_diff), + pd.DataFrame(laggingSpan_middle10_diff), + pd.DataFrame(laggingSpan_upper10_diff), + pd.DataFrame(laggingSpan_lower20_diff), + pd.DataFrame(laggingSpan_middle20_diff), + pd.DataFrame(laggingSpan_upper20_diff), + pd.DataFrame(baseLine_close_diff), + pd.DataFrame(changeLine_close_diff), + pd.DataFrame(changeLine_baseLine_diff), + pd.DataFrame(changeLine_leadingSpan1_diff), + pd.DataFrame(leadingSpan1_leadingSpan2_diff), + + pd.DataFrame(laggingSpan_close_diff_rate), + pd.DataFrame(laggingSpan_changeLine_diff_rate), + pd.DataFrame(laggingSpan_baseLine_diff_rate), + pd.DataFrame(laggingSpan_leadingSpan1_diff_rate), + pd.DataFrame(laggingSpan_leadingSpan2_diff_rate), + pd.DataFrame(laggingSpan_avg60_diff_rate), + pd.DataFrame(laggingSpan_lower10_diff_rate), + pd.DataFrame(laggingSpan_middle10_diff_rate), + pd.DataFrame(laggingSpan_upper10_diff_rate), + pd.DataFrame(laggingSpan_lower20_diff_rate), + pd.DataFrame(laggingSpan_middle20_diff_rate), + pd.DataFrame(laggingSpan_upper20_diff_rate), + pd.DataFrame(baseLine_close_diff_rate), + pd.DataFrame(changeLine_close_diff_rate), + pd.DataFrame(changeLine_baseLine_diff_rate), + pd.DataFrame(changeLine_leadingSpan1_diff_rate), + pd.DataFrame(leadingSpan1_leadingSpan2_diff_rate), + + pd.DataFrame(loc_240_k), pd.DataFrame(loc_240_d), pd.DataFrame(loc_240_s), + + pd.DataFrame(avg5), pd.DataFrame(avg10), pd.DataFrame(avg20), pd.DataFrame(avg60), pd.DataFrame(avg90), pd.DataFrame(avg120), pd.DataFrame(avg240), pd.DataFrame(avg360), pd.DataFrame(avg480), pd.DataFrame(avg720), pd.DataFrame(avg1440), pd.DataFrame(avg2880), + pd.DataFrame(upper_10), pd.DataFrame(lower_10), pd.DataFrame(middle_10), + pd.DataFrame(upper_20), pd.DataFrame(lower_20), pd.DataFrame(middle_20), + + pd.DataFrame(new_high_9), pd.DataFrame(new_high_26), pd.DataFrame(new_high_33), pd.DataFrame(new_high_52), + pd.DataFrame(new_low_9), pd.DataFrame(new_low_26), pd.DataFrame(new_low_33), pd.DataFrame(new_low_52), + pd.DataFrame(slowk_12_df), pd.DataFrame(slowd_12_df), + pd.DataFrame(slowk_26_df), pd.DataFrame(slowd_26_df), + pd.DataFrame(slowk_52_df), pd.DataFrame(slowd_52_df), + + ] + + data = pd.concat(df_list, axis=1) + column_names = [ + 'ymd', + 'open', 'close', 'high', 'low', 'volume', + 'changeLine', 'baseLine', 'laggingSpan', 'leadingSpan1', 'leadingSpan2', + + 'laggingSpan_close_diff', + 'laggingSpan_changeLine_diff', + 'laggingSpan_baseLine_diff', + 'laggingSpan_leadingSpan1_diff', + 'laggingSpan_leadingSpan2_diff', + 'laggingSpan_avg60_diff', + 'laggingSpan_lower10_diff', + 'laggingSpan_middle10_diff', + 'laggingSpan_upper10_diff', + 'laggingSpan_lower20_diff', + 'laggingSpan_middle20_diff', + 'laggingSpan_upper20_diff', + 'baseLine_close_diff', + 'changeLine_close_diff', + 'changeLine_baseLine_diff', + 'changeLine_leadingSpan1_diff', + 'leadingSpan1_leadingSpan2_diff', + + 'laggingSpan_close_diff_rate', + 'laggingSpan_changeLine_diff_rate', + 'laggingSpan_baseLine_diff_rate', + 'laggingSpan_leadingSpan1_diff_rate', + 'laggingSpan_leadingSpan2_diff_rate', + 'laggingSpan_avg60_diff_rate', + 'laggingSpan_lower10_diff_rate', + 'laggingSpan_middle10_diff_rate', + 'laggingSpan_upper10_diff_rate', + 'laggingSpan_lower20_diff_rate', + 'laggingSpan_middle20_diff_rate', + 'laggingSpan_upper20_diff_rate', + 'baseLine_close_diff_rate', + 'changeLine_close_diff_rate', + 'changeLine_baseLine_diff_rate', + 'changeLine_leadingSpan1_diff_rate', + 'leadingSpan1_leadingSpan2_diff_rate', + + 'loc_240_k', 'loc_240_d', 'loc_240_s', + + 'avg5', 'avg10', 'avg20', 'avg60', 'avg90', 'avg120', 'avg240', 'avg360', 'avg480', 'avg720', 'avg1440', 'avg2880', + + 'upper_10', 'lower_10', 'middle_10', + 'upper_20', 'lower_20', 'middle_20', + + 'new_high_9', 'new_high_26', 'new_high_33', 'new_high_52', + 'new_low_9', 'new_low_26', 'new_low_33', 'new_low_52', + + 'slowk_12', 'slowd_12', + 'slowk_26', 'slowd_26', + 'slowk_52', 'slowd_52', + ] + data.columns = column_names + data.index = pd.DatetimeIndex(ymd) + + return data, current_index + + def getData(self, ticker, ymd=None, get_days=14): + if ymd is None: + result = self.getCoinData(ticker, get_days=get_days) + else: + result = self.getCoinData(ticker, ymd=ymd, get_days=get_days) + + if len(result['ymd']) < 1: + return None, None + + #result_tic = self.makeTickData(result_m1, mins=minute) + data, current_index = self.analyze(result) + return data, current_index + + +if __name__ == "__main__": + def min_max_normalize(data): + min_val = min(data) + max_val = max(data) + normalized_data = [(x - min_val) / (max_val - min_val) for x in data] + return normalized_data + + + # 예시 데이터 + original_data = [-4, -3, -2, -1, 0] + normalized_data = min_max_normalize(original_data) + print(np.asarray(normalized_data)-1) + original_data = [0, 2,4,6,8,10] + normalized_data = min_max_normalize(original_data) + print(normalized_data) \ No newline at end of file