Dung Pham Anh commited on
Commit
b2fcf54
·
1 Parent(s): 0647700

update finr

Browse files
Files changed (5) hide show
  1. app.py +21 -8
  2. finrl_agent.py +32 -11
  3. requirements.txt +1 -1
  4. ui.py +20 -12
  5. utils.py +145 -60
app.py CHANGED
@@ -43,16 +43,29 @@ if submit:
43
  history['EMA9'] = utils.calculate_ema(history, 9)
44
  history['VWAP'] = utils.calculate_vwap(history)
45
 
46
- # Khởi tạo huấn luyện mô hình RL
47
- rl_trader = RLTrader()
48
- rl_trader.train(history)
 
 
 
49
 
50
- # Dự đoán tín hiệu giao dịch lấy hiệu suất
51
- rl_signals, performance = rl_trader.predict(history)
52
 
53
- # Thêm tín hiệu RL vào history lưu thông tin hiệu suất
54
- history = pd.concat([history, rl_signals], axis=1)
55
- history.attrs.update(performance)
 
 
 
 
 
 
 
 
 
 
56
 
57
  # Tạo tín hiệu giao dịch
58
  signals = utils.generate_rsi_macd_signals(history)
 
43
  history['EMA9'] = utils.calculate_ema(history, 9)
44
  history['VWAP'] = utils.calculate_vwap(history)
45
 
46
+ # Chuẩn bị dữ liệu cho mô hình RL
47
+ history_for_rl = history.copy()
48
+ # Chuyển đổi tất cả các cột số sang float64
49
+ numeric_columns = ['close', 'volume', 'RSI', 'MACD', 'Signal', 'EMA20', 'EMA50']
50
+ for col in numeric_columns:
51
+ history_for_rl[col] = pd.to_numeric(history_for_rl[col], errors='coerce')
52
 
53
+ # Kiểm traxử missing values
54
+ history_for_rl = history_for_rl.fillna(method='ffill')
55
 
56
+ # Khởi tạohuấn luyện hình RL
57
+ try:
58
+ rl_trader = RLTrader()
59
+ rl_trader.train(history_for_rl)
60
+
61
+ # Dự đoán tín hiệu giao dịch và lấy hiệu suất
62
+ rl_signals, performance = rl_trader.predict(history_for_rl)
63
+
64
+ # Thêm tín hiệu RL vào history và lưu thông tin hiệu suất
65
+ history = pd.concat([history, rl_signals], axis=1)
66
+ history.attrs.update(performance)
67
+ except Exception as e:
68
+ st.error(f"Lỗi trong quá trình huấn luyện mô hình RL: {str(e)}")
69
 
70
  # Tạo tín hiệu giao dịch
71
  signals = utils.generate_rsi_macd_signals(history)
finrl_agent.py CHANGED
@@ -38,16 +38,17 @@ class StockTradingEnv(gym.Env):
38
  return self._next_observation(), {}
39
 
40
  def _next_observation(self):
 
41
  obs = np.array([
42
- self.df['close'].iloc[self.current_step],
43
- self.df['volume'].iloc[self.current_step],
44
- self.df['RSI'].iloc[self.current_step],
45
- self.df['MACD'].iloc[self.current_step],
46
- self.df['Signal'].iloc[self.current_step],
47
- self.df['EMA20'].iloc[self.current_step],
48
- self.df['EMA50'].iloc[self.current_step]
49
- ])
50
- return obs.astype(np.float32)
51
 
52
  def step(self, action):
53
  current_price = self.df['close'].iloc[self.current_step]
@@ -94,8 +95,16 @@ class RLTrader:
94
 
95
  def train(self, df):
96
  """Huấn luyện mô hình RL"""
 
 
 
 
 
 
 
 
97
  # Tạo môi trường
98
- env = DummyVecEnv([lambda: StockTradingEnv(df)])
99
 
100
  # Khởi tạo và huấn luyện mô hình
