Spaces:
Sleeping
Sleeping
Dung Pham Anh commited on
Commit ·
b2fcf54
1
Parent(s): 0647700
update finr
Browse files- app.py +21 -8
- finrl_agent.py +32 -11
- requirements.txt +1 -1
- ui.py +20 -12
- 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 |
-
#
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
#
|
| 51 |
-
|
| 52 |
|
| 53 |
-
#
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 tra và xử lý missing values
|
| 54 |
+
history_for_rl = history_for_rl.fillna(method='ffill')
|
| 55 |
|
| 56 |
+
# Khởi tạo và huấn luyện mô 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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==
|
| 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']].
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 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 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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'].
|
| 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()
|