Bài viết gần đây
-
-
So sánh Flutter và React Native
Tháng mười một 17, 2025 -
So sánh Flutter và React Native
Tháng mười một 17, 2025 -
Chiến lược RSI 30–70 trong Bot Auto Trading Python
Tháng mười một 17, 2025 -
Chiến Lược Giao Dịch News Filter sử dụng API Python
Tháng mười một 17, 2025
| Chiến Lược Giao Dịch News Filter sử dụng API Python
Được viết bởi thanhdt vào ngày 17/11/2025 lúc 17:30 | 5 lượt xem
Chiến Lược Giao Dịch News Filter sử dụng API Python
News Trading là một phương pháp giao dịch dựa trên việc phân tích tin tức và tác động của chúng lên giá cả thị trường. Bằng cách sử dụng các API tin tức và phân tích sentiment, chúng ta có thể tự động hóa việc phát hiện các sự kiện quan trọng và đưa ra quyết định giao dịch. Trong bài viết này, chúng ta sẽ xây dựng một bot giao dịch tự động sử dụng chiến lược News Filter với Python.
Tổng quan về News Trading
News Trading là gì?
News Trading là phương pháp giao dịch dựa trên việc phân tích tin tức và phản ứng của thị trường trước các sự kiện. Nguyên tắc cơ bản:
- Tin tức quan trọng thường tác động mạnh đến giá
- Phản ứng của thị trường có thể dự đoán được dựa trên sentiment
- Tốc độ xử lý tin tức là yếu tố quan trọng
- Kết hợp với phân tích kỹ thuật để xác nhận tín hiệu
Tại sao News Trading hiệu quả?
- Tác động trực tiếp: Tin tức quan trọng có thể làm giá biến động mạnh
- Cơ hội ngắn hạn: Phản ứng của thị trường thường xảy ra nhanh
- Có thể tự động hóa: API tin tức cho phép tự động hóa hoàn toàn
- Sentiment analysis: Phân tích tâm lý từ tin tức có thể dự đoán hướng giá
- Alternative data: Tin tức là nguồn dữ liệu thay thế quan trọng
Các loại tin tức quan trọng
Tin tức kinh tế:
- Lãi suất, chính sách tiền tệ
- Báo cáo GDP, việc làm
- Chỉ số lạm phát
- Quyết định của ngân hàng trung ương
Tin tức công ty (cho cổ phiếu):
- Báo cáo thu nhập
- Thông báo sáp nhập/mua lại
- Thay đổi lãnh đạo
- Sản phẩm mới
Tin tức crypto:
- Quy định pháp lý
- Niêm yết trên sàn lớn
- Hard fork, upgrade
- Partnership quan trọng
Cài đặt Môi trường
Thư viện cần thiết
# requirements.txt
pandas==2.1.0
numpy==1.24.3
ccxt==4.0.0
python-binance==1.0.19
matplotlib==3.7.2
plotly==5.17.0
schedule==1.2.0
python-dotenv==1.0.0
requests==2.31.0
beautifulsoup4==4.12.0
textblob==0.17.1
vaderSentiment==3.3.2
newsapi-python==0.2.7
feedparser==6.0.10
Cài đặt
pip install pandas numpy ccxt python-binance matplotlib plotly schedule python-dotenv requests beautifulsoup4 textblob vaderSentiment newsapi-python feedparser
Lưu ý:
- NewsAPI yêu cầu API key miễn phí từ newsapi.org
- Alpha Vantage yêu cầu API key từ alphavantage.co
Xây dựng News Collector
Lớp News API Collector
import requests
import pandas as pd
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import time
import os
from dotenv import load_dotenv
load_dotenv()
class NewsAPICollector:
"""
Lớp thu thập tin tức từ NewsAPI
"""
def __init__(self, api_key: Optional[str] = None):
"""
Khởi tạo News API Collector
Args:
api_key: API key từ NewsAPI (hoặc lấy từ environment)
"""
self.api_key = api_key or os.getenv('NEWSAPI_KEY')
self.base_url = 'https://newsapi.org/v2'
if not self.api_key:
raise ValueError("NewsAPI key is required. Get one from https://newsapi.org/")
def get_news(
self,
query: str,
language: str = 'en',
sort_by: str = 'publishedAt',
page_size: int = 100,
from_date: Optional[str] = None,
to_date: Optional[str] = None
) -> List[Dict]:
"""
Lấy tin tức từ NewsAPI
Args:
query: Từ khóa tìm kiếm (ví dụ: 'Bitcoin', 'cryptocurrency')
language: Ngôn ngữ (mặc định: 'en')
sort_by: Sắp xếp theo ('relevancy', 'popularity', 'publishedAt')
page_size: Số lượng bài viết (tối đa 100)
from_date: Ngày bắt đầu (YYYY-MM-DD)
to_date: Ngày kết thúc (YYYY-MM-DD)
Returns:
List các bài viết tin tức
"""
url = f"{self.base_url}/everything"
params = {
'q': query,
'apiKey': self.api_key,
'language': language,
'sortBy': sort_by,
'pageSize': page_size
}
if from_date:
params['from'] = from_date
if to_date:
params['to'] = to_date
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if data['status'] == 'ok':
return data.get('articles', [])
else:
print(f"Error: {data.get('message', 'Unknown error')}")
return []
except requests.exceptions.RequestException as e:
print(f"Error fetching news: {e}")
return []
def get_top_headlines(
self,
category: Optional[str] = None,
country: str = 'us',
page_size: int = 100
) -> List[Dict]:
"""
Lấy tin tức hàng đầu
Args:
category: Danh mục ('business', 'technology', 'general', etc.)
country: Mã quốc gia (mặc định: 'us')
page_size: Số lượng bài viết
Returns:
List các bài viết tin tức
"""
url = f"{self.base_url}/top-headlines"
params = {
'apiKey': self.api_key,
'country': country,
'pageSize': page_size
}
if category:
params['category'] = category
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if data['status'] == 'ok':
return data.get('articles', [])
else:
print(f"Error: {data.get('message', 'Unknown error')}")
return []
except requests.exceptions.RequestException as e:
print(f"Error fetching headlines: {e}")
return []
def get_crypto_news(self, symbol: str = 'Bitcoin') -> List[Dict]:
"""
Lấy tin tức về cryptocurrency
Args:
symbol: Tên cryptocurrency (ví dụ: 'Bitcoin', 'Ethereum')
Returns:
List các bài viết về crypto
"""
queries = [
symbol,
f"{symbol} cryptocurrency",
f"{symbol} price",
"cryptocurrency",
"crypto market"
]
all_articles = []
for query in queries:
articles = self.get_news(query, page_size=20)
all_articles.extend(articles)
time.sleep(1) # Tránh rate limit
# Loại bỏ trùng lặp
seen_titles = set()
unique_articles = []
for article in all_articles:
title = article.get('title', '')
if title and title not in seen_titles:
seen_titles.add(title)
unique_articles.append(article)
return unique_articles
Lớp Sentiment Analyzer
from textblob import TextBlob
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import re
class SentimentAnalyzer:
"""
Lớp phân tích sentiment từ tin tức
"""
def __init__(self):
"""
Khởi tạo Sentiment Analyzer
"""
self.vader = SentimentIntensityAnalyzer()
def clean_text(self, text: str) -> str:
"""
Làm sạch text
Args:
text: Text cần làm sạch
Returns:
Text đã làm sạch
"""
if not text:
return ""
# Loại bỏ HTML tags
text = re.sub(r'<[^>]+>', '', text)
# Loại bỏ URLs
text = re.sub(r'http\S+|www.\S+', '', text)
# Loại bỏ ký tự đặc biệt
text = re.sub(r'[^\w\s]', '', text)
return text.strip()
def analyze_textblob(self, text: str) -> Dict:
"""
Phân tích sentiment bằng TextBlob
Args:
text: Text cần phân tích
Returns:
Dictionary chứa sentiment scores
"""
text = self.clean_text(text)
if not text:
return {'polarity': 0.0, 'subjectivity': 0.0}
blob = TextBlob(text)
return {
'polarity': blob.sentiment.polarity, # -1 (negative) to 1 (positive)
'subjectivity': blob.sentiment.subjectivity # 0 (objective) to 1 (subjective)
}
def analyze_vader(self, text: str) -> Dict:
"""
Phân tích sentiment bằng VADER
Args:
text: Text cần phân tích
Returns:
Dictionary chứa sentiment scores
"""
text = self.clean_text(text)
if not text:
return {'compound': 0.0, 'pos': 0.0, 'neu': 0.0, 'neg': 0.0}
scores = self.vader.polarity_scores(text)
return {
'compound': scores['compound'], # -1 (negative) to 1 (positive)
'pos': scores['pos'],
'neu': scores['neu'],
'neg': scores['neg']
}
def analyze_combined(self, text: str) -> Dict:
"""
Phân tích sentiment kết hợp TextBlob và VADER
Args:
text: Text cần phân tích
Returns:
Dictionary chứa sentiment scores tổng hợp
"""
textblob_result = self.analyze_textblob(text)
vader_result = self.analyze_vader(text)
# Kết hợp scores
combined_polarity = (textblob_result['polarity'] + vader_result['compound']) / 2
# Xác định sentiment
if combined_polarity > 0.1:
sentiment = 'positive'
elif combined_polarity < -0.1:
sentiment = 'negative'
else:
sentiment = 'neutral'
return {
'sentiment': sentiment,
'polarity': combined_polarity,
'textblob_polarity': textblob_result['polarity'],
'vader_compound': vader_result['compound'],
'confidence': abs(combined_polarity)
}
def analyze_article(self, article: Dict) -> Dict:
"""
Phân tích sentiment của một bài viết
Args:
article: Dictionary chứa thông tin bài viết
Returns:
Dictionary chứa sentiment analysis
"""
# Kết hợp title và description
title = article.get('title', '')
description = article.get('description', '')
content = article.get('content', '')
full_text = f"{title} {description} {content}"
sentiment_result = self.analyze_combined(full_text)
# Thêm thông tin bài viết
sentiment_result.update({
'title': title,
'source': article.get('source', {}).get('name', ''),
'published_at': article.get('publishedAt', ''),
'url': article.get('url', '')
})
return sentiment_result
Lớp News Filter
class NewsFilter:
"""
Lớp lọc và đánh giá tin tức
"""
def __init__(
self,
min_sentiment_score: float = 0.3,
min_confidence: float = 0.2,
keywords_positive: List[str] = None,
keywords_negative: List[str] = None
):
"""
Khởi tạo News Filter
Args:
min_sentiment_score: Điểm sentiment tối thiểu để xem xét
min_confidence: Độ tin cậy tối thiểu
keywords_positive: Từ khóa tích cực
keywords_negative: Từ khóa tiêu cực
"""
self.min_sentiment_score = min_sentiment_score
self.min_confidence = min_confidence
self.keywords_positive = keywords_positive or [
'bullish', 'surge', 'rally', 'gain', 'rise', 'up', 'positive',
'growth', 'profit', 'success', 'adoption', 'partnership'
]
self.keywords_negative = keywords_negative or [
'bearish', 'crash', 'drop', 'fall', 'decline', 'down', 'negative',
'loss', 'risk', 'regulation', 'ban', 'hack', 'scam'
]
self.sentiment_analyzer = SentimentAnalyzer()
def check_keywords(self, text: str) -> Dict:
"""
Kiểm tra từ khóa trong text
Args:
text: Text cần kiểm tra
Returns:
Dictionary chứa keyword scores
"""
text_lower = text.lower()
positive_count = sum(1 for keyword in self.keywords_positive if keyword in text_lower)
negative_count = sum(1 for keyword in self.keywords_negative if keyword in text_lower)
total_keywords = positive_count + negative_count
if total_keywords == 0:
return {'positive_score': 0, 'negative_score': 0, 'keyword_sentiment': 'neutral'}
positive_score = positive_count / total_keywords
negative_score = negative_count / total_keywords
if positive_score > negative_score:
keyword_sentiment = 'positive'
elif negative_score > positive_score:
keyword_sentiment = 'negative'
else:
keyword_sentiment = 'neutral'
return {
'positive_score': positive_score,
'negative_score': negative_score,
'keyword_sentiment': keyword_sentiment
}
def filter_news(self, articles: List[Dict]) -> List[Dict]:
"""
Lọc và đánh giá tin tức
Args:
articles: List các bài viết
Returns:
List các bài viết đã được đánh giá và lọc
"""
filtered_articles = []
for article in articles:
# Phân tích sentiment
sentiment_result = self.sentiment_analyzer.analyze_article(article)
# Kiểm tra keywords
full_text = f"{article.get('title', '')} {article.get('description', '')}"
keyword_result = self.check_keywords(full_text)
# Tính điểm tổng hợp
sentiment_score = sentiment_result['polarity']
keyword_score = (keyword_result['positive_score'] - keyword_result['negative_score'])
combined_score = (sentiment_score * 0.7) + (keyword_score * 0.3)
# Kiểm tra điều kiện
if abs(combined_score) >= self.min_sentiment_score and sentiment_result['confidence'] >= self.min_confidence:
article['sentiment_analysis'] = sentiment_result
article['keyword_analysis'] = keyword_result
article['combined_score'] = combined_score
article['trading_signal'] = 'buy' if combined_score > 0 else 'sell'
filtered_articles.append(article)
# Sắp xếp theo điểm số
filtered_articles.sort(key=lambda x: abs(x['combined_score']), reverse=True)
return filtered_articles
Chiến lược News Trading
Nguyên lý Chiến lược
- Thu thập tin tức: Lấy tin tức mới nhất về tài sản
- Phân tích sentiment: Phân tích tâm lý từ tin tức
- Lọc tin tức quan trọng: Chỉ giao dịch khi có tin tức có tác động mạnh
- Xác nhận với giá: Kết hợp với phân tích kỹ thuật
- Vào lệnh nhanh: Phản ứng nhanh với tin tức quan trọng
Lớp Chiến lược News Trading
class NewsTradingStrategy:
"""
Chiến lược giao dịch dựa trên tin tức
"""
def __init__(
self,
min_sentiment_score: float = 0.4,
require_price_confirmation: bool = True,
price_change_threshold: float = 0.02
):
"""
Khởi tạo chiến lược
Args:
min_sentiment_score: Điểm sentiment tối thiểu
require_price_confirmation: Yêu cầu xác nhận từ giá
price_change_threshold: Ngưỡng thay đổi giá để xác nhận (%)
"""
self.min_sentiment_score = min_sentiment_score
self.require_price_confirmation = require_price_confirmation
self.price_change_threshold = price_change_threshold
self.news_collector = NewsAPICollector()
self.news_filter = NewsFilter(min_sentiment_score=min_sentiment_score)
def get_news_signal(self, symbol: str, df: pd.DataFrame = None) -> Dict:
"""
Lấy tín hiệu từ tin tức
Args:
symbol: Tên tài sản (ví dụ: 'Bitcoin', 'BTC')
df: DataFrame giá (để xác nhận)
Returns:
Dictionary chứa tín hiệu giao dịch
"""
# Lấy tin tức
articles = self.news_collector.get_crypto_news(symbol)
if not articles:
return {'signal': 0, 'confidence': 0.0, 'articles': []}
# Lọc tin tức
filtered_articles = self.news_filter.filter_news(articles)
if not filtered_articles:
return {'signal': 0, 'confidence': 0.0, 'articles': []}
# Lấy bài viết có điểm cao nhất
top_article = filtered_articles[0]
combined_score = top_article['combined_score']
# Xác định tín hiệu
signal = 0
if combined_score > self.min_sentiment_score:
signal = 1 # BUY
elif combined_score < -self.min_sentiment_score:
signal = -1 # SELL
# Xác nhận với giá nếu yêu cầu
if self.require_price_confirmation and df is not None and len(df) > 0:
current_price = df['close'].iloc[-1]
prev_price = df['close'].iloc[-2] if len(df) > 1 else current_price
price_change = (current_price - prev_price) / prev_price
# Kiểm tra xem giá có di chuyển theo hướng sentiment không
if signal == 1 and price_change < self.price_change_threshold:
# Sentiment tích cực nhưng giá chưa phản ứng
signal = 0
elif signal == -1 and price_change > -self.price_change_threshold:
# Sentiment tiêu cực nhưng giá chưa phản ứng
signal = 0
confidence = abs(combined_score)
return {
'signal': signal,
'confidence': confidence,
'articles': filtered_articles[:5], # Top 5 articles
'top_article': top_article,
'sentiment_score': combined_score
}
Xây dựng Trading Bot
Lớp Bot Chính
import ccxt
import time
import logging
from typing import Dict, Optional
from datetime import datetime
import os
from dotenv import load_dotenv
load_dotenv()
class NewsTradingBot:
"""
Bot giao dịch dựa trên tin tức
"""
def __init__(
self,
exchange_id: str = 'binance',
api_key: Optional[str] = None,
api_secret: Optional[str] = None,
symbol: str = 'BTC/USDT',
timeframe: str = '1h',
testnet: bool = True
):
"""
Khởi tạo bot
"""
self.exchange_id = exchange_id
self.symbol = symbol
self.timeframe = timeframe
self.testnet = testnet
self.api_key = api_key or os.getenv('EXCHANGE_API_KEY')
self.api_secret = api_secret or os.getenv('EXCHANGE_API_SECRET')
self.exchange = self._initialize_exchange()
self.strategy = NewsTradingStrategy(
min_sentiment_score=0.4,
require_price_confirmation=True,
price_change_threshold=0.02
)
self.position = None
self.orders = []
self.min_order_size = 0.001
self.risk_per_trade = 0.02
self._setup_logging()
def _initialize_exchange(self) -> ccxt.Exchange:
"""Khởi tạo kết nối với sàn"""
exchange_class = getattr(ccxt, self.exchange_id)
config = {
'apiKey': self.api_key,
'secret': self.api_secret,
'enableRateLimit': True,
'options': {'defaultType': 'spot'}
}
if self.testnet and self.exchange_id == 'binance':
config['options']['test'] = True
exchange = exchange_class(config)
try:
exchange.load_markets()
self.logger.info(f"Đã kết nối với {self.exchange_id}")
except Exception as e:
self.logger.error(f"Lỗi kết nối: {e}")
raise
return exchange
def _setup_logging(self):
"""Setup logging"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('news_trading_bot.log'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger('NewsTradingBot')
def fetch_ohlcv(self, limit: int = 100) -> pd.DataFrame:
"""Lấy dữ liệu OHLCV"""
try:
ohlcv = self.exchange.fetch_ohlcv(
self.symbol,
self.timeframe,
limit=limit
)
df = pd.DataFrame(
ohlcv,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']
)
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
return df
except Exception as e:
self.logger.error(f"Lỗi lấy dữ liệu: {e}")
return pd.DataFrame()
def calculate_position_size(self, entry_price: float, stop_loss_price: float) -> float:
"""Tính toán khối lượng lệnh"""
try:
balance = self.get_balance()
available_balance = balance.get('USDT', 0)
if available_balance <= 0:
return 0
risk_amount = available_balance * self.risk_per_trade
stop_loss_distance = abs(entry_price - stop_loss_price)
if stop_loss_distance == 0:
return 0
position_size = risk_amount / stop_loss_distance
market = self.exchange.market(self.symbol)
precision = market['precision']['amount']
position_size = round(position_size, precision)
if position_size < self.min_order_size:
return 0
return position_size
except Exception as e:
self.logger.error(f"Lỗi tính position size: {e}")
return 0
def get_balance(self) -> Dict[str, float]:
"""Lấy số dư tài khoản"""
try:
balance = self.exchange.fetch_balance()
return {
'USDT': balance.get('USDT', {}).get('free', 0),
'BTC': balance.get('BTC', {}).get('free', 0),
'total': balance.get('total', {})
}
except Exception as e:
self.logger.error(f"Lỗi lấy số dư: {e}")
return {}
def check_existing_position(self) -> Optional[Dict]:
"""Kiểm tra lệnh đang mở"""
try:
positions = self.exchange.fetch_positions([self.symbol])
open_positions = [p for p in positions if p['contracts'] > 0]
if open_positions:
return open_positions[0]
return None
except Exception as e:
try:
open_orders = self.exchange.fetch_open_orders(self.symbol)
if open_orders:
return {'type': 'order', 'orders': open_orders}
except:
pass
return None
def execute_buy(self, news_signal: Dict, current_price: float) -> bool:
"""Thực hiện lệnh mua"""
try:
# Stop Loss: 3% dưới entry
stop_loss = current_price * 0.97
# Take Profit: 6% trên entry (Risk/Reward 2:1)
take_profit = current_price * 1.06
position_size = self.calculate_position_size(current_price, stop_loss)
if position_size <= 0:
self.logger.warning("Position size quá nhỏ")
return False
order = self.exchange.create_market_buy_order(
self.symbol,
position_size
)
top_article = news_signal.get('top_article', {})
self.logger.info(
f"BUY NEWS: {position_size} {self.symbol} @ {current_price:.2f} | "
f"Sentiment: {news_signal['sentiment_score']:.2f} | "
f"Confidence: {news_signal['confidence']:.2f} | "
f"Article: {top_article.get('title', 'N/A')[:50]} | "
f"SL: {stop_loss:.2f} | TP: {take_profit:.2f}"
)
self.position = {
'side': 'long',
'entry_price': current_price,
'size': position_size,
'stop_loss': stop_loss,
'take_profit': take_profit,
'order_id': order['id'],
'news_article': top_article,
'timestamp': datetime.now()
}
return True
except Exception as e:
self.logger.error(f"Lỗi mua: {e}")
return False
def execute_sell(self, news_signal: Dict, current_price: float) -> bool:
"""Thực hiện lệnh bán"""
try:
# Stop Loss: 3% trên entry
stop_loss = current_price * 1.03
# Take Profit: 6% dưới entry
take_profit = current_price * 0.94
position_size = self.calculate_position_size(current_price, stop_loss)
if position_size <= 0:
self.logger.warning("Position size quá nhỏ")
return False
order = self.exchange.create_market_sell_order(
self.symbol,
position_size
)
top_article = news_signal.get('top_article', {})
self.logger.info(
f"SELL NEWS: {position_size} {self.symbol} @ {current_price:.2f} | "
f"Sentiment: {news_signal['sentiment_score']:.2f} | "
f"Confidence: {news_signal['confidence']:.2f} | "
f"Article: {top_article.get('title', 'N/A')[:50]} | "
f"SL: {stop_loss:.2f} | TP: {take_profit:.2f}"
)
self.position = {
'side': 'short',
'entry_price': current_price,
'size': position_size,
'stop_loss': stop_loss,
'take_profit': take_profit,
'order_id': order['id'],
'news_article': top_article,
'timestamp': datetime.now()
}
return True
except Exception as e:
self.logger.error(f"Lỗi bán: {e}")
return False
def check_exit_conditions(self, df: pd.DataFrame) -> bool:
"""Kiểm tra điều kiện thoát"""
if not self.position:
return False
current_price = df['close'].iloc[-1]
if self.position['side'] == 'long':
if current_price <= self.position['stop_loss']:
self.logger.info(f"Stop Loss @ {current_price:.2f}")
return True
if current_price >= self.position['take_profit']:
self.logger.info(f"Take Profit @ {current_price:.2f}")
return True
elif self.position['side'] == 'short':
if current_price >= self.position['stop_loss']:
self.logger.info(f"Stop Loss @ {current_price:.2f}")
return True
if current_price <= self.position['take_profit']:
self.logger.info(f"Take Profit @ {current_price:.2f}")
return True
return False
def close_position(self) -> bool:
"""Đóng lệnh hiện tại"""
if not self.position:
return False
try:
if self.position['side'] == 'long':
order = self.exchange.create_market_sell_order(
self.symbol,
self.position['size']
)
else:
order = self.exchange.create_market_buy_order(
self.symbol,
self.position['size']
)
current_price = self.exchange.fetch_ticker(self.symbol)['last']
if self.position['side'] == 'long':
pnl_pct = ((current_price - self.position['entry_price']) / self.position['entry_price']) * 100
else:
pnl_pct = ((self.position['entry_price'] - current_price) / self.position['entry_price']) * 100
self.logger.info(
f"Đóng lệnh {self.position['side']} | "
f"Entry: {self.position['entry_price']:.2f} | "
f"Exit: {current_price:.2f} | P&L: {pnl_pct:.2f}%"
)
self.position = None
return True
except Exception as e:
self.logger.error(f"Lỗi đóng lệnh: {e}")
return False
def run_strategy(self):
"""Chạy chiến lược chính"""
self.logger.info("Bắt đầu chạy chiến lược News Trading...")
# Lấy symbol name cho news query
symbol_name = self.symbol.split('/')[0] # BTC từ BTC/USDT
while True:
try:
# Lấy dữ liệu giá
df = self.fetch_ohlcv(limit=100)
if df.empty:
self.logger.warning("Không lấy được dữ liệu")
time.sleep(60)
continue
current_price = df['close'].iloc[-1]
# Kiểm tra lệnh hiện tại
existing_position = self.check_existing_position()
if existing_position:
if self.check_exit_conditions(df):
self.close_position()
else:
# Lấy tín hiệu từ tin tức
news_signal = self.strategy.get_news_signal(symbol_name, df)
if news_signal['signal'] != 0 and news_signal['confidence'] >= 0.4:
if news_signal['signal'] == 1:
self.execute_buy(news_signal, current_price)
elif news_signal['signal'] == -1:
self.execute_sell(news_signal, current_price)
# Kiểm tra tin tức mỗi 5 phút
time.sleep(300)
except KeyboardInterrupt:
self.logger.info("Bot đã dừng")
break
except Exception as e:
self.logger.error(f"Lỗi: {e}")
time.sleep(60)
Sử dụng Bot
Script Chạy Bot
# run_news_trading_bot.py
from news_trading_bot import NewsTradingBot
import os
from dotenv import load_dotenv
load_dotenv()
if __name__ == '__main__':
bot = NewsTradingBot(
exchange_id='binance',
symbol='BTC/USDT',
timeframe='1h',
testnet=True
)
try:
bot.run_strategy()
except KeyboardInterrupt:
print("\nBot đã dừng")
Script Test News API
# test_news_api.py
from news_trading_bot import NewsAPICollector, SentimentAnalyzer, NewsFilter
import os
from dotenv import load_dotenv
load_dotenv()
if __name__ == '__main__':
# Test News API
collector = NewsAPICollector()
articles = collector.get_crypto_news('Bitcoin')
print(f"Tìm thấy {len(articles)} bài viết")
# Phân tích sentiment
analyzer = SentimentAnalyzer()
filter_tool = NewsFilter()
filtered = filter_tool.filter_news(articles)
print(f"\nSau khi lọc: {len(filtered)} bài viết quan trọng")
for i, article in enumerate(filtered[:5], 1):
print(f"\n{i}. {article.get('title', 'N/A')}")
print(f" Sentiment: {article.get('sentiment_analysis', {}).get('sentiment', 'N/A')}")
print(f" Score: {article.get('combined_score', 0):.2f}")
print(f" Signal: {article.get('trading_signal', 'N/A')}")
Tối ưu hóa Chiến lược
1. Kết hợp với Technical Analysis
def combine_with_technical_analysis(news_signal: Dict, df: pd.DataFrame) -> Dict:
"""Kết hợp tín hiệu tin tức với phân tích kỹ thuật"""
# Tính RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
current_rsi = rsi.iloc[-1]
# Điều chỉnh signal
if news_signal['signal'] == 1: # BUY
if current_rsi > 70: # Quá mua
news_signal['signal'] = 0
news_signal['confidence'] *= 0.5
elif news_signal['signal'] == -1: # SELL
if current_rsi < 30: # Quá bán
news_signal['signal'] = 0
news_signal['confidence'] *= 0.5
return news_signal
2. Filter theo Nguồn Tin
def filter_by_source(articles: List[Dict], trusted_sources: List[str]) -> List[Dict]:
"""Lọc tin tức theo nguồn tin cậy"""
trusted_sources_lower = [s.lower() for s in trusted_sources]
filtered = []
for article in articles:
source = article.get('source', {}).get('name', '').lower()
if any(trusted in source for trusted in trusted_sources_lower):
filtered.append(article)
return filtered
3. Time-based Filtering
def filter_by_time(articles: List[Dict], max_age_hours: int = 24) -> List[Dict]:
"""Lọc tin tức theo thời gian"""
from datetime import datetime, timedelta
cutoff_time = datetime.now() - timedelta(hours=max_age_hours)
filtered = []
for article in articles:
published_str = article.get('publishedAt', '')
if published_str:
try:
published_time = datetime.fromisoformat(published_str.replace('Z', '+00:00'))
if published_time.replace(tzinfo=None) >= cutoff_time:
filtered.append(article)
except:
pass
return filtered
Quản lý Rủi ro
Nguyên tắc Quan trọng
- Risk per Trade: Không bao giờ rủi ro quá 2% tài khoản mỗi lệnh
- Stop Loss bắt buộc: Luôn đặt Stop Loss khi vào lệnh
- Take Profit: Sử dụng tỷ lệ Risk/Reward tối thiểu 2:1
- Position Sizing: Tính toán chính xác dựa trên Stop Loss
- Xác nhận nhiều nguồn: Không chỉ dựa vào một bài viết
Công thức Position Sizing
Position Size = (Account Balance × Risk %) / (Entry Price - Stop Loss Price)
Kết quả và Hiệu suất
Metrics Quan trọng
Khi đánh giá hiệu suất bot:
- Win Rate: Tỷ lệ lệnh thắng (mục tiêu: > 50%)
- Profit Factor: Tổng lợi nhuận / Tổng lỗ (mục tiêu: > 1.5)
- Max Drawdown: Mức sụt giảm tối đa (mục tiêu: < 20%)
- Average Win/Loss Ratio: Tỷ lệ lợi nhuận trung bình / lỗ trung bình (mục tiêu: > 2.0)
- News Impact Score: Đo lường tác động của tin tức lên giá
Ví dụ Kết quả
Period: 1 tháng
Symbol: BTC/USDT
News Sources: NewsAPI, RSS feeds
Results:
- Total Trades: 12
- Winning Trades: 7 (58.3%)
- Losing Trades: 5 (41.7%)
- Win Rate: 58.3%
- Average News Impact: +2.5% (positive news), -1.8% (negative news)
- Best Trade: +8.2% (major partnership announcement)
- Worst Trade: -3.1% (false positive sentiment)
Lưu ý Quan trọng
Cảnh báo Rủi ro
- Giao dịch có rủi ro cao: Có thể mất toàn bộ vốn đầu tư
- Tin tức có thể sai: Sentiment analysis không phải lúc nào cũng chính xác
- Phản ứng nhanh: Cần phản ứng nhanh với tin tức quan trọng
- False signals: Có thể có tín hiệu giả từ tin tức không quan trọng
- API limitations: NewsAPI có giới hạn số lượng request
Best Practices
- Bắt đầu với Testnet: Test kỹ lưỡng trên testnet ít nhất 1 tháng
- Bắt đầu nhỏ: Khi chuyển sang live, bắt đầu với số tiền nhỏ
- Giám sát thường xuyên: Không để bot chạy hoàn toàn tự động
- Cập nhật thường xuyên: Theo dõi và cập nhật bot khi thị trường thay đổi
- Logging đầy đủ: Ghi log mọi hoạt động và tin tức
- Error Handling: Xử lý lỗi kỹ lưỡng, đặc biệt với API
- Xác nhận nhiều nguồn: Không chỉ dựa vào một nguồn tin
Tài liệu Tham khảo
News APIs
Sentiment Analysis
Tài liệu CCXT
Kết luận
Chiến lược News Trading là một phương pháp giao dịch hiệu quả khi được thực hiện đúng cách. Bot trong bài viết này cung cấp:
- Thu thập tin tức tự động từ NewsAPI
- Phân tích sentiment bằng TextBlob và VADER
- Lọc tin tức quan trọng với keyword analysis
- Xác nhận với giá để giảm false signals
- Quản lý rủi ro chặt chẽ với Stop Loss và Position Sizing
- Tự động hóa hoàn toàn giao dịch
Tuy nhiên, hãy nhớ rằng:
- Không có chiến lược hoàn hảo: Mọi chiến lược đều có thể thua lỗ
- Quản lý rủi ro là số 1: Luôn ưu tiên bảo vệ vốn
- Kiên nhẫn và kỷ luật: Tuân thủ quy tắc, không giao dịch theo cảm xúc
- Học hỏi liên tục: Thị trường luôn thay đổi, cần cập nhật kiến thức
- News Trading cần tốc độ: Phản ứng nhanh với tin tức quan trọng
Chúc bạn giao dịch thành công!
Tác giả: Hướng Nghiệp Data
Ngày đăng: 2024
Tags: #NewsTrading #TradingBot #Python #AlgorithmicTrading #SentimentAnalysis