101
  self.model = PPO("MlpPolicy", env, verbose=0,
@@ -115,12 +124,24 @@ class RLTrader:
115
  obs = env.reset()[0]
116
 
117
  actions = []
118
- for _ in range(len(df)):
 
 
 
119
  action, _ = self.model.predict(obs, deterministic=True)
120
  actions.append(action)
 
121
  obs, _, done, _, _ = env.step(action)
 
122
  if done:
 
 
 
 
123
  break
 
 
 
124
 
125
  # Chuyển đổi actions thành tín hiệu
126
  signals = pd.DataFrame({
 
38
  return self._next_observation(), {}
39
 
40
  def _next_observation(self):
41
+ # Chuyển đổi các giá trị sang float64 trước khi tạo array
42
  obs = np.array([
43
+ float(self.df['close'].iloc[self.current_step]),
44
+ float(self.df['volume'].iloc[self.current_step]),
45
+ float(self.df['RSI'].iloc[self.current_step]),
46
+ float(self.df['MACD'].iloc[self.current_step]),
47
+ float(self.df['Signal'].iloc[self.current_step]),
48
+ float(self.df['EMA20'].iloc[self.current_step]),
49
+ float(self.df['EMA50'].iloc[self.current_step])
50
+ ], dtype=np.float32)
51
+ return obs
52
 
53
  def step(self, action):
54
  current_price = self.df['close'].iloc[self.current_step]
 
95
 
96
  def train(self, df):
97
  """Huấn luyện mô hình RL"""
98
+ # Kiểm tra dữ liệu đầu vào
99
+ if not all(col in df.columns for col in ['close', 'volume', 'RSI', 'MACD', 'Signal', 'EMA20', 'EMA50']):
100
+ raise ValueError("Thiếu một số cột dữ liệu cần thiết cho mô hình RL")
101
+
102
+ # Kiểm tra giá trị NaN
103
+ if df[['close', 'volume', 'RSI', 'MACD', 'Signal', 'EMA20', 'EMA50']].isna().any().any():
104
+ raise ValueError("Dữ liệu chứa giá trị NaN")
105
+
106
  # Tạo môi trường
107
+ env = DummyVecEnv([lambda: StockTradingEnv(df.copy())])
108
 
109
  # Khởi tạo và huấn luyện mô hình
110
  self.model = PPO("MlpPolicy", env, verbose=0,
 
124
  obs = env.reset()[0]
125
 
126
  actions = []
127
+ states = []
128
+ current_step = 0
129
+
130
+ while current_step < len(df):
131
  action, _ = self.model.predict(obs, deterministic=True)
132
  actions.append(action)
133
+ states.append(obs)
134
  obs, _, done, _, _ = env.step(action)
135
+ current_step += 1
136
  if done:
137
+ # Nếu đã kết thúc nhưng chưa đủ độ dài, thêm hành động giữ
138
+ while current_step < len(df):
139
+ actions.append(1) # 1 = Giữ
140
+ current_step += 1
141
  break
142
+
143
+ # Đảm bảo độ dài của actions bằng với độ dài của DataFrame
144
+ actions = actions[:len(df)]
145
 
146
  # Chuyển đổi actions thành tín hiệu
147
  signals = pd.DataFrame({
requirements.txt CHANGED
@@ -25,7 +25,7 @@ kiwisolver==1.4.8
25
  MarkupSafe==3.0.2
26
  matplotlib==3.10.1
27
  narwhals==1.32.0
28
- numpy==2.2.4
29
  openpyxl==3.1.5
30
  packaging==24.2
31
  pandas==2.2.3
 
25
  MarkupSafe==3.0.2
26
  matplotlib==3.10.1
27
  narwhals==1.32.0
28
+ numpy==1.26.4
29
  openpyxl==3.1.5
30
  packaging==24.2
31
  pandas==2.2.3
ui.py CHANGED
@@ -499,7 +499,7 @@ def display_signals(history, stock_symbol):
499
  'RSI': buy_signals['RSI'].round(2),
500
  'MACD': buy_signals['MACD'].round(2)
501
  })
502
- timeline_df = pd.concat([timeline_df, buy_df])
503
 
504
  # Thêm tín hiệu bán
505
  if not sell_signals.empty:
@@ -510,7 +510,7 @@ def display_signals(history, stock_symbol):
510
  'RSI': sell_signals['RSI'].round(2),
511
  'MACD': sell_signals['MACD'].round(2)
512
  })
513
- timeline_df = pd.concat([timeline_df, sell_df])
514
 
515
  # Sắp xếp theo ngày
516
  if not timeline_df.empty:
@@ -587,15 +587,21 @@ def display_signals(history, stock_symbol):
587
 
588
  with signals_tab2:
589
  if not sell_signals.empty:
590
- sell_signals_display = sell_signals[['time', 'close', 'RSI', 'MACD', 'Signal']].rename(
591
- columns={
592
- 'time': 'Ngày',
593
- 'close': 'Giá đóng cửa',
594
- 'RSI': 'RSI',
595
- 'MACD': 'MACD',
596
- 'Signal': 'Signal'
597
- }
598
- )
 
 
 
 
 
 
599
  st.dataframe(sell_signals_display, use_container_width=True)
600
  else:
601
  st.info("Không có tín hiệu BÁN trong khoảng thời gian này.")
@@ -948,13 +954,15 @@ def display_rl_analysis(history, stock_symbol):
948
 
949
  # Hiển thị danh sách tín hiệu gần đây
950
  st.subheader("Tín hiệu gần đây từ mô hình RL")
951
- recent_signals = history[['time', 'close', 'RL_Signal', 'RL_Action']].tail(10)
952
  recent_signals = recent_signals.rename(columns={
953
  'time': 'Thời gian',
954
  'close': 'Giá đóng cửa',
955
  'RL_Signal': 'Tín hiệu',
956
  'RL_Action': 'Hành động'
957
  })
 
 
958
  st.dataframe(recent_signals.style.format({
959
  'Giá đóng cửa': '{:,.0f}'
960
  }))
 
499
  'RSI': buy_signals['RSI'].round(2),
500
  'MACD': buy_signals['MACD'].round(2)
501
  })
