2025-11-05 19:39:02 +08:00
import os
2025-11-06 21:09:24 +08:00
import logging
import json
import requests
from technicalanalyzer import TechnicalAnalyzer
logger = logging . getLogger ( __name__ )
2025-11-05 19:39:02 +08:00
class DeepSeekAnalyzer :
""" DeepSeek AI Analyzer (KEN2.0 optimized) """
def __init__ ( self ) :
self . api_key = os . getenv ( ' DEEPSEEK_API_KEY ' )
self . api_url = ' https://api.deepseek.com/v1/chat/completions '
def analyze_market ( self , symbol , market_data , technical_indicators ) :
""" Use DeepSeek to analyze market (optimized version) """
if not self . api_key :
logger . warning ( " DeepSeek API key not set, skipping AI analysis " )
return None
try :
# Prepare analysis data
latest = market_data . iloc [ - 1 ]
technical_summary = self . _prepare_enhanced_technical_summary ( technical_indicators , market_data )
prompt = self . _create_enhanced_analysis_prompt ( symbol , latest , market_data , technical_summary )
response = self . _call_deepseek_api ( prompt )
return self . _parse_deepseek_response ( response )
except Exception as e :
logger . error ( f " DeepSeek analysis error: { e } " )
return None
def _prepare_enhanced_technical_summary ( self , indicators , market_data ) :
""" Prepare enhanced technical indicator summary """
summary = [ ]
try :
# Price information
current_price = indicators . get ( ' current_price ' , 0 )
summary . append ( f " Current price: $ { current_price : .4f } " )
# Multi-period RSI
rsi_7 = indicators . get ( ' rsi_7 ' , 50 )
rsi_14 = indicators . get ( ' rsi ' , 50 )
rsi_21 = indicators . get ( ' rsi_21 ' , 50 )
if hasattr ( rsi_7 , ' __len__ ' ) :
rsi_7 , rsi_14 , rsi_21 = rsi_7 . iloc [ - 1 ] , rsi_14 . iloc [ - 1 ] , rsi_21 . iloc [ - 1 ]
summary . append ( f " RSI(7/14/21): { rsi_7 : .1f } / { rsi_14 : .1f } / { rsi_21 : .1f } " )
# RSI status analysis
rsi_status = [ ]
for rsi_val , period in [ ( rsi_7 , 7 ) , ( rsi_14 , 14 ) , ( rsi_21 , 21 ) ] :
if rsi_val > 70 :
rsi_status . append ( f " RSI { period } overbought " )
elif rsi_val < 30 :
rsi_status . append ( f " RSI { period } oversold " )
if rsi_status :
summary . append ( f " RSI status: { ' , ' . join ( rsi_status ) } " )
# KDJ indicator
k , d , j = indicators . get ( ' kdj ' , ( 50 , 50 , 50 ) )
if hasattr ( k , ' __len__ ' ) :
k , d , j = k . iloc [ - 1 ] , d . iloc [ - 1 ] , j . iloc [ - 1 ]
summary . append ( f " KDJ: K= { k : .1f } , D= { d : .1f } , J= { j : .1f } " )
if j > 80 :
summary . append ( " KDJ status: Overbought area " )
elif j < 20 :
summary . append ( " KDJ status: Oversold area " )
# MACD indicator
macd_line , signal_line , histogram = indicators . get ( ' macd ' , ( 0 , 0 , 0 ) )
if hasattr ( macd_line , ' __len__ ' ) :
macd_line , signal_line , histogram = macd_line . iloc [ - 1 ] , signal_line . iloc [ - 1 ] , histogram . iloc [ - 1 ]
summary . append ( f " MACD: line= { macd_line : .4f } , signal= { signal_line : .4f } , histogram= { histogram : .4f } " )
summary . append ( f " MACD status: { ' Golden cross ' if macd_line > signal_line else ' Death cross ' } " )
# Moving averages
sma_20 = indicators . get ( ' sma_20 ' , 0 )
sma_50 = indicators . get ( ' sma_50 ' , 0 )
sma_100 = indicators . get ( ' sma_100 ' , 0 )
if hasattr ( sma_20 , ' __len__ ' ) :
sma_20 , sma_50 , sma_100 = sma_20 . iloc [ - 1 ] , sma_50 . iloc [ - 1 ] , sma_100 . iloc [ - 1 ]
price_vs_ma = [ ]
if current_price > sma_20 : price_vs_ma . append ( " Above 20-day MA " )
else : price_vs_ma . append ( " Below 20-day MA " )
if current_price > sma_50 : price_vs_ma . append ( " Above 50-day MA " )
else : price_vs_ma . append ( " Below 50-day MA " )
summary . append ( f " Moving averages: 20-day=$ { sma_20 : .2f } , 50-day=$ { sma_50 : .2f } , 100-day=$ { sma_100 : .2f } " )
summary . append ( f " Price position: { ' , ' . join ( price_vs_ma ) } " )
# Bollinger Bands
upper , middle , lower = indicators . get ( ' bollinger_2std ' , ( 0 , 0 , 0 ) )
if hasattr ( upper , ' __len__ ' ) :
upper , middle , lower = upper . iloc [ - 1 ] , middle . iloc [ - 1 ] , lower . iloc [ - 1 ]
bb_position = ( current_price - lower ) / ( upper - lower ) * 100 if ( upper - lower ) > 0 else 50
summary . append ( f " Bollinger Band position: { bb_position : .1f } % (0%=lower band, 100%=upper band) " )
# Trend strength
trend_strength = indicators . get ( ' trend_strength ' , 0 )
summary . append ( f " Trend strength: { trend_strength : .2f } (0-1, higher means stronger trend) " )
# Volatility
volatility = indicators . get ( ' volatility ' , 0 ) * 100 # Convert to percentage
summary . append ( f " Annualized volatility: { volatility : .1f } % " )
# ATR
atr = indicators . get ( ' atr ' , 0 )
if hasattr ( atr , ' __len__ ' ) :
atr = atr . iloc [ - 1 ]
atr_percent = ( atr / current_price * 100 ) if current_price > 0 else 0
summary . append ( f " ATR: { atr : .4f } ( { atr_percent : .2f } %) " )
# Support resistance information
sr_levels = TechnicalAnalyzer . calculate_support_resistance ( market_data )
if sr_levels :
summary . append ( f " Static support: $ { sr_levels [ ' static_support ' ] : .4f } , resistance: $ { sr_levels [ ' static_resistance ' ] : .4f } " )
summary . append ( f " Dynamic support: $ { sr_levels [ ' dynamic_support ' ] : .4f } , resistance: $ { sr_levels [ ' dynamic_resistance ' ] : .4f } " )
summary . append ( f " Relative resistance: { sr_levels [ ' current_vs_resistance ' ] : +.2f } %, relative support: { sr_levels [ ' current_vs_support ' ] : +.2f } % " )
return " \n " . join ( summary )
except Exception as e :
logger . error ( f " Error preparing technical summary: { e } " )
return " Technical indicator calculation exception "
def _create_enhanced_analysis_prompt ( self , symbol , latest , market_data , technical_summary ) :
""" Create enhanced analysis prompt """
# Calculate price changes
price_change_24h = 0
price_change_7d = 0
if len ( market_data ) > = 24 :
price_change_24h = ( ( latest [ ' close ' ] - market_data . iloc [ - 24 ] [ ' close ' ] ) / market_data . iloc [ - 24 ] [ ' close ' ] * 100 )
if len ( market_data ) > = 168 : # 7 days data (hourly)
price_change_7d = ( ( latest [ ' close ' ] - market_data . iloc [ - 168 ] [ ' close ' ] ) / market_data . iloc [ - 168 ] [ ' close ' ] * 100 )
# Calculate volume changes
volume_trend = " Rising " if latest [ ' volume ' ] > market_data [ ' volume ' ] . mean ( ) else " Falling "
prompt = f """
You are a professional cryptocurrency trading AI , conducting autonomous trading in the OKX digital currency market . Please comprehensively analyze the { symbol } trading pair and execute trades .
# Core Objective
* * Maximize Sharpe Ratio * *
Sharpe Ratio = Average Return / Return Volatility , which means :
Quality trades ( high win rate , large profit / loss ratio ) → Improve Sharpe
Stable returns , control drawdowns → Improve Sharpe
Frequent trading , small profits and losses → Increase volatility , severely reduce Sharpe
【 Market Overview 】
- Current price : $ { latest [ ' close ' ] : .4 f }
- 24 - hour change : { price_change_24h : + .2 f } %
- 7 - day change : { price_change_7d : + .2 f } %
- Volume : { latest [ ' volume ' ] : .0 f } ( { volume_trend } )
- Volume relative level : { ' High ' if latest [ ' volume ' ] > market_data [ ' volume ' ] . quantile ( 0.7 ) else ' Normal ' if latest [ ' volume ' ] > market_data [ ' volume ' ] . quantile ( 0.3 ) else ' Low ' }
【 Multi - dimensional Technical Analysis 】
{ technical_summary }
【 Market Environment Assessment 】
Please consider comprehensively :
1. Trend direction and strength
2. Overbought / oversold status
3. Volume coordination
4. Volatility level
5. Support resistance positions
6. Multi - timeframe consistency
【 Risk Management Suggestions 】
- Suggested position : Adjust based on signal strength and volatility
- Stop - loss setting : Reference ATR and support levels
- Holding time : Based on trend strength
【 Output Format Requirements 】
Please reply strictly in the following JSON format :
{ {
" action " : " BUY/SELL/HOLD " ,
" confidence " : " HIGH/MEDIUM/LOW " ,
" position_size " : " Suggested position ratio (0.1-0.5) " ,
" stop_loss " : " Suggested stop-loss price or percentage " ,
" take_profit " : " Suggested take-profit price or percentage " ,
" timeframe " : " Suggested holding time (SHORT/MEDIUM/LONG) " ,
" reasoning " : " Detailed analysis logic and risk explanation "
} }
Please provide objective suggestions based on professional technical analysis and risk management principles .
"""
return prompt
def analyze_sell_proportion ( self , symbol , position_info , market_analysis , current_price ) :
""" Analyze sell proportion """
if not self . api_key :
logger . warning ( " DeepSeek API key not set, using default sell proportion " )
return self . _get_fallback_sell_proportion ( position_info , ' MEDIUM ' )
try :
prompt = self . _create_sell_proportion_prompt ( symbol , position_info , market_analysis , current_price )
response = self . _call_deepseek_api ( prompt )
return self . _parse_sell_proportion_response ( response )
except Exception as e :
logger . error ( f " DeepSeek sell proportion analysis error: { e } " )
return self . _get_fallback_sell_proportion ( position_info , ' MEDIUM ' )
def _create_sell_proportion_prompt ( self , symbol , position_info , market_analysis , current_price ) :
""" Create sell proportion analysis prompt """
# Calculate profit situation
entry_price = position_info [ ' entry_price ' ]
profit_ratio = ( current_price - entry_price ) / entry_price if entry_price > 0 else 0
profit_status = " Profit " if profit_ratio > 0 else " Loss "
profit_amount = ( current_price - entry_price ) * position_info [ ' base_amount ' ]
# Calculate position ratio
position_ratio = position_info [ ' position_ratio ' ] * 100 # Convert to percentage
# Prepare technical indicator summary
technical_summary = self . _prepare_enhanced_technical_summary (
market_analysis [ ' technical_indicators ' ] ,
market_analysis [ ' market_data ' ]
)
prompt = f """
You are a professional cryptocurrency trading AI , conducting autonomous trading in the OKX digital currency market . Analyze the { symbol } position situation and consider and execute sell decisions .
【 Current Position Situation 】
- Position quantity : { position_info [ ' base_amount ' ] : .8 f }
- Average entry price : $ { entry_price : .4 f }
- Current price : $ { current_price : .4 f }
- Profit ratio : { profit_ratio : + .2 % } ( { profit_status } )
- Profit amount : $ { profit_amount : + .2 f }
- Position ratio : { position_ratio : .2 f } % ( percentage of total portfolio )
- Position value : $ { position_info [ ' position_value ' ] : .2 f }
【 Market Technical Analysis 】
{ technical_summary }
【 Risk Situation 】
- Current price distance to stop - loss : { position_info [ ' distance_to_stop_loss ' ] : .2 % }
- Current price distance to take - profit : { position_info [ ' distance_to_take_profit ' ] : .2 % }
- Market volatility : { position_info [ ' market_volatility ' ] : .2 % }
- Trend strength : { position_info . get ( ' trend_strength ' , 0 ) : .2 f }
【 Sell Strategy Considerations 】
Please consider the following factors to determine sell proportion :
1. Profit ratio : Large profits can consider partial profit - taking , keep some position for higher gains
2. Position ratio : Overweight positions should reduce position to lower risk , light positions can consider holding
3. Technical signals : Strong bearish signals should increase sell proportion , bullish signals can reduce sell proportion
4. Market environment : High volatility markets should be more conservative , low volatility markets can be more aggressive
5. Risk control : Close to stop - loss should sell decisively , far from stop - loss can be more flexible
【 Position Management Principles 】
- Profit over 20 % : Consider partial profit - taking ( 30 - 70 % ) , lock in profits
- Profit 10 - 20 % : Decide based on signal strength ( 20 - 50 % )
- Small profit ( 0 - 10 % ) : Mainly based on technical signals ( 0 - 30 % )
- Small loss ( 0 - 5 % ) : Based on risk control ( 50 - 80 % )
- Large loss ( > 5 % ) : Consider stop - loss or position reduction ( 80 - 100 % )
【 Output Format Requirements 】
Please reply strictly in the following JSON format , only containing numeric ratio ( 0 - 1 ) :
{ {
" sell_proportion " : 0.75
} }
Explanation :
- 0.1 means sell 10 % of position
- 0.5 means sell 50 % of position
- 1.0 means sell all position
- 0.0 means don ' t sell (only in extremely bullish situations)
Please provide reasonable sell proportion suggestions based on professional analysis and risk management .
"""
return prompt
def _parse_sell_proportion_response ( self , response ) :
""" Parse sell proportion response """
try :
content = response [ ' choices ' ] [ 0 ] [ ' message ' ] [ ' content ' ]
# Try to parse JSON format
if ' { ' in content and ' } ' in content :
json_start = content . find ( ' { ' )
json_end = content . rfind ( ' } ' ) + 1
json_str = content [ json_start : json_end ]
parsed = json . loads ( json_str )
proportion = parsed . get ( ' sell_proportion ' , 0.5 )
# Ensure proportion is within reasonable range
proportion = max ( 0.0 , min ( 1.0 , proportion ) )
logger . info ( f " DeepSeek suggested sell proportion: { proportion : .1% } " )
return proportion
else :
# Text analysis: try to extract proportion from text
import re
patterns = [
r ' [ \ " \ " ]([ \ d.]+) % [ \ " \ " ] ' , # "50%"
r ' sell \ s*([ \ d.]+) % ' , # sell 50%
r ' ([ \ d.]+) % ' , # 50%
r ' [ \ " \ " ]([ \ d.]+)[ \ " \ " ] ' , # "0.5"
]
for pattern in patterns :
match = re . search ( pattern , content )
if match :
value = float ( match . group ( 1 ) )
if value > 1 : # If in percentage format
value = value / 100
proportion = max ( 0.0 , min ( 1.0 , value ) )
logger . info ( f " Parsed sell proportion from text: { proportion : .1% } " )
return proportion
# Default value
logger . warning ( " Unable to parse sell proportion, using default 50 % " )
return 0.5
except Exception as e :
logger . error ( f " Error parsing sell proportion response: { e } " )
return 0.5
def _get_fallback_sell_proportion ( self , position_info , decision_confidence ) :
""" Fallback sell proportion decision (when DeepSeek is unavailable) """
profit_ratio = position_info [ ' profit_ratio ' ]
position_ratio = position_info [ ' position_ratio ' ]
market_volatility = position_info . get ( ' market_volatility ' , 0 )
trend_strength = position_info . get ( ' trend_strength ' , 0 )
# Decision based on profit situation
if profit_ratio > = 0.20 : # Profit over 20%
base_proportion = 0.6 if decision_confidence == ' HIGH ' else 0.4
elif profit_ratio > = 0.10 : # Profit 10%-20%
base_proportion = 0.5 if decision_confidence == ' HIGH ' else 0.3
elif profit_ratio > = 0 : # Profit 0%-10%
base_proportion = 0.4 if decision_confidence == ' HIGH ' else 0.2
elif profit_ratio > = - 0.05 : # Loss 0%-5%
base_proportion = 0.7 if decision_confidence == ' HIGH ' else 0.5
else : # Loss over 5%
base_proportion = 0.9 if decision_confidence == ' HIGH ' else 0.7
# Adjustment based on position ratio
if position_ratio > 0.15 : # Overweight position
base_proportion = min ( 1.0 , base_proportion + 0.2 )
elif position_ratio < 0.05 : # Very light position
base_proportion = max ( 0.1 , base_proportion - 0.1 )
# Adjustment based on market volatility
if market_volatility > 0.05 : # High volatility market
base_proportion = min ( 1.0 , base_proportion + 0.1 )
elif market_volatility < 0.02 : # Low volatility market
base_proportion = max ( 0.1 , base_proportion - 0.1 )
# Adjustment based on trend strength
if trend_strength > 0.7 : # Strong trend
base_proportion = max ( 0.1 , base_proportion - 0.1 )
elif trend_strength < 0.3 : # Weak trend
base_proportion = min ( 1.0 , base_proportion + 0.1 )
# Ensure proportion is within reasonable range
base_proportion = max ( 0.1 , min ( 1.0 , base_proportion ) )
logger . info ( f " Fallback sell proportion decision: profit= { profit_ratio : +.2% } , position ratio= { position_ratio : .2% } , "
f " volatility= { market_volatility : .2% } , trend strength= { trend_strength : .2f } , final proportion= { base_proportion : .1% } " )
return base_proportion
def _call_deepseek_api ( self , prompt , max_retries = 3 ) :
""" Call DeepSeek API (improved retry mechanism) """
headers = {
' Authorization ' : f ' Bearer { self . api_key } ' ,
' Content-Type ' : ' application/json '
}
data = {
' model ' : ' deepseek-chat ' ,
' messages ' : [
{
' role ' : ' system ' ,
' content ' : ' You are a professional cryptocurrency trading AI, conducting autonomous trading in the OKX digital currency market, pursuing stable and sustainable returns '
} ,
{
' role ' : ' user ' ,
' content ' : prompt
}
] ,
' temperature ' : 0.3 ,
' max_tokens ' : 1000
}
for attempt in range ( max_retries ) :
try :
response = requests . post (
self . api_url ,
headers = headers ,
json = data ,
timeout = 30 # 30 second timeout
)
if response . status_code == 200 :
return response . json ( )
elif response . status_code == 429 : # Rate limit
wait_time = 2 * * attempt # Exponential backoff
logger . warning ( f " DeepSeek API rate limit, waiting { wait_time } seconds before retry " )
time . sleep ( wait_time )
continue
else :
logger . error ( f " DeepSeek API call failed: { response . status_code } - { response . text } " )
if attempt < max_retries - 1 :
time . sleep ( 1 )
continue
else :
raise Exception ( f " API call failed: { response . status_code } " )
except requests . exceptions . Timeout :
logger . warning ( f " DeepSeek API request timeout, attempt { attempt + 1 } / { max_retries } " )
if attempt < max_retries - 1 :
time . sleep ( 1 )
continue
else :
raise Exception ( " DeepSeek API request timeout " )
except Exception as e :
logger . error ( f " DeepSeek API call exception: { e } " )
if attempt < max_retries - 1 :
time . sleep ( 1 )
continue
else :
raise
def _parse_deepseek_response ( self , response ) :
""" Parse DeepSeek response - enhanced version """
try :
content = response [ ' choices ' ] [ 0 ] [ ' message ' ] [ ' content ' ] . strip ( )
logger . debug ( f " DeepSeek raw response: { content } " )
# Remove possible code block markers
if content . startswith ( ' ```json ' ) :
content = content [ 7 : ]
if content . endswith ( ' ``` ' ) :
content = content [ : - 3 ]
content = content . strip ( )
# Try to parse JSON format
if ' { ' in content and ' } ' in content :
json_start = content . find ( ' { ' )
json_end = content . rfind ( ' } ' ) + 1
json_str = content [ json_start : json_end ]
try :
parsed = json . loads ( json_str )
result = {
' action ' : parsed . get ( ' action ' , ' HOLD ' ) . upper ( ) ,
' confidence ' : parsed . get ( ' confidence ' , ' MEDIUM ' ) . upper ( ) ,
' reasoning ' : parsed . get ( ' reasoning ' , ' ' ) ,
' raw_response ' : content
}
# Validate action and confidence legality
if result [ ' action ' ] not in [ ' BUY ' , ' SELL ' , ' HOLD ' ] :
logger . warning ( f " AI returned illegal action: { result [ ' action ' ] } , using HOLD " )
result [ ' action ' ] = ' HOLD '
if result [ ' confidence ' ] not in [ ' HIGH ' , ' MEDIUM ' , ' LOW ' ] :
logger . warning ( f " AI returned illegal confidence: { result [ ' confidence ' ] } , using MEDIUM " )
result [ ' confidence ' ] = ' MEDIUM '
logger . info ( f " AI parsing successful: { result [ ' action ' ] } (confidence: { result [ ' confidence ' ] } ) " )
return result
except json . JSONDecodeError as e :
logger . warning ( f " JSON parsing failed: { e } , trying text analysis " )
return self . _parse_text_response ( content )
else :
# Text analysis
return self . _parse_text_response ( content )
except Exception as e :
logger . error ( f " Error parsing DeepSeek response: { e } " )
# Return default values to avoid system crash
return {
' action ' : ' HOLD ' ,
' confidence ' : ' MEDIUM ' ,
' reasoning ' : f ' Parsing error: { str ( e ) } ' ,
' raw_response ' : str ( response )
}
def _parse_text_response ( self , text ) :
""" Parse text response - enhanced version """
text_lower = text . lower ( )
# More precise action recognition
action = ' HOLD '
if any ( word in text_lower for word in [ ' 买入 ' , ' 做多 ' , ' buy ' , ' long ' , ' 上涨 ' , ' 看涨 ' ] ) :
action = ' BUY '
elif any ( word in text_lower for word in [ ' 卖出 ' , ' 做空 ' , ' sell ' , ' short ' , ' 下跌 ' , ' 看跌 ' ] ) :
action = ' SELL '
# More precise confidence level recognition
confidence = ' MEDIUM '
if any ( word in text_lower for word in [ ' 强烈 ' , ' 高信心 ' , ' high ' , ' 非常 ' , ' 强烈建议 ' ] ) :
confidence = ' HIGH '
elif any ( word in text_lower for word in [ ' 低 ' , ' 低信心 ' , ' low ' , ' 轻微 ' , ' 谨慎 ' ] ) :
confidence = ' LOW '
logger . info ( f " Text parsing result: { action } (confidence: { confidence } ) " )
return {
' action ' : action ,
' confidence ' : confidence ,
' reasoning ' : text ,
' raw_response ' : text
}