update analyst rating

This commit is contained in:
MuslemRahimi 2024-12-02 13:05:39 +01:00
parent 2ee0c3d530
commit ac6d080b1c

View File

@ -13,7 +13,7 @@ from collections import Counter
import aiohttp import aiohttp
import asyncio import asyncio
import statistics import statistics
import math
load_dotenv() load_dotenv()
api_key = os.getenv('BENZINGA_API_KEY') api_key = os.getenv('BENZINGA_API_KEY')
@ -99,6 +99,19 @@ async def get_data(ticker_list, item):
return item return item
def smooth_scale(value, max_value=100, curve_factor=2):
"""
Create a smooth, non-linear scaling that prevents extreme values
while allowing nuanced differentiation.
"""
# Ensure inputs are valid
if max_value <= 0:
raise ValueError("max_value must be greater than 0")
# Clamp the value to a non-negative range
normalized = max(min(value / max_value, 1), 0)
return math.pow(normalized, curve_factor)
def calculate_rating(data): def calculate_rating(data):
@ -106,6 +119,8 @@ def calculate_rating(data):
overall_success_rate = float(data['successRate']) overall_success_rate = float(data['successRate'])
total_ratings = int(data['totalRatings']) total_ratings = int(data['totalRatings'])
last_rating = data['lastRating'] last_rating = data['lastRating']
average_return_percentile = float(data['avgReturnPercentile'])
total_ratings_percentile = float(data['totalRatingsPercentile'])
try: try:
last_rating_date = datetime.strptime(last_rating, "%Y-%m-%d") last_rating_date = datetime.strptime(last_rating, "%Y-%m-%d")
@ -117,49 +132,54 @@ def calculate_rating(data):
return 0 return 0
else: else:
# Define weights for each factor # Define weights for each factor
weight_return = 0.4 weights = {
weight_success_rate = 0.3 'return': 0.35,
weight_total_ratings = 0.1 'success_rate': 0.35,
weight_difference = 0.2 # Reduced weight for difference 'total_ratings': 0.1,
'recency': 0.1,
'returnPercentile': 0.05,
'ratingsPercentile': 0.05,
}
# Calculate weighted sum # Calculate weighted sum
weighted_sum = (weight_return * overall_average_return + weighted_components = [
weight_success_rate * overall_success_rate + weights['return'] * smooth_scale(overall_average_return, max_value=50, curve_factor=1.8),
weight_total_ratings * total_ratings + weights['success_rate'] * smooth_scale(overall_success_rate, max_value=100, curve_factor=1.5),
weight_difference * (1 / (1 + difference))) # Adjusted weight for difference weights['total_ratings'] * smooth_scale(min(total_ratings, 100), max_value=100, curve_factor=1.3),
weights['recency'] * (1 / (1 + math.log1p(difference))),
weights['returnPercentile'] * smooth_scale(average_return_percentile),
weights['ratingsPercentile'] * smooth_scale(total_ratings_percentile),
]
# Normalize the weighted sum to get a rating between 0 and 5 # Calculate base rating
min_rating = 0 base_rating = sum(weighted_components)
max_rating = 5 normalized_rating = min(max(base_rating / sum(weights.values()) * 5, 0), 5)
normalized_rating = min(max(weighted_sum / (weight_return + weight_success_rate + weight_total_ratings + weight_difference), min_rating), max_rating) # Encourage higher ratings for sufficient data and good performance
if total_ratings > 50 and overall_success_rate > 60 and overall_average_return > 60:
normalized_rating += 1.0
# Apply additional conditions based on total ratings and average return elif total_ratings > 30 and overall_success_rate > 50 and overall_average_return > 50:
if normalized_rating >= 4: normalized_rating += 0.5
if total_ratings < 10:
normalized_rating -= 2.4 elif total_ratings > 20 and overall_success_rate >= 50 and overall_average_return >= 15:
elif total_ratings < 15: normalized_rating += 0.3
normalized_rating -= 2.5
elif total_ratings < 20: # Apply additional conditions based on return and success rate thresholds
normalized_rating -= 0.75 if overall_average_return <= 5:
elif total_ratings < 30: normalized_rating = max(normalized_rating - 1.5, 0)
normalized_rating -= 1
elif overall_average_return <= 10: elif overall_average_return <= 10:
normalized_rating -= 1.1 normalized_rating = max(normalized_rating - 1.0, 0)
if overall_average_return <= 0:
normalized_rating = max(normalized_rating - 2, 0)
# Cap the rating if the last rating is older than 30 days
if difference > 30:
normalized_rating = min(normalized_rating, 4.5)
if overall_success_rate < 50: if overall_success_rate < 50:
normalized_rating = min(normalized_rating, 4.6) normalized_rating = min(normalized_rating, 3.5)
if overall_average_return < 30: # Cap the rating for older ratings
normalized_rating = min(normalized_rating, 4.6) if difference > 30:
normalized_rating = min(normalized_rating, 4.8)
return round(normalized_rating, 2) # Ensure final rating remains in valid bounds
#print(round(min(max(normalized_rating, 0), 5), 2))
return round(min(max(normalized_rating, 0), 5), 2)
def get_top_stocks(): def get_top_stocks():
with open(f"json/analyst/all-analyst-data.json", 'r') as file: with open(f"json/analyst/all-analyst-data.json", 'r') as file:
@ -311,6 +331,8 @@ async def get_all_analyst_stats():
'avgReturn': item['ratings_accuracy'].get('overall_average_return', 0), 'avgReturn': item['ratings_accuracy'].get('overall_average_return', 0),
'successRate': item['ratings_accuracy'].get('overall_success_rate', 0), 'successRate': item['ratings_accuracy'].get('overall_success_rate', 0),
'totalRatings': item['ratings_accuracy'].get('total_ratings', 0), 'totalRatings': item['ratings_accuracy'].get('total_ratings', 0),
'totalRatingsPercentile': item['ratings_accuracy'].get('total_ratings_percentile', 0),
'avgReturnPercentile': item['ratings_accuracy'].get('avg_return_percentile', 0),
} for item in res_list] } for item in res_list]
return final_list return final_list
@ -399,7 +421,7 @@ async def process_analyst(item, con, session, start_date, end_date):
# Calculate success rate # Calculate success rate
if valid_ratings_count > 0: if valid_ratings_count > 0:
item['successRate'] = round(success_count / valid_ratings_count * 100, 2) # Success rate in percentage item['successRate'] = round((success_count / valid_ratings_count) * 100, 2) # Success rate in percentage
else: else:
item['successRate'] = 0 item['successRate'] = 0
@ -409,6 +431,8 @@ async def process_analyst(item, con, session, start_date, end_date):
'successRate': item.get('successRate', 0), 'successRate': item.get('successRate', 0),
'totalRatings': item['totalRatings'], 'totalRatings': item['totalRatings'],
'lastRating': item['lastRating'], 'lastRating': item['lastRating'],
'totalRatingsPercentile': item['totalRatingsPercentile'],
'avgReturnPercentile': item['avgReturnPercentile']
} }
item['analystScore'] = calculate_rating(stats_dict) item['analystScore'] = calculate_rating(stats_dict)