502
+ timeline_df = pd.concat([timeline_df, buy_df], ignore_index=True)
503
 
504
  # Thêm tín hiệu bán
505
  if not sell_signals.empty:
 
510
  'RSI': sell_signals['RSI'].round(2),
511
  'MACD': sell_signals['MACD'].round(2)
512
  })
513
+ timeline_df = pd.concat([timeline_df, sell_df], ignore_index=True)
514
 
515
  # Sắp xếp theo ngày
516
  if not timeline_df.empty:
 
587
 
588
  with signals_tab2:
589
  if not sell_signals.empty:
590
+ sell_signals_display = sell_signals[['time', 'close', 'RSI', 'MACD', 'Signal']].copy()
591
+ # Chuyển đổi dữ liệu số sang float
592
+ numeric_cols = ['close', 'RSI', 'MACD', 'Signal']
593
+ sell_signals_display[numeric_cols] = sell_signals_display[numeric_cols].apply(pd.to_numeric, errors='coerce')
594
+
595
+ sell_signals_display = sell_signals_display.rename(columns={
596
+ 'time': 'Ngày',
597
+ 'close': 'Giá đóng cửa',
598
+ 'RSI': 'RSI',
599
+ 'MACD': 'MACD',
600
+ 'Signal': 'Signal'
601
+ })
602
+
603
+ # Format số trước khi hiển thị
604
+ sell_signals_display['Giá đóng cửa'] = sell_signals_display['Giá đóng cửa'].map('{:,.0f}'.format)
605
  st.dataframe(sell_signals_display, use_container_width=True)
606
  else:
607
  st.info("Không có tín hiệu BÁN trong khoảng thời gian này.")
 
954
 
