
基于套接字(Sockets)的Twitter情绪分析
引言
本文介绍了一种利用社交媒体平台实时情绪分析来指导交易决策的复杂交易机器人。通过将 MetaTrader 5(MT5)与基于 Python 的情绪分析引擎相结合,该机器人代表了量化金融与自然语言处理的前沿融合。
该机器人的架构基于客户端-服务器模型,利用套接字通信弥合了 MT5 交易能力和 Python 数据处理能力之间的差距。其核心是分析与特定金融工具相关的 Twitter 情绪,将社交媒体的热度转化为可操作的交易信号。
这种创新方法不仅展示了跨学科技术在金融领域的潜力,还突显了现代交易策略中替代数据源日益增长的重要性。随着我们深入探讨机器人的功能和代码结构,我们将探索它是如何处理社交媒体数据、管理网络通信以及根据情绪评分执行交易的。
以下分析将提供对机器人组件的说明,讨论以MQL5语言编写的EA及其 Python 服务器对应部分。我们将研究它们之间的交互细节、情绪分析方法以及实现的交易逻辑。这一探索将为对社交媒体分析与算法交易交叉领域感兴趣的交易者、开发者和研究人员提供宝贵的视角。
分解
我将分别解释 MetaTrader 5 的EA和 Python 服务器的代码,阐述它们的关键组件和功能。
MetaTrader 5 专家顾问(MQL5)
1. 初始化和输入参数:
EA 首先定义交易品种、Twitter API 凭证以及止损和获利等交易参数的输入参数。它还包含了用于交易执行和头寸管理的必要库。
//+------------------------------------------------------------------+ //| Twitter_Sentiment_with_soquets.mq5 | //| Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera. | //| https://www.mql5.com/en/users/jsgaston/news | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera." #property link "https://www.mql5.com/en/users/jsgaston/news" #property version "1.001" #property description "This EA sends data to a Python server and receives sentiment analysis" input group "---- Symbol to work with ----" input string symbol1 = "BTCUSD"; // Symbol input group "---- Passwords ----" input string twitter_api_key = "TwitterApiKey"; // Twitter API key input string twitter_api_secret = "TwitterApiSecret"; // Twitter API secret input string twitter_access_token = "TwitterAccessToken"; // Twitter Access Token input string twitter_access_token_secret = "TwitterAccessTokenSecret"; // Twitter Access Token Secret input string twitter_bearer_token = "TwitterBearerToken"; // Twitter Bearer Token input string client_id = "TwitterClientID"; // Twitter Client ID input string client_secret = "TwitterClientSecret"; // Twitter Client Secret input group "---- Stops ----" input bool InpUseStops = false; // Use stops in trading input int InpTakeProfit = 1000; // TakeProfit level input int InpStopLoss = 500; // StopLoss level input double InpLot = 0.1; // Lot size #include <Trade\Trade.mqh> // Instatiate Trades Execution Library #include <Trade\OrderInfo.mqh> // Instatiate Library for Orders Information #include <Trade\PositionInfo.mqh> // Library for all position features and information // Create a trade object CTrade trade; // Last request time datetime lastRequestTime = 0; int requestInterval = 30 * 60; // 30 minutes in seconds
2. OnInit 函数:
- 检查推送消息功能是否被启用。
- 在初始化时激活同Python服务器的通信。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!TerminalInfoInteger(TERMINAL_NOTIFICATIONS_ENABLED)) { Print("Push notifications are not enabled. Please enable them in terminal settings."); return INIT_FAILED; } // Call the function to communicate with the Python server during initialization string data = StringFormat("%s,%s,%s,%s,%s,%s,%s,%s", symbol1, twitter_api_key, twitter_api_secret, twitter_access_token, twitter_access_token_secret, twitter_bearer_token, client_id, client_secret); string result = CommunicateWithPython(data); Print("Result received from Python server during initialization: ", result); return(INIT_SUCCEEDED); }
3. OnTick 函数:
- 实现了向 Python 服务器发送请求的 30 分钟间隔。
- 使用 CommunicateWithPython() 函数对数据进行格式化并发送到 Python 服务器。
- 处理接收到的情绪数据,并根据情绪评分执行交易。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check if 30 minutes have passed since the last request if(TimeCurrent() - lastRequestTime < requestInterval) { return; // Exit if the interval has not passed } // Update the last request time lastRequestTime = TimeCurrent(); // Call the function to communicate with the Python server string data = StringFormat("%s,%s,%s,%s,%s,%s,%s,%s", symbol1, twitter_api_key, twitter_api_secret, twitter_access_token, twitter_access_token_secret, twitter_bearer_token, client_id, client_secret); string result = CommunicateWithPython(data); if(result == "") { Print("No data received from Python"); return; } // Process the sentiment value Print("Raw result: ", result); // Debug line string sentiment_values[]; int num_elements = StringSplit(result, ',', sentiment_values); Print("Number of elements: ", num_elements); // Debug line if(num_elements > 0) { double tweet_sentiment = StringToDouble(sentiment_values[0]); Print("Twitter sentiment: ", tweet_sentiment); // Debug line double price = SymbolInfoDouble(symbol1, SYMBOL_BID); double take_profit = InpTakeProfit * _Point; double stop_loss = InpStopLoss * _Point; if(tweet_sentiment > 0) { // Buy if sentiment is positive if(PositionSelect(symbol1)) { Print("Position already open. Skipping buy."); } else { if(trade.Buy(InpLot, symbol1, price, price - stop_loss, price + take_profit)) Print("Buying ", InpLot, " lots of ", symbol1); else Print("Failed to place buy order. Error: ", GetLastError()); } } else if(tweet_sentiment < 0) { // Sell if sentiment is negative if(PositionSelect(symbol1)) { Print("Position already open. Skipping sell."); } else { if(trade.Sell(InpLot, symbol1, price, price + stop_loss, price - take_profit)) Print("Selling ", InpLot, " lots of ", symbol1); else Print("Failed to place sell order. Error: ", GetLastError()); } } } else if(StringFind(result, "ERROR,") == 0) { Print("Error received from Python server: ", result); } else { Print("Unexpected response format: ", result); } }
4. 套接字通信:
- InitSocket() 函数创建一个套接字并连接到 Python 服务器。
- CommunicateWithPython() 函数负责将数据发送到 Python 服务器并接收来自服务器的响应。
//+------------------------------------------------------------------+ //| Initialize socket | //+------------------------------------------------------------------+ int InitSocket() { int socket_handle = SocketCreate(); if(socket_handle < 0) { Print("Error creating socket"); return -1; } Print("Socket created successfully."); // Connect to Python server bool isConnected = SocketConnect(socket_handle, "127.0.0.1", 65432, 5000); if(!isConnected) { int error = GetLastError(); Print("Error connecting to Python server. Error code: ", error); SocketClose(socket_handle); return -1; } Print("Connection to Python server established."); return socket_handle; } //+------------------------------------------------------------------+ //| Function to send and receive data | //+------------------------------------------------------------------+ string CommunicateWithPython(string data) { int socket_handle = InitSocket(); if(socket_handle < 0) return ""; // Ensure data is encoded in UTF-8 uchar send_buffer[]; StringToCharArray(data, send_buffer); int bytesSent = SocketSend(socket_handle, send_buffer, ArraySize(send_buffer)); if(bytesSent < 0) { Print("Error sending data!"); SocketClose(socket_handle); return ""; } Print("Data sent: ", bytesSent); uint timeout = 5000; // 5 seconds timeout uchar rsp[]; string result; uint timeout_check = GetTickCount() + timeout; do { uint len = SocketIsReadable(socket_handle); if(len) { int rsp_len; rsp_len = SocketRead(socket_handle, rsp, len, timeout); if(rsp_len > 0) { result += CharArrayToString(rsp, 0, rsp_len); } } } while(GetTickCount() < timeout_check && !IsStopped()); SocketClose(socket_handle); if(result == "") { Print("No data received from Python"); return ""; } Print("Data received from Python: ", result); return result; } //+------------------------------------------------------------------+ //| Helper function to convert uchar array to string | //+------------------------------------------------------------------+ string CharArrayToString(const uchar &arr[], int start, int length) { string result; char temp[]; ArrayResize(temp, length); ArrayCopy(temp, arr, 0, start, length); result = CharArrayToString(temp); return result; } //+------------------------------------------------------------------+
5. 交易逻辑:
- 如果情绪为正面(> 0),它尝试开启一个多头仓位。
- 如果情绪为负面(< 0),它尝试开启一个空头仓位。
- 使用 CTrade 类来执行交易。
Python 服务器:
1. 服务器设置:
- start_server() 函数初始化一个套接字服务器,用于监听传入的连接。
def start_server(): """Starts the server that waits for incoming connections.""" server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('127.0.0.1', 65432)) server_socket.listen(1) print("Python server started and waiting for connections...") while True: client_socket, addr = server_socket.accept() print(f"Connection from {addr}") try: data = client_socket.recv(1024) data = data.decode('utf-8', errors='ignore') print(f"Received data: {data}") inputs = data.split(',') if len(inputs) != 8: raise ValueError("Eight inputs were expected") symbol, twitter_api_key, twitter_api_secret, twitter_access_token, twitter_access_token_secret, twitter_bearer_token, client_id, client_secret = inputs result = process_data(symbol, twitter_bearer_token) result_string = f"{result['tweet_sentiment']}" client_socket.sendall(result_string.encode('utf-8')) print(f"Response sent to client: {result_string}") except Exception as e: print(f"Communication error: {e}") error_message = f"ERROR,{str(e)}" client_socket.sendall(error_message.encode('utf-8')) finally: client_socket.shutdown(socket.SHUT_RDWR) client_socket.close() print("Connection closed")
2. 数据处理:
当接收到连接时,它会对数据进行解码,并将其拆分为单独的输入。
调用 process_data() 函数,并传入交易品种和 Twitter 访问令牌。
def process_data(symbol, bearer_token): """Processes the data obtained from news and tweets.""" result = { "tweet_sentiment": 0} try: result["tweet_sentiment"] = analyze_tweets(bearer_token, symbol) except Exception as e: raise Exception(f"Error processing data: {e}") print(f"Data processed. Result: {result}") return result
3. 推文分析:
- analyze_tweets() 函数使用 Twitter API 获取关于指定交易品种的最新推文。
- 它使用 TextBlob 对每条推文进行情绪分析。
- 计算所有检索到的推文的情绪评分的平均值。
def analyze_tweets(bearer_token, symbol): """Analyzes recent tweets related to the given symbol.""" try: headers = { 'Authorization': f'Bearer {bearer_token}', } query = f"{symbol} lang:en -is:retweet" # Get the current time and subtract an hour end_time = datetime.now(timezone.utc) - timedelta(seconds=10) # Subtract 10 seconds from the current time start_time = end_time - timedelta(hours=4) # Convert to RFC 3339 (ISO 8601) format with second precision and 'Z' at the end start_time_str = start_time.strftime('%Y-%m-%dT%H:%M:%SZ') end_time_str = end_time.strftime('%Y-%m-%dT%H:%M:%SZ') search_url = f"https://api.twitter.com/2/tweets/search/recent?query={query}&max_results=100&start_time={start_time_str}&end_time={end_time_str}&sort_order=relevancy" print(f"Performing tweet search with query: {query}") print(f"Search URL: {search_url}") response = requests.get(search_url, headers=headers) print(f"Response status code: {response.status_code}") print(f"Response text: {response.text}") if response.status_code != 200: raise Exception(f"Error searching tweets: {response.status_code} - {response.text}") tweets = response.json().get('data', []) if not tweets: print("No tweets found") return 0 sentiments = [TextBlob(tweet['text']).sentiment.polarity for tweet in tweets] if not sentiments: print("No sentiments found") return 0 average_sentiment = sum(sentiments) / len(sentiments) return average_sentiment except Exception as e: print(f"Error: {e}") raise Exception(f"Error analyzing tweets: {e}")
4. 错误处理:
- 实现了 try-except 块,以捕获和处理数据处理以及 API 请求过程中可能出现的错误。
5. 响应:
- 将计算出的情绪评分发送回 MT5 客户端。
要点:
1. 实时集成: 该系统通过每 30 分钟获取和分析最新推文,提供近乎实时的情绪分析。
2. 基于情绪的交易: EA 使用情绪评分进行交易决策,对于正面情绪开启买入仓位,对于负面情绪开启卖出仓位。
3. 错误处理: EA 和 Python 服务器均实现了错误处理,以应对 API 请求、数据处理或通信中可能出现的问题。
4. 可扩展性: 基于套接字的通信允许潜在的扩展,例如在 Python 服务器中添加更多数据源或更复杂的分析,而无需大幅修改 MT5 EA。
5. 安全考虑: 该系统在每次请求中传递 API 凭证。在生产环境中,需要增强安全性。
6. 局限性: 当前实现仅开启新仓位,而不根据情绪变化管理现有仓位。
这个机器人展示了一种将外部数据分析与 MT5 交易相结合的有趣方法。然而,在用于实际交易环境之前,它需要经过彻底的测试,并可能需要优化其交易逻辑。
接下来该如何进行?
首先插入URL(工具->Expert Advisor)
第二步开启Python服务端
Python server started and waiting for connections...
第三步开始连接EA并等到接收数据
2024.07.24 23:29:45.087 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Socket created successfully. 2024.07.24 23:29:45.090 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Connection to Python server established. 2024.07.24 23:29:45.090 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Data sent: 380
Data processed. Result: {'tweet_sentiment': 0.20970252525252508} Response sent to client: 0.20970252525252508 Connection closed
2024.07.24 23:29:50.082 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Data received from Python: 0.20970252525252508 2024.07.24 23:29:50.082 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Result received from Python server during initialization: 0.20970252525252508 2024.07.24 23:29:50.082 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Socket created successfully. 2024.07.24 23:29:50.084 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Connection to Python server established. 2024.07.24 23:29:50.084 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Data sent: 380 2024.07.24 23:29:55.083 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Data received from Python: 0.20970252525252508 2024.07.24 23:29:55.083 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Raw result: 0.20970252525252508 2024.07.24 23:29:55.083 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Number of elements: 1 2024.07.24 23:29:55.084 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Twitter sentiment: 0.20970252525252508 2024.07.24 23:29:55.201 Twitter_Sentiment_with_soquets_v4 (EURUSD,H1) Buying 0.1 lots of EURUSD
如何获得好的结果?
为了改进策略和情绪分析以获得更好的结果,请考虑以下改进措施:
1. 优化情绪分析:
- 实现更先进的自然语言处理技术:
* 使用预训练语言模型(如 BERT 或 GPT)进行更细致的情绪分析。
* 引入基于方面的(aspect-based)情绪分析,专注于资产的特定属性。
- 扩展数据来源:
* 包括金融新闻文章、SEC 报告以及其他相关文本来源。
* 分析来自多个社交媒体平台的数据,而不仅仅是 Twitter。
- 实现情绪趋势分析:
* 跟踪情绪随时间的变化,而不仅仅是绝对值。
* 使用情绪评分的移动平均值来平滑短期波动。
2. 增强交易逻辑:
- 实现情绪阈值:
* 定义开启多头或空头仓位的具体情绪水平。
* 使用不同的阈值来进入和退出交易。
- 将情绪与技术分析相结合:
* 使用情绪作为技术指标的确认工具。
* 例如,只有当情绪为正面且价格高于移动平均线时,才做多。
- 引入成交量分析:
* 结合情绪分析时考虑社交媒体帖子的成交量。
* 较高的成交量与强烈的情绪可能表明信号更可靠。
3. 仓位规模与风险管理:
- 实现动态仓位规模调整:
* 根据情绪信号的强度调整仓位规模。
* 在确定仓位规模时考虑账户余额和整体市场波动。
- 使用情绪调整止损和获利水平:
* 根据情绪波动调整止损水平。
* 设置考虑潜在情绪变化的获利目标。
4. 时间框架考量:
- 在多个时间框架内分析情绪:
* 短期情绪用于入场/出场时机。
* 长期情绪趋势用于整体市场方向。
- 实现基于时间的过滤器:
* 在分析情绪和进行交易时考虑一天中的时间和一周中的日期。
* 某些资产在特定市场时段内可能具有更可靠的情绪信号。
5. 资产特定调整:
- 为不同资产类别定制策略:
* 加密货币对情绪的反应可能与传统股票不同。
* 开发特定于资产的情绪词典或模型。
6. 机器学习集成:
- 开发用于预测价格走势的机器学习模型:
* 将情绪评分作为特征,与传统市场数据一起使用。
* 实现强化学习以持续改进策略。
7. 回测与优化:
- 进行广泛的回测:
* 在不同的市场条件和时间段内测试策略。
* 使用逐步优化以避免过拟合。
- 实现参数优化:
* 使用遗传算法或其他优化技术来微调策略参数。
8. 情绪验证:
- 实现一个系统来验证情绪分析的准确性:
* 定期手动检查分析的文本样本。
* 跟踪情绪评分与随后价格走势之间的相关性。
9. 自适应策略:
- 开发能够适应市场变化的系统:
* 根据整体市场波动调整情绪阈值。
* 实现市场状态检测以切换不同的子策略。
10. 情绪衰减:
- 实现情绪的时间衰减因子:
* 给予最近的情绪数据更多权重。
* 逐渐降低较旧情绪评分的影响。
11. 反向策略:
- 在某些条件下考虑实施反向策略:
* 极端的情绪可能表明潜在的反转。
请记住,虽然这些改进措施可能会提升策略的表现,但它们也会增加复杂性。彻底测试每一个改变并理解其对整个系统的影响至关重要。从简单的改进开始,并在验证每一步后逐步增加复杂性。此外,始终要意识到,过去的业绩并不能保证未来的结果,尤其是在动态变化的金融市场中。
此外,为了获取 Twitter 推文,我使用了 X for Developers 账户。
结论
本文介绍的交易机器人在将社交媒体情绪分析与算法交易相结合方面迈出了重要一步。通过利用 MetaTrader 5 和 Python 的力量,该系统展示了在金融市场中进行实时、数据驱动决策的潜力。
该机器人的创新方法,结合基于套接字的通信、Twitter 数据的情绪分析以及自动化交易执行,展示了跨学科技术整合的可能性。它突显了现代交易策略中替代数据源日益增长的重要性,以及自然语言处理为金融决策提供有价值见解的潜力。
然而,与任何交易系统一样,还有改进和完善的空间。为改进策略提供的建议——从实施更复杂的情绪分析技术到引入机器学习模型——为未来发展提供了路线图。这些改进可能会带来更稳健可靠的交易信号、更好的风险管理以及整体性能的提升。
需要强调的是,尽管这个机器人提供了一种令人兴奋的情绪交易方法,但在将其部署到实际交易环境中之前,应进行彻底的测试和验证。金融市场的复杂性、情绪的快速变化以及算法交易的固有风险都需要谨慎和系统化的方法来实施和优化。
随着自然语言处理、机器学习和算法交易领域的不断发展,像这样的系统可能会在金融领域中发挥越来越重要的作用。它们不仅为交易者提供了一种新工具,还为理解和与金融市场互动提供了新的范式。
从最初的实现到一个完全优化、市场就绪的系统,这一过程是一场持续的学习、测试和改进之旅。它反映了金融科技更广泛的演变——始终突破边界,始终寻求新的见解,并始终致力于在不断变化的市场环境中做出更明智的决策。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15407



