analyser_hj3415 3.2.1__py3-none-any.whl → 3.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- analyser_hj3415/__init__.py +0 -5
- analyser_hj3415/analyser/__init__.py +30 -0
- analyser_hj3415/analyser/compile.py +84 -65
- analyser_hj3415/analyser/eval/blue.py +78 -9
- analyser_hj3415/analyser/eval/common.py +72 -110
- analyser_hj3415/analyser/eval/growth.py +77 -6
- analyser_hj3415/analyser/eval/mil.py +119 -18
- analyser_hj3415/analyser/eval/red.py +95 -66
- analyser_hj3415/analyser/tsa/__init__.py +1 -13
- analyser_hj3415/analyser/tsa/common.py +33 -0
- analyser_hj3415/analyser/tsa/lstm.py +108 -137
- analyser_hj3415/analyser/tsa/prophet.py +262 -126
- analyser_hj3415/cli.py +13 -11
- {analyser_hj3415-3.2.1.dist-info → analyser_hj3415-3.3.0.dist-info}/METADATA +1 -1
- analyser_hj3415-3.3.0.dist-info/RECORD +23 -0
- analyser_hj3415-3.2.1.dist-info/RECORD +0 -22
- {analyser_hj3415-3.2.1.dist-info → analyser_hj3415-3.3.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-3.2.1.dist-info → analyser_hj3415-3.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,3 @@
|
|
1
|
-
"""
|
2
|
-
Time Series Analysis
|
3
|
-
"""
|
4
1
|
import os
|
5
2
|
import numpy as np
|
6
3
|
import yfinance as yf
|
@@ -11,16 +8,16 @@ import plotly.graph_objs as go
|
|
11
8
|
from plotly.offline import plot
|
12
9
|
import matplotlib.pyplot as plt # Matplotlib 수동 임포트
|
13
10
|
from sklearn.preprocessing import MinMaxScaler
|
14
|
-
from tensorflow.keras.models import Sequential
|
15
|
-
from tensorflow.keras.layers import LSTM, Dense, Dropout
|
16
|
-
from tensorflow.keras.callbacks import EarlyStopping
|
17
|
-
from tensorflow.keras import Input
|
11
|
+
from tensorflow.keras.models import Sequential # type: ignore
|
12
|
+
from tensorflow.keras.layers import LSTM, Dense, Dropout # type: ignore
|
13
|
+
from tensorflow.keras.callbacks import EarlyStopping # type: ignore
|
14
|
+
from tensorflow.keras import Input # type: ignore
|
18
15
|
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
|
19
16
|
from dataclasses import dataclass
|
20
17
|
|
21
18
|
from utils_hj3415 import tools, setup_logger
|
22
19
|
from db_hj3415 import myredis
|
23
|
-
|
20
|
+
from analyser_hj3415.analyser import MIs, tsa
|
24
21
|
|
25
22
|
mylogger = setup_logger(__name__,'WARNING')
|
26
23
|
expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
|
@@ -28,6 +25,20 @@ expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
|
|
28
25
|
|
29
26
|
@dataclass
|
30
27
|
class LSTMData:
|
28
|
+
"""
|
29
|
+
LSTM 모델에서 사용할 데이터를 저장하는 데이터 클래스.
|
30
|
+
|
31
|
+
속성:
|
32
|
+
ticker (str): 주식 티커(symbol).
|
33
|
+
data_2d (np.ndarray): 원본 종가 데이터를 저장한 2차원 배열.
|
34
|
+
train_size (int): 학습 데이터 크기.
|
35
|
+
train_data_2d (np.ndarray): 학습 데이터 2차원 배열.
|
36
|
+
test_data_2d (np.ndarray): 테스트 데이터 2차원 배열.
|
37
|
+
X_train_3d (np.ndarray): 학습 데이터 3차원 배열.
|
38
|
+
X_test_3d (np.ndarray): 테스트 데이터 3차원 배열.
|
39
|
+
y_train_1d (np.ndarray): 학습 정답 데이터 1차원 배열.
|
40
|
+
y_test_1d (np.ndarray): 테스트 정답 데이터 1차원 배열.
|
41
|
+
"""
|
31
42
|
ticker: str
|
32
43
|
|
33
44
|
data_2d: np.ndarray
|
@@ -44,7 +55,16 @@ class LSTMData:
|
|
44
55
|
@dataclass
|
45
56
|
class LSTMGrade:
|
46
57
|
"""
|
47
|
-
|
58
|
+
LSTM 모델 학습 결과를 평가하기 위한 데이터 클래스.
|
59
|
+
|
60
|
+
속성:
|
61
|
+
ticker (str): 주식 티커(symbol).
|
62
|
+
train_mse (float): 학습 데이터에 대한 평균 제곱 오차(MSE).
|
63
|
+
train_mae (float): 학습 데이터에 대한 평균 절대 오차(MAE).
|
64
|
+
train_r2 (float): 학습 데이터에 대한 결정 계수(R²).
|
65
|
+
test_mse (float): 테스트 데이터에 대한 평균 제곱 오차(MSE).
|
66
|
+
test_mae (float): 테스트 데이터에 대한 평균 절대 오차(MAE).
|
67
|
+
test_r2 (float): 테스트 데이터에 대한 결정 계수(R²).
|
48
68
|
"""
|
49
69
|
ticker: str
|
50
70
|
train_mse: float
|
@@ -56,6 +76,16 @@ class LSTMGrade:
|
|
56
76
|
|
57
77
|
|
58
78
|
class MyLSTM:
|
79
|
+
"""
|
80
|
+
주가 데이터를 기반으로 LSTM 모델을 생성, 학습 및 예측하는 클래스.
|
81
|
+
|
82
|
+
속성:
|
83
|
+
future_days (int): 미래 예측할 일 수. 기본값은 30.
|
84
|
+
scaler (MinMaxScaler): 데이터 정규화를 위한 MinMaxScaler.
|
85
|
+
_ticker (str): 주식 티커(symbol).
|
86
|
+
raw_data (pd.DataFrame): 원본 주가 데이터.
|
87
|
+
lstm_data (LSTMData): LSTM 학습에 사용할 데이터.
|
88
|
+
"""
|
59
89
|
# 미래 몇일을 예측할 것인가?
|
60
90
|
future_days = 30
|
61
91
|
|
@@ -102,12 +132,11 @@ class MyLSTM:
|
|
102
132
|
|
103
133
|
def initializing(self):
|
104
134
|
"""
|
105
|
-
LSTM
|
106
|
-
get_final_predictions(refresh=True)를 시행하기전에 반드시 먼저 실행해줘아 한다.
|
135
|
+
LSTM 모델 학습을 위한 데이터를 준비합니다.
|
107
136
|
|
108
|
-
|
109
|
-
|
110
|
-
|
137
|
+
Yahoo Finance에서 주가 데이터를 가져와 정규화, 학습 및 테스트 데이터로 분리,
|
138
|
+
LSTM 모델 입력 형식으로 변환합니다. `get_final_predictions` 메서드 실행 전에
|
139
|
+
반드시 호출해야 합니다.
|
111
140
|
"""
|
112
141
|
def get_raw_data() -> pd.DataFrame:
|
113
142
|
"""
|
@@ -198,34 +227,24 @@ class MyLSTM:
|
|
198
227
|
y_train_1d=y_train_1d,
|
199
228
|
y_test_1d=y_test_1d,
|
200
229
|
)
|
230
|
+
|
231
|
+
self.scaler = MinMaxScaler(feature_range=(0, 1))
|
232
|
+
|
201
233
|
self.raw_data = get_raw_data()
|
202
234
|
self.lstm_data = preprocessing_for_lstm()
|
203
235
|
|
204
236
|
def ensemble_training(self, num) -> Tuple[list, LSTMGrade]:
|
205
237
|
"""
|
206
|
-
|
207
|
-
Defines functions for LSTM model training, prediction, grading results, and ensemble model training.
|
238
|
+
앙상블 LSTM 모델을 훈련하고, 예측 결과를 생성 및 평가합니다.
|
208
239
|
|
209
|
-
|
210
|
-
|
211
|
-
- prediction: Uses a trained model to perform predictions, restores normalization, and returns results.
|
212
|
-
- grading: Analyzes the training and testing predictions, computes evaluation metrics, and returns grading details.
|
213
|
-
- ensemble_training: Trains multiple LSTM models to create ensemble predictions and future forecasts.
|
240
|
+
매개변수:
|
241
|
+
num (int): 앙상블에 포함할 모델의 수.
|
214
242
|
|
215
|
-
|
216
|
-
|
217
|
-
and future trend forecasting. Evaluates the models collectively using grading metrics.
|
243
|
+
반환값:
|
244
|
+
Tuple[list, LSTMGrade]: 미래 예측 값 리스트와 학습 결과 평가 데이터.
|
218
245
|
|
219
|
-
|
220
|
-
|
221
|
-
The number of ensemble LSTM models to train.
|
222
|
-
|
223
|
-
Returns:
|
224
|
-
Tuple[list, LSTMGrade]
|
225
|
-
A list of predicted scaled values for future forecasts and the performance grading object.
|
226
|
-
|
227
|
-
Raises:
|
228
|
-
Does not explicitly raise errors but logs warnings for possible issues during training or prediction.
|
246
|
+
예외:
|
247
|
+
IndexError: 모델 훈련을 위한 데이터가 부족한 경우 경고 로그를 출력합니다.
|
229
248
|
"""
|
230
249
|
def model_training() -> Sequential:
|
231
250
|
# LSTM 모델 생성 - 유닛과 드롭아웃의 수는 테스트로 최적화 됨.
|
@@ -377,25 +396,18 @@ class MyLSTM:
|
|
377
396
|
|
378
397
|
def get_final_predictions(self, refresh: bool, num=5) -> Tuple[dict, LSTMGrade]:
|
379
398
|
"""
|
380
|
-
|
399
|
+
LSTM 모델을 사용하여 미래 주가를 예측하고 평가 데이터를 반환합니다.
|
381
400
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
and caches whether the predicted data demonstrates an increasing trend over time.
|
401
|
+
매개변수:
|
402
|
+
refresh (bool): 데이터 새로고침 여부.
|
403
|
+
num (int): 앙상블 모델의 수. 기본값은 5.
|
386
404
|
|
387
|
-
|
388
|
-
|
389
|
-
num (int): Number of times to repeat ensemble training for more consistent predictions.
|
390
|
-
Defaults to 5.
|
405
|
+
반환값:
|
406
|
+
Tuple[dict, LSTMGrade]: 날짜별 예측 주가와 모델 평가 데이터.
|
391
407
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
Raises:
|
397
|
-
AssertionError: Raised if the lengths of 'future_dates' and 'final_future_predictions' do
|
398
|
-
not match during the data preparation.
|
408
|
+
로그:
|
409
|
+
- 캐시 데이터 검색 및 새 데이터 생성 과정 출력.
|
410
|
+
- 예측 값의 증가 추세를 분석하여 캐시에 저장.
|
399
411
|
"""
|
400
412
|
print("**** Start get_final_predictions... ****")
|
401
413
|
redis_name = f'{self.ticker}_mylstm_predictions'
|
@@ -416,36 +428,8 @@ class MyLSTM:
|
|
416
428
|
redis_name = f'{self.ticker}_is_lstm_up'
|
417
429
|
print(f"redisname: '{redis_name}' / expire_time : {expire_time / 3600}h")
|
418
430
|
|
419
|
-
|
420
|
-
|
421
|
-
# 데이터가 비어있으면 추세를 판단할 수 없음
|
422
|
-
return False
|
423
|
-
|
424
|
-
# 1) 날짜(키) 기준 오름차순 정렬
|
425
|
-
sorted_dates = sorted(future_data_in.keys())
|
426
|
-
values = [future_data_in[d] for d in sorted_dates]
|
427
|
-
|
428
|
-
# 2) x 축을 0,1,2... 형태로 부여 (날짜 간격을 동일하게 가정)
|
429
|
-
x = np.arange(len(values), dtype=float)
|
430
|
-
y = np.array(values, dtype=float)
|
431
|
-
|
432
|
-
# 3) 선형 회귀(최소제곱법)로 기울기(slope) 계산
|
433
|
-
x_mean = np.mean(x)
|
434
|
-
y_mean = np.mean(y)
|
435
|
-
|
436
|
-
# 분자: sum((xi - x_mean) * (yi - y_mean))
|
437
|
-
numerator = np.sum((x - x_mean) * (y - y_mean))
|
438
|
-
# 분모: sum((xi - x_mean)^2)
|
439
|
-
denominator = np.sum((x - x_mean) ** 2)
|
440
|
-
|
441
|
-
if denominator == 0:
|
442
|
-
# 데이터가 1개 이하인 경우 등
|
443
|
-
return False
|
444
|
-
|
445
|
-
slope = numerator / denominator
|
446
|
-
|
447
|
-
# 4) 기울기가 양수면 "우상향 추세"로 판별
|
448
|
-
is_up = slope > 0
|
431
|
+
is_up = tsa.common.is_up_by_OLS(future_data_in)
|
432
|
+
mylogger.debug(f"is_up: {is_up}")
|
449
433
|
myredis.Base.set_value(redis_name, is_up, expire_time)
|
450
434
|
|
451
435
|
def fetch_final_predictions(num_in) -> tuple:
|
@@ -472,6 +456,7 @@ class MyLSTM:
|
|
472
456
|
data[future_dates[i].strftime("%Y-%m-%d")] = final_future_predictions[i][0]
|
473
457
|
return data
|
474
458
|
|
459
|
+
self.initializing()
|
475
460
|
# 앙상블 트레이닝 시행
|
476
461
|
future_predictions_2d, lstm_grade = self.ensemble_training(num=num_in)
|
477
462
|
mylogger.debug(f'future_predictions_2d[:5] : {future_predictions_2d[:5]}')
|
@@ -492,32 +477,21 @@ class MyLSTM:
|
|
492
477
|
|
493
478
|
return future_data, lstm_grade
|
494
479
|
|
495
|
-
def export(self, refresh=False, to="
|
480
|
+
def export(self, refresh=False, to="html", num=5) -> Optional[str]:
|
496
481
|
"""
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
Returns
|
512
|
-
-------
|
513
|
-
Optional[str]
|
514
|
-
A string representation of the graph in HTML format if `to` is set to 'str'.
|
515
|
-
Returns None if `to` is set to either 'png' or 'htmlfile'.
|
516
|
-
|
517
|
-
Raises
|
518
|
-
------
|
519
|
-
Exception
|
520
|
-
Raised if the `to` parameter does not match the allowed values: 'str', 'png', or 'htmlfile'.
|
482
|
+
과거 및 예측된 주가 데이터를 기반으로 시각화를 생성하고 저장합니다.
|
483
|
+
|
484
|
+
매개변수:
|
485
|
+
refresh (bool): 데이터 새로고침 여부. 기본값은 False.
|
486
|
+
to (str): 그래프 출력 형식 ('hrml', 'png', 'file'). 기본값은 'html'.
|
487
|
+
num (int): 예측 모델 수. 기본값은 5.
|
488
|
+
|
489
|
+
반환값:
|
490
|
+
Optional[str]: HTML 형식의 그래프 문자열(`to='html'`인 경우).
|
491
|
+
None: PNG 또는 HTML 파일로 저장된 경우.
|
492
|
+
|
493
|
+
예외:
|
494
|
+
Exception: 잘못된 `to` 값이 주어졌을 때 발생.
|
521
495
|
"""
|
522
496
|
def prepare_past_data(past_days) -> tuple:
|
523
497
|
# 데이터 준비
|
@@ -599,7 +573,7 @@ class MyLSTM:
|
|
599
573
|
showlegend=False,
|
600
574
|
)
|
601
575
|
|
602
|
-
if to == '
|
576
|
+
if to == 'html':
|
603
577
|
# 그래프 HTML로 변환 (string 형식으로 저장)
|
604
578
|
graph_html = plot(fig, output_type='div')
|
605
579
|
return graph_html
|
@@ -607,7 +581,7 @@ class MyLSTM:
|
|
607
581
|
# 그래프를 PNG 파일로 저장
|
608
582
|
fig.write_image(f"myLSTM_{self.ticker}.png")
|
609
583
|
return None
|
610
|
-
elif to == '
|
584
|
+
elif to == 'file':
|
611
585
|
# 그래프를 HTML로 저장
|
612
586
|
plot(fig, filename=f'myLSTM_{self.ticker}.html', auto_open=False)
|
613
587
|
return None
|
@@ -616,24 +590,13 @@ class MyLSTM:
|
|
616
590
|
|
617
591
|
def visualization(self, refresh=True):
|
618
592
|
"""
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
refresh : bool, optional
|
627
|
-
Indicates whether to refresh and retrieve the latest predictions before
|
628
|
-
visualizing. Defaults to True.
|
629
|
-
|
630
|
-
Raises
|
631
|
-
------
|
632
|
-
None
|
633
|
-
|
634
|
-
Returns
|
635
|
-
-------
|
636
|
-
None
|
593
|
+
실제 주가와 예측 주가를 시각화합니다.
|
594
|
+
|
595
|
+
매개변수:
|
596
|
+
refresh (bool): 예측 데이터를 새로고침할지 여부. 기본값은 True.
|
597
|
+
|
598
|
+
반환값:
|
599
|
+
None: 시각화를 출력합니다.
|
637
600
|
"""
|
638
601
|
self.initializing()
|
639
602
|
future_data, _ = self.get_final_predictions(refresh=refresh)
|
@@ -670,18 +633,22 @@ class MyLSTM:
|
|
670
633
|
|
671
634
|
def is_lstm_up(self) -> bool:
|
672
635
|
"""
|
673
|
-
|
636
|
+
LSTM 모델의 추세가 상승인지 여부를 확인합니다.
|
674
637
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
Returns:
|
679
|
-
bool: True if the LSTM model is active (up), False otherwise.
|
638
|
+
반환값:
|
639
|
+
bool: 추세가 상승 상태(True) 또는 추세가 하락 상태(False).
|
680
640
|
"""
|
681
641
|
return myredis.Base.get_value(f'{self.ticker}_is_lstm_up')
|
682
642
|
|
683
643
|
|
684
644
|
class CorpLSTM(MyLSTM):
|
645
|
+
"""
|
646
|
+
특정 기업 코드를 기반으로 주가를 예측하는 LSTM 클래스.
|
647
|
+
|
648
|
+
속성:
|
649
|
+
code (str): 기업 코드.
|
650
|
+
name (str): 기업명.
|
651
|
+
"""
|
685
652
|
def __init__(self, code: str):
|
686
653
|
assert tools.is_6digit(code), f'Invalid value : {code}'
|
687
654
|
self._code = code
|
@@ -701,11 +668,16 @@ class CorpLSTM(MyLSTM):
|
|
701
668
|
|
702
669
|
|
703
670
|
class MILSTM(MyLSTM):
|
671
|
+
"""
|
672
|
+
특정 MI 타입에 따라 주가를 예측하는 LSTM 클래스.
|
673
|
+
|
674
|
+
속성:
|
675
|
+
mi_type (str): MI 타입.
|
676
|
+
"""
|
704
677
|
def __init__(self, mi_type: str):
|
705
|
-
|
706
|
-
assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
|
678
|
+
assert mi_type in MIs._fields, f"Invalid MI type ({MIs._fields})"
|
707
679
|
self._mi_type = mi_type
|
708
|
-
super().__init__(ticker=MIs
|
680
|
+
super().__init__(ticker=getattr(MIs, mi_type))
|
709
681
|
|
710
682
|
@property
|
711
683
|
def mi_type(self) -> str:
|
@@ -713,8 +685,7 @@ class MILSTM(MyLSTM):
|
|
713
685
|
|
714
686
|
@mi_type.setter
|
715
687
|
def mi_type(self, mi_type: str):
|
716
|
-
|
717
|
-
assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
|
688
|
+
assert mi_type in MIs._fields, f"Invalid MI type ({MIs._fields})"
|
718
689
|
self._mi_type = mi_type
|
719
|
-
self.ticker = MIs
|
690
|
+
self.ticker = getattr(MIs, mi_type)
|
720
691
|
|