955
  # Hiển thị danh sách tín hiệu gần đây
956
  st.subheader("Tín hiệu gần đây từ mô hình RL")
957
+ recent_signals = history[['time', 'close', 'RL_Signal', 'RL_Action']].tail(10).copy()
958
  recent_signals = recent_signals.rename(columns={
959
  'time': 'Thời gian',
960
  'close': 'Giá đóng cửa',
961
  'RL_Signal': 'Tín hiệu',
962
  'RL_Action': 'Hành động'
963
  })
964
+ # Đảm bảo kiểu dữ liệu trước khi định dạng
965
+ recent_signals['Giá đóng cửa'] = pd.to_numeric(recent_signals['Giá đóng cửa'], errors='coerce')
966
  st.dataframe(recent_signals.style.format({
967
  'Giá đóng cửa': '{:,.0f}'
968
  }))
utils.py CHANGED
@@ -12,17 +12,32 @@ def calculate_rsi(data, period=14):
12
  Returns:
13
  Series: Series chứa giá trị RSI
14
  """
15
- delta = data['close'].diff()
16
- gain = delta.where(delta > 0, 0)
17
- loss = -delta.where(delta < 0, 0)
18
-
19
- avg_gain = gain.rolling(window=period).mean()
20
- avg_loss = loss.rolling(window=period).mean()
21
-
22
- rs = avg_gain / avg_loss
23
- rsi = 100 - (100 / (1 + rs))
24
-
25
- return rsi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
 
28
  def calculate_macd(data, fast_period=12, slow_period=26, signal_period=9):
@@ -38,12 +53,25 @@ def calculate_macd(data, fast_period=12, slow_period=26, signal_period=9):
38
  Returns:
39
  tuple: (MACD, Signal)
40
  """
41
- exp1 = data['close'].ewm(span=fast_period, adjust=False).mean()
42
- exp2 = data['close'].ewm(span=slow_period, adjust=False).mean()
43
- macd = exp1 - exp2
44
- signal = macd.ewm(span=signal_period, adjust=False).mean()
45
-
46
- return macd, signal
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
 
49
  def calculate_ema(data, period):
@@ -57,7 +85,13 @@ def calculate_ema(data, period):
57
  Returns:
58
  Series: Series chứa giá trị EMA
59
  """
60
- return data['close'].ewm(span=period, adjust=False).mean()
 
 
 
 
 
 
61
 
62
 
63
  def calculate_ema9(data):
@@ -85,12 +119,27 @@ def calculate_bollinger_bands(data, period=20, std_dev=2):
85
  Returns:
86
  tuple: (Upper Band, Middle Band, Lower Band)
87
  """
88
- middle_band = data['close'].rolling(window=period).mean()
89
- std = data['close'].rolling(window=period).std()
90
- upper_band = middle_band + (std_dev * std)
91
- lower_band = middle_band - (std_dev * std)
92
-
93
- return upper_band, middle_band, lower_band
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
 
96
  def calculate_vwap(data):
@@ -103,9 +152,22 @@ def calculate_vwap(data):
103
  Returns:
104
  Series: Series chứa giá trị VWAP
105
  """
106
- typical_price = (data['high'] + data['low'] + data['close']) / 3
107
- vwap = (typical_price * data['volume']).cumsum() / data['volume'].cumsum()
108
- return vwap
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
 
111
  def generate_rsi_macd_signals(data):
@@ -118,38 +180,61 @@ def generate_rsi_macd_signals(data):
118
  Returns:
119
  DataFrame: DataFrame chứa tín hiệu mua/bán
120
  """
