2025-12-29 03:06:49 +08:00
"""
Researcher agents.
Includes: bull researcher and bear researcher.
"""
import json
from typing import Dict , Any
from .base_agent import BaseAgent
from app.services.llm import LLMService
logger = __import__ ( 'app.utils.logger' , fromlist = [ 'get_logger' ]) . get_logger ( __name__ )
class BullResearcher ( BaseAgent ):
"""Bullish researcher."""
def __init__ ( self , memory = None ):
super () . __init__ ( "BullResearcher" , memory )
self . llm_service = LLMService ()
def analyze ( self , context : Dict [ str , Any ]) -> Dict [ str , Any ]:
"""Construct the bull case."""
market = context . get ( 'market' )
symbol = context . get ( 'symbol' )
language = context . get ( 'language' , 'zh-CN' )
model = context . get ( 'model' )
# Inputs
market_report = context . get ( 'market_report' , {})
fundamental_report = context . get ( 'fundamental_report' , {})
news_report = context . get ( 'news_report' , {})
sentiment_report = context . get ( 'sentiment_report' , {})
# Memory
situation = f " { market } : { symbol } bull case"
2025-12-30 21:02:38 +08:00
memory_meta = {
"market" : market ,
"symbol" : symbol ,
"timeframe" : context . get ( "timeframe" ),
"features" : context . get ( "memory_features" ) or {},
}
memories = self . get_memories ( situation , n_matches = None , metadata = memory_meta )
2025-12-29 03:06:49 +08:00
memory_prompt = self . format_memories_for_prompt ( memories )
lang_instruction = self . _get_language_instruction ( language )
system_prompt = f """You are a Bullish Analyst, constructing a bullish argument for an investment decision. Your tasks are:
{ lang_instruction }
1. Highlight growth potential, competitive advantages, and positive market indicators.
2. Use the provided research and data to build a strong argument.
3. Effectively address/counter bearish viewpoints.
4. Learn from historical experience: { memory_prompt }
5. **Confidence Score**: Evaluate your confidence in the bullish case (0-100). Be realistic. If the data is mixed or weak, lower your confidence. Do NOT default to 75.
Please return in JSON format as follows:
{{
"argument": "Detailed bullish argument...",
"key_points": ["Point 1", "Point 2", "Point 3"],
"confidence": 75
}} """
user_prompt = f """Based on the following analysis reports, construct a bullish argument for { symbol } in { market } market:
**Market Technical Analysis:**
{ json . dumps ( market_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if market_report else 'No Data' }
**Fundamental Analysis:**
{ json . dumps ( fundamental_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if fundamental_report else 'No Data' }
**News Analysis:**
{ json . dumps ( news_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if news_report else 'No Data' }
**Sentiment Analysis:**
{ json . dumps ( sentiment_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if sentiment_report else 'No Data' }
Please construct a strong bullish argument, emphasizing growth potential, competitive advantages, and positive indicators."""
result = self . llm_service . safe_call_llm (
system_prompt ,
user_prompt ,
{ "argument" : "" , "key_points" : [], "confidence" : 50 },
model = model
)
return {
"type" : "bull" ,
"data" : result
}
def _get_language_instruction ( self , language : str ) -> str :
language_map = {
'zh-CN' : 'Answer in Simplified Chinese.' ,
'zh-TW' : 'Answer in Traditional Chinese.' ,
'en-US' : 'Answer in English.' ,
'ja-JP' : 'Answer in Japanese.' ,
'ko-KR' : 'Answer in Korean.' ,
'vi-VN' : 'Answer in Vietnamese.' ,
'th-TH' : 'Answer in Thai.' ,
'ar-SA' : 'Answer in Arabic.' ,
'fr-FR' : 'Answer in French.' ,
'de-DE' : 'Answer in German.'
}
return language_map . get ( language , 'Answer in English.' )
class BearResearcher ( BaseAgent ):
"""Bearish researcher."""
def __init__ ( self , memory = None ):
super () . __init__ ( "BearResearcher" , memory )
self . llm_service = LLMService ()
def analyze ( self , context : Dict [ str , Any ]) -> Dict [ str , Any ]:
"""Construct the bear case."""
market = context . get ( 'market' )
symbol = context . get ( 'symbol' )
language = context . get ( 'language' , 'zh-CN' )
model = context . get ( 'model' )
# Inputs
market_report = context . get ( 'market_report' , {})
fundamental_report = context . get ( 'fundamental_report' , {})
news_report = context . get ( 'news_report' , {})
sentiment_report = context . get ( 'sentiment_report' , {})
risk_report = context . get ( 'risk_report' , {})
# Bull argument (if present)
bull_argument = context . get ( 'bull_argument' , '' )
# Memory
situation = f " { market } : { symbol } bear case"
2025-12-30 21:02:38 +08:00
memory_meta = {
"market" : market ,
"symbol" : symbol ,
"timeframe" : context . get ( "timeframe" ),
"features" : context . get ( "memory_features" ) or {},
}
memories = self . get_memories ( situation , n_matches = None , metadata = memory_meta )
2025-12-29 03:06:49 +08:00
memory_prompt = self . format_memories_for_prompt ( memories )
lang_instruction = self . _get_language_instruction ( language )
system_prompt = f """You are a Bearish Analyst, constructing a bearish argument for an investment decision. Your tasks are:
{ lang_instruction }
1. Identify risks, challenges, and negative indicators.
2. Use the provided research and data to build a strong argument.
3. Effectively address/counter bullish viewpoints.
4. Learn from historical experience: { memory_prompt }
5. **Confidence Score**: Evaluate your confidence in the bearish case (0-100). Be realistic. If the data is mixed or weak, lower your confidence. Do NOT default to 75.
Please return in JSON format as follows:
{{
"argument": "Detailed bearish argument...",
"key_points": ["Point 1", "Point 2", "Point 3"],
"confidence": 75
}} """
# Bull argument section (avoid backslashes in f-string expression)
bull_argument_section = f "**Bullish Argument (Needs Rebuttal):** \n { bull_argument } " if bull_argument else ""
user_prompt = f """Based on the following analysis reports, construct a bearish argument for { symbol } in { market } market:
**Market Technical Analysis:**
{ json . dumps ( market_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if market_report else 'No Data' }
**Fundamental Analysis:**
{ json . dumps ( fundamental_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if fundamental_report else 'No Data' }
**News Analysis:**
{ json . dumps ( news_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if news_report else 'No Data' }
**Sentiment Analysis:**
{ json . dumps ( sentiment_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if sentiment_report else 'No Data' }
**Risk Analysis:**
{ json . dumps ( risk_report . get ( 'data' , {}), ensure_ascii = False , indent = 2 ) if risk_report else 'No Data' }
{ bull_argument_section }
Please construct a strong bearish argument, emphasizing risks, challenges, and negative indicators."""
result = self . llm_service . safe_call_llm (
system_prompt ,
user_prompt ,
{ "argument" : "" , "key_points" : [], "confidence" : 50 },
model = model
)
return {
"type" : "bear" ,
"data" : result
}
def _get_language_instruction ( self , language : str ) -> str :
language_map = {
'zh-CN' : 'Answer in Simplified Chinese.' ,
'zh-TW' : 'Answer in Traditional Chinese.' ,
'en-US' : 'Answer in English.' ,
'ja-JP' : 'Answer in Japanese.' ,
'ko-KR' : 'Answer in Korean.' ,
'vi-VN' : 'Answer in Vietnamese.' ,
'th-TH' : 'Answer in Thai.' ,
'ar-SA' : 'Answer in Arabic.' ,
'fr-FR' : 'Answer in French.' ,
'de-DE' : 'Answer in German.'
}
return language_map . get ( language , 'Answer in English.' )