This commit is contained in:
dsyoon
2023-12-12 00:15:38 +09:00
parent 6b22132b43
commit fbfab6d236
7 changed files with 550 additions and 359 deletions

View File

@@ -1,144 +1,211 @@
import pandas as pd
import os
from dtw import dtw
import json
import sqlite3
import numpy as np
from datetime import datetime, timedelta
from stock.analysis.Common import Common
from stock.analysis.Stochastic import Stochastic
from stock.analysis.RSI import RSI
from stock.analysis.MACD import MACD
from stock.analysis.IchimokuCloud import IchimokuCloud
class BuySellChecker():
PATTERNS = None
RESOURCE_PATH = None
class BuySellChecker:
common = None
stochastic = None
rsi = None
macd = None
ichimokuCloud = None
BUY_COUNT = None
def __init__(self):
self.common = Common()
self.stochastic = Stochastic()
self.rsi = RSI()
self.macd = MACD()
self.ichimokuCloud = IchimokuCloud()
self.BUY_COUNT = 0
def __init__(self, RESOURCE_PATH, ticker):
self.RESOURCE_PATH = RESOURCE_PATH
self.readBuyPattern(RESOURCE_PATH, ticker)
return
def getBuyPriceAndWeight(self, i, data):
buy, weight, type = -1, -1, ""
def readBuyPattern(self, RESOURCE_PATH, ticker):
with open(os.path.join(RESOURCE_PATH, "buy_pattern_data.json"), 'r') as f:
PATTERNS = json.load(f)
"""
# 매수전략 #1: 다이버전스
if data['macd'][i] < 0 and data['open'][i] < data['close'][i]:
if 0 < len(data['rsi'].tolist()[i - 10:i - 5]):
if min(data['rsi'].tolist()[i - 10:i - 5]) < data['rsi'][i - 1]:
if data['low'][i - 1] < min(data['low'].tolist()[i - 10:i - 5]):
weight = 1
buy = data['close'][i]
type = 'Divergence'
"""
self.PATTERNS = {'min_max': [], 'stndardization': []}
for key in PATTERNS:
for min_max in PATTERNS[key]['min_max']:
self.PATTERNS['min_max'].append(min_max)
for stndardization in PATTERNS[key]['stndardization']:
self.PATTERNS['stndardization'].append(stndardization)
return
high_barrier = 70
low_barrier = 30
Buy_Price=[]
Sell_Price=[]
number=[]
temp01 = []
temp01_id = []
temp02 = []
temp01_id = []
temp01_min_price = []
temp02_min_price = []
temp01_min_rsi = []
temp02_min_rsi = []
n_id=[]
i_id=[]
flag=1
n = 0
def nearDisparity(self, data, i):
if (0.998 < data['disparity_avg5'][i] < 1.002 and
0.998 < data['disparity_avg5'][i] < 1.002 and
0.998 < data['disparity_avg5'][i] < 1.002 and
0.998 < data['disparity_avg5'][i] < 1.002 and
0.998 < data['disparity_avg5'][i] < 1.002):
return True
return False
# https://superhky.tistory.com/441
find = False
for c in range(i-40, i-1):
if data['rsi'][i-1] > low_barrier and data['rsi'][i] < low_barrier:
for k in range(c, i):
if data['rsi'][k-1] < low_barrier and data['rsi'][k] > low_barrier:
temp01 = data['rsi'].iloc[c:k]
temp01_id = temp01.argmin() + c
temp01_min_rsi = data['rsi'][temp01_id]
temp01_min_price = data['close'][temp01_id]
def cosine_similarity(self, x, y):
return np.dot(x, y) / (np.sqrt(np.dot(x, x)) * np.sqrt(np.dot(y, y)))
for m in range(k, i):
if data['rsi'][m-1] < low_barrier and data['rsi'][m] < low_barrier:
for n in range(m, i):
if data['rsi]'][n-1] < low_barrier and data['rsi'][n] < low_barrier:
temp02 = data['rsi'].iloc[m:n]
temp02_id = temp02.argmin() + m
temp02_min_rsi = data['rsi'][temp02_id]
temp02_min_price = data['close'][temp02_id]
"""
def findBuyPoint(self, data, data_signal, i):
# 코사인 유사도(cosine similarity)로 과거 주가의 유사 패턴을 찾아 미래 예측하기
# https://teddylee777.github.io/pandas/cos-sim-stock/
if temp01_min_rsi < temp02_min_rsi and temp01_min_price > temp02_min_price and flag == 1:
if c == i-1:
weight = 1
buy = data['close'][i]
type = 'Divergence'
find = True
break
if find: break
if find: break
if find: break
"""
# 매수전략 #3: stochastic + rsi + macd
check = False
if data['slow_k'][i - 1] < data['slow_k'][i] and data['slow_d'][i] < data['slow_k'][i]:
buy_target = data['close'].iloc[i-179:i+1]
window_size = len(buy_target)
if window_size == 180:
buy_target = (buy_target - buy_target.min()) / (buy_target.max() - buy_target.min())
for pattern in self.PATTERNS:
cos_similarity = self.cosine_similarity(pattern, buy_target)
if 0.995 < cos_similarity:
return True
# 과매도 체크
index = -1
for c in range(i - 40, i):
if data['slow_k'][i] < 20:
index = c
check = True
if check:
# 과매도 후 과매수 였는지 체크
check = False
for d in range(index, i):
if 80 < data['slow_k'][d]:
check = True
break
if not check:
# 과매도 후 과매수가 아니라면
if data['rsi'][i - 1] < 50 and 50 < data['rsi'][i]:
if data['macds'][i] < data['macd'][i] < 0:
weight = 1
buy = data['close'][i]
type = 'S+R+M'
"""
return buy, weight, type
return False
"""
def findBuyPoint(self, data, i):
# DTW (Dynamic Time Warping)
# 시계열 유사도: https://m.blog.naver.com/happyrachy/221693939341
if i < 24:
return False
for p in range(len(self.PATTERNS['min_max'])):
size = len(self.PATTERNS['stndardization'][p])
if i - size + 1 < 0:
continue
close = data['close'].iloc[i-size+1:i+1]
#min_max = np.array(self.PATTERNS['min_max'][p]).reshape(-1, 1)
stndardization = np.array(self.PATTERNS['stndardization'][p]).reshape(-1, 1)
#min_max_y = np.array((close - close.min()) / (close.max() - close.min())).reshape(-1, 1)
stndardization_y = np.array((close - close.mean()) / close.std()).reshape(-1, 1)
#manhattan_distance = lambda min_max, min_max_y: np.abs(min_max - min_max_y)
#min_max_d, cost_matrix, acc_cost_matrix, path = dtw(min_max, min_max_y, dist=manhattan_distance)
manhattan_distance = lambda stndardization, stndardization_y: np.abs(stndardization - stndardization_y)
stndardization_d, cost_matrix, acc_cost_matrix, path = dtw(stndardization, stndardization_y, dist=manhattan_distance)
if stndardization_d < 2:
#print(i, data['ymd'].iloc[i], stndardization_d)
return True
return False
def getMacd(self, ticker_code, day, mins=1):
table = 'minutely_max_macd_' + str(mins)
conn = sqlite3.connect(os.path.join(self.RESOURCE_PATH, 'coins.db'))
cursor = conn.cursor()
day1 = (datetime.strptime(day, '%Y%m%d') - timedelta(1)).strftime('%Y%m%d')
cursor.execute('SELECT ymd, hms, macd, close FROM '+table+' WHERE (CODE=? or CODE=?) and (ymd=? or ymd=?) order by macd desc', (ticker_code, ticker_code.replace('KRW-', ''), day, day1, ))
db_result1 = cursor.fetchall()
cursor.close()
conn.close()
macd_limit = [(datetime.strptime(rows[0]+" "+rows[1], '%Y%m%d %H%M%S'), rows[2], rows[3]) for rows in db_result1]
macd_dup = list(set(macd_limit))
return macd_dup
def getBuyPriceAndWeight1(self, ticker, MAX_BUY_PRICE, i, data, data_signal, BUY_LIST, isRealTime=True):
buy_ymd, buy_price, buy_count, buy_cut, buy_type = None, -1, -1, -1, ''
df_tmp = data_signal['ymd'] <= data['ymd'][i]
df_signal = data_signal.loc[df_tmp]
si = len(df_signal) - 1
if isRealTime:
macds = data['macd'][i-300:i].to_list()
if 0 < len(macds):
macds_max = max(macds)
mi = i-300 + macds.index(macds_max)
if data['macd'][i] < macds_max and data['close'][mi] < data['close'][i]:
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
else:
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
else:
macds = self.getMacd(ticker['ticker_code'], data['ymd'][i].strftime('%Y%m%d'), mins=1)
if len(macds) == 0:
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
macds_sort = sorted(macds, key=lambda x:x[0], reverse=True)
if data['macd'][i] < macds_sort[0][1] and macds_sort[0][2] < data['close'][i]:
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
duration = 3
if sum(data['trend_avg'][i - duration:i]) / duration < data['trend_avg'][i]:
# 상승 트렌드
if data_signal['avg20'][si] < data_signal['avg5'][si]:
# 방법 1:
if max(data['volume_up'][i-180:i]) < data['volume_up'][i]:
if data_signal['slow_k'][si] < 70:
if BUY_LIST is not None and 0 < len(BUY_LIST['buy_list']) and BUY_LIST['buy_list'][-1]['buy_price'] < data['close'][i]:
buy_price = data['close'][i]
buy_type = 'volume_up'
buy_ymd = data['ymd'][i]
if data['slow_k'][si] < 30:
buy_count = MAX_BUY_PRICE / (1 * data['close'][i])
elif data['slow_k'][si] < 50:
buy_count = MAX_BUY_PRICE / (2 * data['close'][i])
else:
buy_count = MAX_BUY_PRICE / (3 * data['close'][i])
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
else:
buy_price = data['close'][i]
buy_type = 'volume_up'
buy_ymd = data['ymd'][i]
if data['slow_k'][si] < 30:
buy_count = MAX_BUY_PRICE / (1 * data['close'][i])
elif data['slow_k'][si] < 50:
buy_count = MAX_BUY_PRICE / (2 * data['close'][i])
else:
buy_count = MAX_BUY_PRICE / (3 * data['close'][i])
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
# 방법 2:
if data['avg480'][i] < data['avg120'][i] < data['avg60'][i] < data['avg20'][i] < data['avg5'][i] < data['close'][i]:
if data['avg240'][i] < min(data['avg5'][i], data['avg20'][i], data['avg60'][i], data['avg120'][i]):
if BUY_LIST is not None and 0 < len(BUY_LIST['buy_list']) and data['ymd'][i] < BUY_LIST['buy_list'][-1]['buy_ymd'] + timedelta(minutes=10):
if BUY_LIST['buy_list'][-1]['buy_price'] < data['close'][i]:
buy_price = data['close'][i]
buy_type = 'golden'
buy_ymd = data['ymd'][i]
if data['slow_k'][si] < 30:
buy_count = MAX_BUY_PRICE / (1 * data['close'][i])
elif data['slow_k'][si] < 50:
buy_count = MAX_BUY_PRICE / (2 * data['close'][i])
else:
buy_count = MAX_BUY_PRICE / (3 * data['close'][i])
else:
buy_price = data['close'][i]
buy_type = 'golden'
buy_ymd = data['ymd'][i]
if data['slow_k'][si] < 30:
buy_count = MAX_BUY_PRICE / (1 * data['close'][i])
elif data['slow_k'][si] < 50:
buy_count = MAX_BUY_PRICE / (2 * data['close'][i])
else:
buy_count = MAX_BUY_PRICE / (3 * data['close'][i])
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
def getSellPriceAndWeight(self, i, data):
sell, weight, type = -1, -1, ""
return buy_ymd, buy_price, buy_count, buy_cut, buy_type
max_value = max(data['macd'].tolist()) * 0.8
if (max_value < data['macd'][i] or 1.9 < data['macds'][i]) and (0 < data['macdo'][i-1] and data['macdo'][i] <= 0):
#if data['macds'][i-1] < data['macd'][i-1] and data['macd'][i] < data['macds'][i]:
weight = 1
sell = data['close'][i]
type = 'method1'
def getSellPriceAndWeight1(self, ticker, i, data, data_signal, BUY_LIST=None):
sell_price, sell_count = -1, -1
# 매수전략 #2: RSI 과매수에서 데드크로스
if (data['macds'][i - 1] < data['macd'][i - 1] and data['macd'][i] < data['macds'][i]):
if 70 < data['rsi'][i]:
weight = 1
sell = data['close'][i]
type = 'method2'
if BUY_LIST is not None and 0 < len(BUY_LIST['buy_list']):
# 방법1에 대해서는 1% 이익시 매도 한다. (Upbit.py 파일에서)
# 방법2에 대한 매도
if data['close'][i-1] < data['open'][i-1] and data['close'][i] < data['open'][i]:
count = sum([price['buy_count'] for price in BUY_LIST['buy_list'] if price['buy_type'] == 'golden'])
if 0 < count:
sell_price = data['close'][i]
sell_count = sum([price['buy_count'] for price in BUY_LIST['buy_list']])
return sell, weight, type
return sell_price, sell_count
def checkTransaction1(self, ticker, MAX_BUY_PRICE, data, data_signal, BUY_LIST=None, isRealTime=True):
def checkTransaction(self, data, isRealTime=True):
# 어제 오늘 데이터로 분석
bsLine = {}
@@ -149,150 +216,52 @@ class BuySellChecker:
# isRealTime=True, 실시간 적용
last_index = size - 1
buy, buy_weight, buy_type = self.getBuyPriceAndWeight(last_index, data)
sell, sell_weight, sell_type = self.getSellPriceAndWeight(last_index, data)
bsLine['buy'] = [buy]
bsLine['buy_weight'] = [buy_weight]
bsLine['buy_type'] = [buy_type]
bsLine['sell'] = [sell]
sell_price, sell_weight = self.getSellPriceAndWeight1(ticker, last_index, data, data_signal, BUY_LIST)
bsLine['sell_price'] = [sell_price]
bsLine['sell_weight'] = [sell_weight]
bsLine['sell_type'] = [sell_type]
buy_ymd, buy_price, buy_count, buy_cut, buy_type = self.getBuyPriceAndWeight1(ticker, MAX_BUY_PRICE, last_index, data, data_signal, BUY_LIST, isRealTime)
bsLine['buy_ymd'] = [buy_ymd]
bsLine['buy_price'] = [buy_price]
bsLine['buy_count'] = [buy_count]
bsLine['buy_cut'] = [buy_cut]
bsLine['buy_type'] = [buy_type]
if BUY_LIST is not None and 0 < buy_price:
BUY_LIST['buy_list'].append({'buy_ymd': buy_ymd, 'buy_price': buy_price, 'buy_count': buy_count, 'buy_cut': buy_cut, 'buy_type': buy_type})
else:
# Type=False, 시뮬레이션 적용
bsLine['buy'] = [-1 for i in range(size)]
bsLine['buy_weight'] = [-1 for i in range(size)]
bsLine['buy_ymd'] = [-1 for i in range(size)]
bsLine['buy_price'] = [-1 for i in range(size)]
bsLine['buy_count'] = [-1 for i in range(size)]
bsLine['buy_cut'] = [-1 for i in range(size)]
bsLine['buy_type'] = ['' for i in range(size)]
bsLine['sell'] = [-1 for i in range(size)]
bsLine['sell_price'] = [-1 for i in range(size)]
bsLine['sell_weight'] = [-1 for i in range(size)]
bsLine['sell_type'] = ['' for i in range(size)]
for last_index in range(size):
buy, buy_weight, buy_type = self.getBuyPriceAndWeight(last_index, data)
sell, sell_weight, sell_type = self.getSellPriceAndWeight(last_index, data)
bsLine['buy'][last_index] = buy
bsLine['buy_weight'][last_index] = buy_weight
bsLine['buy_type'][last_index] = buy_type
bsLine['sell'][last_index] = sell
sell_price, sell_weight = self.getSellPriceAndWeight1(ticker, last_index, data, data_signal, BUY_LIST)
bsLine['sell_price'][last_index] = sell_price
bsLine['sell_weight'][last_index] = sell_weight
bsLine['sell_type'][last_index] = sell_type
if sell_price < 0:
buy_ymd, buy_price, buy_count, buy_cut, buy_type = self.getBuyPriceAndWeight1(ticker, MAX_BUY_PRICE, last_index, data, data_signal, BUY_LIST, isRealTime)
bsLine['buy_price'][last_index] = buy_price
bsLine['buy_count'][last_index] = buy_count
bsLine['buy_cut'][last_index] = buy_cut
bsLine['buy_type'][last_index] = buy_type
if BUY_LIST is not None and 0 < buy_price:
BUY_LIST['buy_list'].append({'buy_ymd': buy_ymd, 'buy_price': buy_price, 'buy_count': buy_count, 'buy_cut': buy_cut, 'buy_type': buy_type})
else:
bsLine['buy'] = [-1]
bsLine['buy_weight'] = [-1]
bsLine['buy_price'] = [-1]
bsLine['buy_count'] = [-1]
bsLine['buy_cut'] = [-1]
bsLine['buy_type'] = ['']
bsLine['sell'] = [-1]
bsLine['sell_price'] = [-1]
bsLine['sell_weight'] = [-1]
bsLine['sell_type'] = ['']
return bsLine
def analyze(self, result):
# 기본 캔들 정보
open = result["open"]
close = result["close"]
high = result["high"]
low = result["low"]
vol = result["vol"]
# 이동 평균
close_df = pd.DataFrame(close)
avg5_list = close_df.rolling(window=5).mean().fillna(close[0]).values.tolist()
avg5 = [item[0] for item in avg5_list]
avg20_list = close_df.rolling(window=20).mean().fillna(close[0]).values.tolist()
avg20 = [item[0] for item in avg20_list]
avg30_list = close_df.rolling(window=30).mean().fillna(close[0]).values.tolist()
avg30 = [item[0] for item in avg30_list]
avg60_list = close_df.rolling(window=60).mean().fillna(close[0]).values.tolist()
avg60 = [item[0] for item in avg60_list]
avg120_list = close_df.rolling(window=120).mean().fillna(close[0]).values.tolist()
avg120 = [item[0] for item in avg120_list]
avg200_list = close_df.rolling(window=200).mean().fillna(close[0]).values.tolist()
avg200 = [item[0] for item in avg200_list]
open_df = pd.DataFrame(close)
disparity_avg5_list = (open_df / close_df.rolling(window=5).mean()).values.tolist()
disparity_avg5 = [item[0] for item in disparity_avg5_list]
disparity_avg20_list = (open_df / close_df.rolling(window=20).mean()).values.tolist()
disparity_avg20 = [item[0] for item in disparity_avg20_list]
disparity_avg30_list = (open_df / close_df.rolling(window=30).mean()).values.tolist()
disparity_avg30 = [item[0] for item in disparity_avg30_list]
disparity_avg60_list = (open_df / close_df.rolling(window=60).mean()).values.tolist()
disparity_avg60 = [item[0] for item in disparity_avg60_list]
disparity_avg120_list = (open_df / close_df.rolling(window=120).mean()).values.tolist()
disparity_avg120 = [item[0] for item in disparity_avg120_list]
disparity_avg200_list = (open_df / close_df.rolling(window=200).mean()).values.tolist()
disparity_avg200 = [item[0] for item in disparity_avg200_list]
# 볼린져 밴드
df = pd.DataFrame(close)
max20 = df.rolling(window=20).mean()
stddev20 = df.rolling(window=20).std()
upper_df = max20 + (stddev20 * 2) # 상단 볼린저 밴드
lower_df = max20 - (stddev20 * 2) # 하단 볼린저 밴드
upper, lower = [], []
for i in range(len(upper_df)):
if i < 10:
upper.append(upper_df.values[0][0])
lower.append(lower_df.values[0][0])
else:
upper.append(upper_df.values[i][0])
lower.append(lower_df.values[i][0])
point_temp = result["time"]
STOCK = []
for i in range(len(open)):
STOCK.append({'volume': vol[i], 'close': close[i], 'open': open[i], 'high': high[i], 'low': low[i],
'avg5': avg5[i], 'avg20': avg20[i], 'avg30': avg30[i], 'avg60': avg60[i], 'avg120': avg120[i], 'avg200': avg200[i]})
# stochastic
stochastic_df = self.stochastic.apply(STOCK, n=30, m=5, t=5)
fast_k = stochastic_df['fast_k'].values.tolist()
slow_k = stochastic_df['slow_k'].values.tolist()
slow_d = stochastic_df['slow_d'].values.tolist()
# macd
#macd_df = self.macd.apply(STOCK, short=12, long=26, t=9)
macd_df = self.macd.apply(STOCK, short=5, long=20, t=5)
macd = macd_df['macd'].values.tolist()
macds = macd_df['macds'].values.tolist()
macdo = macd_df['macdo'].values.tolist()
# rsi
rsi_df = self.rsi.apply(STOCK, period=30, window=5)
rsi = rsi_df['rsi'].values.tolist()
rsis = rsi_df['rsis'].values.tolist()
# ichimokuCloud
ichimokuCloud_df = self.ichimokuCloud.apply(STOCK, c=9, b=26, l=52)
ichimokuCloud_df = ichimokuCloud_df[:len(ichimokuCloud_df) - 51]
changeLine = ichimokuCloud_df['changeLine'].values.tolist()
baseLine = ichimokuCloud_df['baseLine'].values.tolist()
laggingSpan = ichimokuCloud_df['laggingSpan'].values.tolist()
leadingSpan1 = ichimokuCloud_df['leadingSpan1'].values.tolist()
leadingSpan2 = ichimokuCloud_df['leadingSpan2'].values.tolist()
# 결과
temp = {
"date": point_temp,
"open": open, "high": high, "low": low, "close": close, "volume": vol,
"avg5": avg5, "avg20": avg20, "avg30": avg30, "avg60": avg60, "avg120": avg120, "avg200": avg200,
"disparity_avg5": disparity_avg5, "disparity_avg20": disparity_avg20, "disparity_avg30": disparity_avg30,
"disparity_avg60": disparity_avg60, "disparity_avg120": disparity_avg120, "disparity_avg200": disparity_avg200,
"upper": upper, "lower": lower,
"macd": macd, "macds": macds, "macdo": macdo,
"fast_k": fast_k, "slow_k": slow_k, "slow_d": slow_d,
"rsi": rsi, "rsis": rsis,
"changeLine": changeLine, "baseLine": baseLine, "laggingSpan": laggingSpan, "leadingSpan1": leadingSpan1,
"leadingSpan2": leadingSpan2,
}
data = pd.DataFrame(temp)
df_final_time = pd.DatetimeIndex(point_temp)
data.index = df_final_time
data = data.fillna(-1)
return data