121
- signals = pd.DataFrame(index=data.index)
122
-
123
- # RSI Signals
124
- signals['RSI_Buy'] = ((data['RSI'] < 30) & (data['RSI'].shift(1) >= 30))
125
- signals['RSI_Sell'] = ((data['RSI'] > 70) & (data['RSI'].shift(1) <= 70))
126
-
127
- # MACD Signals
128
- signals['MACD_Buy'] = ((data['MACD'] > data['Signal']) &
129
- (data['MACD'].shift(1) <= data['Signal'].shift(1)))
130
- signals['MACD_Sell'] = ((data['MACD'] < data['Signal']) &
131
- (data['MACD'].shift(1) >= data['Signal'].shift(1)))
132
-
133
- # EMA Signals
134
- signals['EMA_Buy'] = ((data['close'] > data['EMA50']) &
135
- (data['close'].shift(1) <= data['EMA50'].shift(1)))
136
- signals['EMA_Sell'] = ((data['close'] < data['EMA50']) &
137
- (data['close'].shift(1) >= data['EMA50'].shift(1)))
138
-
139
- # Bollinger Bands Signals
140
- signals['BB_Buy'] = (data['close'] < data['BB_Lower'])
141
- signals['BB_Sell'] = (data['close'] > data['BB_Upper'])
142
-
143
- # Combined Signals - more conservative approach (require multiple indicators)
144
- signals['Strong_Buy'] = ((signals['RSI_Buy'] & signals['MACD_Buy']) |
145
- (signals['MACD_Buy'] & signals['EMA_Buy']) |
146
- (signals['RSI_Buy'] & signals['BB_Buy']))
147
-
148
- signals['Strong_Sell'] = ((signals['RSI_Sell'] & signals['MACD_Sell']) |
149
- (signals['MACD_Sell'] & signals['EMA_Sell']) |
150
- (signals['RSI_Sell'] & signals['BB_Sell']))
151
-
152
- return signals
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
 
155
  def calculate_performance(data):
@@ -173,7 +258,7 @@ def calculate_performance(data):
173
  data.loc[data['Strong_Sell'], 'Position'] = 0
174
 
175
  # Forward fill: giữ vị thế cho đến khi có tín hiệu ngược lại
176
- data['Position'] = data['Position'].replace(to_replace=0, method='ffill')
177
 
178
  # Tính toán lợi nhuận theo ngày
179
  data['Returns'] = data['close'].pct_change()
 
12
  Returns:
13
  Series: Series chứa giá trị RSI
14
  """
15
+ try:
16
+ # Chuyển đổi dữ liệu sang float
17
+ close_prices = pd.to_numeric(data['close'], errors='coerce')
18
+
19
+ delta = close_prices.diff()
20
+ gain = delta.where(delta > 0, 0)
21
+ loss = -delta.where(delta < 0, 0)
22
+
23
+ avg_gain = gain.rolling(window=period).mean()
24
+ avg_loss = loss.rolling(window=period).mean()
25
+
26
+ # Tránh chia cho 0
27
+ # Thay thế giá trị 0 trong avg_loss bằng np.inf
28
+ avg_loss = np.where(avg_loss == 0, np.inf, avg_loss)
29
+ rs = avg_gain / avg_loss
30
+ rsi = 100 - (100 / (1 + rs))
31
+
32
+ # Xử lý các giá trị không hợp lệ
33
+ rsi = rsi.fillna(50) # Giá trị trung tính cho RSI
34
+ rsi = rsi.clip(0, 100) # Giới hạn giá trị trong khoảng [0, 100]
35
+
36
+ return rsi.astype(np.float32)
37
+
38
+ except Exception as e:
39
+ print(f"Lỗi khi tính RSI: {str(e)}")
40
+ return pd.Series(index=data.index, dtype=np.float32)
41
 
42
 
43
  def calculate_macd(data, fast_period=12, slow_period=26, signal_period=9):
 
53
  Returns:
54
  tuple: (MACD, Signal)
55
  """
56
+ try:
57
+ # Chuyển đổi dữ liệu sang float
58
+ close_prices = pd.to_numeric(data['close'], errors='coerce')
59
+
60
+ exp1 = close_prices.ewm(span=fast_period, adjust=False).mean()
61
+ exp2 = close_prices.ewm(span=slow_period, adjust=False).mean()
62
+ macd = exp1 - exp2
63
+ signal = macd.ewm(span=signal_period, adjust=False).mean()
64
+
65
+ # Xử lý các giá trị không hợp lệ
66
+ macd = macd.ffill().fillna(0)
67
+ signal = signal.ffill().fillna(0)
68
+
69
+ return macd.astype(np.float32), signal.astype(np.float32)
70
+
71
+ except Exception as e:
72
+ print(f"Lỗi khi tính MACD: {str(e)}")
73
+ empty_series = pd.Series(index=data.index, dtype=np.float32)
74
+ return empty_series, empty_series
75
 
76
 
77
  def calculate_ema(data, period):
 
85
  Returns:
86
  Series: Series chứa giá trị EMA
87
  """
88
+ try:
89
+ close_prices = pd.to_numeric(data['close'], errors='coerce')
90
+ ema = close_prices.ewm(span=period, adjust=False).mean()
91
+ return ema.fillna(method='ffill').fillna(0).astype(np.float32)
92
+ except Exception as e:
93
+ print(f"Lỗi khi tính EMA: {str(e)}")
94
+ return pd.Series(index=data.index, dtype=np.float32)
95
 
96
 
97
  def calculate_ema9(data):
 
119
  Returns:
120
  tuple: (Upper Band, Middle Band, Lower Band)
121
  """
122
+ try:
123
+ # Chuyển đổi dữ liệu sang float
124
+ close_prices = pd.to_numeric(data['close'], errors='coerce')
125
+
126
+ middle_band = close_prices.rolling(window=period).mean()
127
+ std = close_prices.rolling(window=period).std()
128
+ upper_band = middle_band + (std_dev * std)
129
+ lower_band = middle_band - (std_dev * std)
130
+
131
+ # Xử lý các giá trị không hợp lệ
132
+ middle_band = middle_band.ffill().fillna(0)
133
+ upper_band = upper_band.ffill().fillna(0)
134
+ lower_band = lower_band.ffill().fillna(0)
135
+
136
+ return (upper_band.astype(np.float32),
137
+ middle_band.astype(np.float32),
138
+ lower_band.astype(np.float32))
139
+ except Exception as e:
140
+ print(f"Lỗi khi tính Bollinger Bands: {str(e)}")
141
+ empty_series = pd.Series(index=data.index, dtype=np.float32)
142
+ return empty_series, empty_series, empty_series
143
 
144
 
145
  def calculate_vwap(data):
 
152
  Returns:
153
  Series: Series chứa giá trị VWAP
154
  """
155
+ try:
156
+ # Chuyển đổi dữ liệu sang float
157
+ high = pd.to_numeric(data['high'], errors='coerce')
158
+ low = pd.to_numeric(data['low'], errors='coerce')
159
+ close = pd.to_numeric(data['close'], errors='coerce')
160
+ volume = pd.to_numeric(data['volume'], errors='coerce')
161
+
162
+ typical_price = (high + low + close) / 3
163
+ vwap = (typical_price * volume).cumsum() / volume.cumsum()
164
+
165
+ # Xử lý các giá trị không hợp lệ
166
+ vwap = vwap.ffill().bfill()
167
+ return vwap.astype(np.float32)
168
+ except Exception as e:
169
+ print(f"Lỗi khi tính VWAP: {str(e)}")
170
+ return pd.Series(index=data.index, dtype=np.float32)
171
 
172
 
173
  def generate_rsi_macd_signals(data):
 
180
  Returns:
181
  DataFrame: DataFrame chứa tín hiệu mua/bán
182
  """
183
+ try:
184
+ # Kiểm tra các cột cần thiết
185
+ required_columns = ['RSI', 'MACD', 'Signal', 'close', 'EMA50', 'BB_Lower', 'BB_Upper']
186
+ if not all(col in data.columns for col in required_columns):
187
+ raise ValueError("Thiếu một số cột dữ liệu cần thiết cho việc tạo tín hiệu")
188
+
189
+ # Chuyển đổi dữ liệu sang float
190
+ signals_data = data[required_columns].apply(pd.to_numeric, errors='coerce')
191
+
192
+ signals = pd.DataFrame(index=data.index)
193
+
194
+ # RSI Signals với kiểm tra giá trị hợp lệ
195
+ signals['RSI_Buy'] = ((signals_data['RSI'] < 30) & (signals_data['RSI'].shift(1) >= 30)).fillna(False)
196
+ signals['RSI_Sell'] = ((signals_data['RSI'] > 70) & (signals_data['RSI'].shift(1) <= 70)).fillna(False)
197
+
198
+ # MACD Signals với kiểm tra giá trị hợp lệ
199
+ signals['MACD_Buy'] = ((signals_data['MACD'] > signals_data['Signal']) &
200
+ (signals_data['MACD'].shift(1) <= signals_data['Signal'].shift(1))).fillna(False)
201
+ signals['MACD_Sell'] = ((signals_data['MACD'] < signals_data['Signal']) &
202
+ (signals_data['MACD'].shift(1) >= signals_data['Signal'].shift(1))).fillna(False)
203
+
204
+ # EMA Signals với kiểm tra giá trị hợp lệ
205
+ signals['EMA_Buy'] = ((signals_data['close'] > signals_data['EMA50']) &
206
+ (signals_data['close'].shift(1) <= signals_data['EMA50'].shift(1))).fillna(False)
207
+ signals['EMA_Sell'] = ((signals_data['close'] < signals_data['EMA50']) &
208
+ (signals_data['close'].shift(1) >= signals_data['EMA50'].shift(1))).fillna(False)
209
+
210
+ # Bollinger Bands Signals với kiểm tra giá trị hợp lệ
211
+ signals['BB_Buy'] = (signals_data['close'] < signals_data['BB_Lower']).fillna(False)
212
+ signals['BB_Sell'] = (signals_data['close'] > signals_data['BB_Upper']).fillna(False)
213
+
214
+ # Combined Signals với kiểm tra giá trị hợp lệ
215
+ signals['Strong_Buy'] = ((signals['RSI_Buy'] & signals['MACD_Buy']) |
216
+ (signals['MACD_Buy'] & signals['EMA_Buy']) |
217
+ (signals['RSI_Buy'] & signals['BB_Buy']))
218
+
219
+ signals['Strong_Sell'] = ((signals['RSI_Sell'] & signals['MACD_Sell']) |
220
+ (signals['MACD_Sell'] & signals['EMA_Sell']) |
221
+ (signals['RSI_Sell'] & signals['BB_Sell']))
222
+
223
+ # Đảm bảo không có tín hiệu mua và bán đồng thời
224
+ signals.loc[signals['Strong_Buy'] & signals['Strong_Sell'], 'Strong_Buy'] = False
225
+
226
+ return signals
227
+
228
+ except Exception as e:
229
+ print(f"Lỗi khi tạo tín hiệu giao dịch: {str(e)}")
230
+ # Trả về DataFrame trống với các cột tín hiệu
231
+ empty_signals = pd.DataFrame(index=data.index)
232
+ signal_columns = ['RSI_Buy', 'RSI_Sell', 'MACD_Buy', 'MACD_Sell',
233
+ 'EMA_Buy', 'EMA_Sell', 'BB_Buy', 'BB_Sell',
234
+ 'Strong_Buy', 'Strong_Sell']
235
+ for col in signal_columns:
236
+ empty_signals[col] = False
237
+ return empty_signals
238
 
239
 
240
  def calculate_performance(data):
 
258
  data.loc[data['Strong_Sell'], 'Position'] = 0
259
 
260
  # Forward fill: giữ vị thế cho đến khi có tín hiệu ngược lại
261
+ data['Position'] = data['Position'].ffill().fillna(0)
262
 
263
  # Tính toán lợi nhuận theo ngày
264
  data['Returns'] = data['close'].pct_change()