|
@@ -860,6 +860,14 @@ class HyperliquidAccountAnalyzer:
|
|
ranges['pnl_max'] = max(pnls)
|
|
ranges['pnl_max'] = max(pnls)
|
|
ranges['pnl_range'] = ranges['pnl_max'] - ranges['pnl_min']
|
|
ranges['pnl_range'] = ranges['pnl_max'] - ranges['pnl_min']
|
|
|
|
|
|
|
|
+ # Separate positive and negative PnL for different scoring
|
|
|
|
+ positive_pnls = [p for p in pnls if p > 0]
|
|
|
|
+ negative_pnls = [p for p in pnls if p < 0]
|
|
|
|
+ ranges['has_profitable'] = len(positive_pnls) > 0
|
|
|
|
+ ranges['has_unprofitable'] = len(negative_pnls) > 0
|
|
|
|
+ ranges['most_profitable'] = max(positive_pnls) if positive_pnls else 0
|
|
|
|
+ ranges['most_unprofitable'] = min(negative_pnls) if negative_pnls else 0
|
|
|
|
+
|
|
# Win rate range (use all accounts)
|
|
# Win rate range (use all accounts)
|
|
win_rates = [s.win_rate for s in all_accounts]
|
|
win_rates = [s.win_rate for s in all_accounts]
|
|
ranges['winrate_min'] = min(win_rates)
|
|
ranges['winrate_min'] = min(win_rates)
|
|
@@ -883,12 +891,18 @@ class HyperliquidAccountAnalyzer:
|
|
ranges['duration_max'] = 24
|
|
ranges['duration_max'] = 24
|
|
ranges['duration_range'] = 24
|
|
ranges['duration_range'] = 24
|
|
|
|
|
|
- # Drawdown range (use all accounts)
|
|
|
|
|
|
+ # Drawdown range (use all accounts) - ENHANCED
|
|
drawdowns = [s.max_drawdown for s in all_accounts]
|
|
drawdowns = [s.max_drawdown for s in all_accounts]
|
|
ranges['drawdown_min'] = min(drawdowns)
|
|
ranges['drawdown_min'] = min(drawdowns)
|
|
ranges['drawdown_max'] = max(drawdowns)
|
|
ranges['drawdown_max'] = max(drawdowns)
|
|
ranges['drawdown_range'] = ranges['drawdown_max'] - ranges['drawdown_min']
|
|
ranges['drawdown_range'] = ranges['drawdown_max'] - ranges['drawdown_min']
|
|
|
|
|
|
|
|
+ # Account age range (NEW)
|
|
|
|
+ ages = [s.analysis_period_days for s in all_accounts]
|
|
|
|
+ ranges['age_min'] = min(ages)
|
|
|
|
+ ranges['age_max'] = max(ages)
|
|
|
|
+ ranges['age_range'] = ranges['age_max'] - ranges['age_min']
|
|
|
|
+
|
|
return ranges
|
|
return ranges
|
|
|
|
|
|
ranges = get_data_ranges(stats_list)
|
|
ranges = get_data_ranges(stats_list)
|
|
@@ -898,7 +912,7 @@ class HyperliquidAccountAnalyzer:
|
|
score = 0.0
|
|
score = 0.0
|
|
score_breakdown = {}
|
|
score_breakdown = {}
|
|
|
|
|
|
- # 1. COPYABILITY FILTER (40% weight - most important)
|
|
|
|
|
|
+ # 1. COPYABILITY FILTER (35% weight - most important)
|
|
is_hft = stats.trading_frequency_per_day > 50
|
|
is_hft = stats.trading_frequency_per_day > 50
|
|
is_too_slow = stats.trading_frequency_per_day < 1
|
|
is_too_slow = stats.trading_frequency_per_day < 1
|
|
is_copyable = 1 <= stats.trading_frequency_per_day <= 20
|
|
is_copyable = 1 <= stats.trading_frequency_per_day <= 20
|
|
@@ -907,69 +921,95 @@ class HyperliquidAccountAnalyzer:
|
|
copyability_score = 0 # HFT bots get 0
|
|
copyability_score = 0 # HFT bots get 0
|
|
score_breakdown['copyability'] = f"❌ HFT Bot (0 points)"
|
|
score_breakdown['copyability'] = f"❌ HFT Bot (0 points)"
|
|
elif is_too_slow:
|
|
elif is_too_slow:
|
|
- copyability_score = 10 # Inactive accounts get minimal points
|
|
|
|
- score_breakdown['copyability'] = f"⚠️ Inactive (10 points)"
|
|
|
|
|
|
+ copyability_score = 5 # Inactive accounts get very low points
|
|
|
|
+ score_breakdown['copyability'] = f"⚠️ Inactive (5 points)"
|
|
elif is_copyable:
|
|
elif is_copyable:
|
|
- # For copyable accounts, score based on how close to ideal frequency (20 trades/day)
|
|
|
|
- ideal_freq = 20
|
|
|
|
|
|
+ # For copyable accounts, score based on how close to ideal frequency (15 trades/day)
|
|
|
|
+ ideal_freq = 15
|
|
freq_distance = abs(stats.trading_frequency_per_day - ideal_freq)
|
|
freq_distance = abs(stats.trading_frequency_per_day - ideal_freq)
|
|
# Max score when exactly at ideal, decreases as distance increases
|
|
# Max score when exactly at ideal, decreases as distance increases
|
|
- copyability_score = max(0, 40 - (freq_distance * 2)) # Lose 2 points per trade away from ideal
|
|
|
|
|
|
+ copyability_score = max(0, 35 - (freq_distance * 1.5)) # Lose 1.5 points per trade away from ideal
|
|
score_breakdown['copyability'] = f"✅ Copyable ({copyability_score:.1f} points - {stats.trading_frequency_per_day:.1f} trades/day)"
|
|
score_breakdown['copyability'] = f"✅ Copyable ({copyability_score:.1f} points - {stats.trading_frequency_per_day:.1f} trades/day)"
|
|
else:
|
|
else:
|
|
- copyability_score = 20 # Questionable frequency
|
|
|
|
|
|
+ copyability_score = 15 # Questionable frequency
|
|
score_breakdown['copyability'] = f"⚠️ Questionable ({copyability_score} points)"
|
|
score_breakdown['copyability'] = f"⚠️ Questionable ({copyability_score} points)"
|
|
|
|
|
|
score += copyability_score
|
|
score += copyability_score
|
|
|
|
|
|
- # 2. PROFITABILITY (25% weight) - Relative to cohort
|
|
|
|
- if ranges['pnl_range'] > 0:
|
|
|
|
- # Linear interpolation: worst performer gets 0, best gets 25
|
|
|
|
- pnl_normalized = (stats.total_pnl - ranges['pnl_min']) / ranges['pnl_range']
|
|
|
|
- profitability_score = pnl_normalized * 25
|
|
|
|
|
|
+ # 2. PROFITABILITY (30% weight) - HARSH PUNISHMENT for losses
|
|
|
|
+ if stats.total_pnl < 0:
|
|
|
|
+ # Severe punishment for unprofitable accounts
|
|
|
|
+ if ranges['has_unprofitable'] and ranges['most_unprofitable'] < stats.total_pnl:
|
|
|
|
+ # Scale from -15 (worst) to 0 (break-even)
|
|
|
|
+ loss_severity = abs(stats.total_pnl) / abs(ranges['most_unprofitable'])
|
|
|
|
+ profitability_score = -15 * loss_severity # Negative score for losses!
|
|
|
|
+ else:
|
|
|
|
+ profitability_score = -15 # Maximum penalty
|
|
|
|
+ score_breakdown['profitability'] = f"❌ LOSING ({profitability_score:.1f} points - ${stats.total_pnl:.0f} LOSS)"
|
|
|
|
+ elif stats.total_pnl == 0:
|
|
|
|
+ profitability_score = 0 # Breakeven gets no points
|
|
|
|
+ score_breakdown['profitability'] = f"⚖️ Breakeven (0 points - ${stats.total_pnl:.0f})"
|
|
else:
|
|
else:
|
|
- profitability_score = 12.5 # If all same PnL, give average score
|
|
|
|
|
|
+ # Positive PnL gets full scoring
|
|
|
|
+ if ranges['has_profitable'] and ranges['most_profitable'] > 0:
|
|
|
|
+ profit_ratio = stats.total_pnl / ranges['most_profitable']
|
|
|
|
+ profitability_score = profit_ratio * 30
|
|
|
|
+ else:
|
|
|
|
+ profitability_score = 15 # Average score if only one profitable account
|
|
|
|
+ score_breakdown['profitability'] = f"✅ Profitable ({profitability_score:.1f} points - ${stats.total_pnl:.0f})"
|
|
|
|
|
|
score += profitability_score
|
|
score += profitability_score
|
|
- score_breakdown['profitability'] = f"💰 Profitability ({profitability_score:.1f} points - ${stats.total_pnl:.0f})"
|
|
|
|
|
|
|
|
- # 3. WIN RATE (15% weight) - Relative to cohort
|
|
|
|
- if ranges['winrate_range'] > 0:
|
|
|
|
- winrate_normalized = (stats.win_rate - ranges['winrate_min']) / ranges['winrate_range']
|
|
|
|
- winrate_score = winrate_normalized * 15
|
|
|
|
- else:
|
|
|
|
- winrate_score = 7.5 # If all same win rate, give average score
|
|
|
|
|
|
+ # 3. RISK MANAGEMENT (20% weight) - HARSH PUNISHMENT for high drawdown
|
|
|
|
+ if stats.max_drawdown > 0.5: # 50%+ drawdown is disqualifying
|
|
|
|
+ risk_score = -10 # Negative score for extreme risk!
|
|
|
|
+ score_breakdown['risk'] = f"❌ EXTREME RISK ({risk_score} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
+ elif stats.max_drawdown > 0.25: # 25%+ drawdown is very bad
|
|
|
|
+ risk_score = -5 # Negative score for high risk
|
|
|
|
+ score_breakdown['risk'] = f"❌ HIGH RISK ({risk_score} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
+ elif stats.max_drawdown > 0.15: # 15%+ drawdown is concerning
|
|
|
|
+ risk_score = 5 # Low positive score
|
|
|
|
+ score_breakdown['risk'] = f"⚠️ Moderate Risk ({risk_score} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
+ elif stats.max_drawdown > 0.05: # 5-15% drawdown is acceptable
|
|
|
|
+ risk_score = 15 # Good score
|
|
|
|
+ score_breakdown['risk'] = f"✅ Good Risk Mgmt ({risk_score} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
+ else: # <5% drawdown is excellent
|
|
|
|
+ risk_score = 20 # Full points
|
|
|
|
+ score_breakdown['risk'] = f"✅ Excellent Risk ({risk_score} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
|
|
- score += winrate_score
|
|
|
|
- score_breakdown['winrate'] = f"📈 Win Rate ({winrate_score:.1f} points - {stats.win_rate:.1%})"
|
|
|
|
|
|
+ score += risk_score
|
|
|
|
|
|
- # 4. TRADE DURATION (10% weight) - Preference for 2-48 hour range
|
|
|
|
- if stats.avg_trade_duration_hours == 0:
|
|
|
|
- duration_score = 2 # Minimal score for 0 duration
|
|
|
|
|
|
+ # 4. ACCOUNT MATURITY (10% weight) - NEW FACTOR
|
|
|
|
+ min_good_age = 30 # At least 30 days of history is preferred
|
|
|
|
+ if stats.analysis_period_days < 7:
|
|
|
|
+ age_score = 0 # Too new, no confidence
|
|
|
|
+ score_breakdown['maturity'] = f"❌ Too New ({age_score} points - {stats.analysis_period_days} days)"
|
|
|
|
+ elif stats.analysis_period_days < 14:
|
|
|
|
+ age_score = 2 # Very new
|
|
|
|
+ score_breakdown['maturity'] = f"⚠️ Very New ({age_score} points - {stats.analysis_period_days} days)"
|
|
|
|
+ elif stats.analysis_period_days < min_good_age:
|
|
|
|
+ age_score = 5 # Somewhat new
|
|
|
|
+ score_breakdown['maturity'] = f"⚠️ Somewhat New ({age_score} points - {stats.analysis_period_days} days)"
|
|
else:
|
|
else:
|
|
- # Ideal range: 2-48 hours gets full score
|
|
|
|
- if 2 <= stats.avg_trade_duration_hours <= 48:
|
|
|
|
- duration_score = 10 # Perfect range
|
|
|
|
- elif 1 <= stats.avg_trade_duration_hours < 2:
|
|
|
|
- duration_score = 7 # Slightly too fast
|
|
|
|
- elif 48 < stats.avg_trade_duration_hours <= 168: # 1 week
|
|
|
|
- duration_score = 8 # Slightly too slow but still good
|
|
|
|
|
|
+ # Scale from 30 days (7 points) to max age (10 points)
|
|
|
|
+ if ranges['age_range'] > 0:
|
|
|
|
+ age_ratio = min(1.0, (stats.analysis_period_days - min_good_age) / max(1, ranges['age_max'] - min_good_age))
|
|
|
|
+ age_score = 7 + (age_ratio * 3) # 7-10 points
|
|
else:
|
|
else:
|
|
- duration_score = 3 # Too extreme
|
|
|
|
|
|
+ age_score = 8 # Average if all same age
|
|
|
|
+ score_breakdown['maturity'] = f"✅ Mature ({age_score:.1f} points - {stats.analysis_period_days} days)"
|
|
|
|
|
|
- score += duration_score
|
|
|
|
- score_breakdown['duration'] = f"🕒 Duration ({duration_score:.1f} points - {stats.avg_trade_duration_hours:.1f}h)"
|
|
|
|
|
|
+ score += age_score
|
|
|
|
|
|
- # 5. RISK MANAGEMENT (10% weight) - Lower drawdown is better
|
|
|
|
- if ranges['drawdown_range'] > 0:
|
|
|
|
- # Invert: lowest drawdown gets full score
|
|
|
|
- drawdown_normalized = 1 - ((stats.max_drawdown - ranges['drawdown_min']) / ranges['drawdown_range'])
|
|
|
|
- risk_score = drawdown_normalized * 10
|
|
|
|
|
|
+ # 5. WIN RATE (5% weight) - Reduced importance
|
|
|
|
+ if ranges['winrate_range'] > 0:
|
|
|
|
+ winrate_normalized = (stats.win_rate - ranges['winrate_min']) / ranges['winrate_range']
|
|
|
|
+ winrate_score = winrate_normalized * 5
|
|
else:
|
|
else:
|
|
- risk_score = 5 # If all same drawdown, give average score
|
|
|
|
|
|
+ winrate_score = 2.5 # If all same win rate, give average score
|
|
|
|
|
|
- score += risk_score
|
|
|
|
- score_breakdown['risk'] = f"📉 Risk Mgmt ({risk_score:.1f} points - {stats.max_drawdown:.1%} drawdown)"
|
|
|
|
|
|
+ score += winrate_score
|
|
|
|
+ score_breakdown['winrate'] = f"📈 Win Rate ({winrate_score:.1f} points - {stats.win_rate:.1%})"
|
|
|
|
|
|
return score, score_breakdown
|
|
return score, score_breakdown
|
|
|
|
|
|
@@ -985,10 +1025,15 @@ class HyperliquidAccountAnalyzer:
|
|
# Print data ranges for context
|
|
# Print data ranges for context
|
|
print(f"\n📊 COHORT ANALYSIS (for relative scoring):")
|
|
print(f"\n📊 COHORT ANALYSIS (for relative scoring):")
|
|
print(f" 💰 PnL Range: ${ranges['pnl_min']:.0f} to ${ranges['pnl_max']:.0f}")
|
|
print(f" 💰 PnL Range: ${ranges['pnl_min']:.0f} to ${ranges['pnl_max']:.0f}")
|
|
|
|
+ if ranges['has_unprofitable']:
|
|
|
|
+ print(f" ❌ Worst Loss: ${ranges['most_unprofitable']:.0f}")
|
|
|
|
+ if ranges['has_profitable']:
|
|
|
|
+ print(f" ✅ Best Profit: ${ranges['most_profitable']:.0f}")
|
|
print(f" 📈 Win Rate Range: {ranges['winrate_min']:.1%} to {ranges['winrate_max']:.1%}")
|
|
print(f" 📈 Win Rate Range: {ranges['winrate_min']:.1%} to {ranges['winrate_max']:.1%}")
|
|
print(f" 🔄 Frequency Range: {ranges['freq_min']:.1f} to {ranges['freq_max']:.1f} trades/day")
|
|
print(f" 🔄 Frequency Range: {ranges['freq_min']:.1f} to {ranges['freq_max']:.1f} trades/day")
|
|
- print(f" 🕒 Duration Range: {ranges['duration_min']:.1f}h to {ranges['duration_max']:.1f}h")
|
|
|
|
print(f" 📉 Drawdown Range: {ranges['drawdown_min']:.1%} to {ranges['drawdown_max']:.1%}")
|
|
print(f" 📉 Drawdown Range: {ranges['drawdown_min']:.1%} to {ranges['drawdown_max']:.1%}")
|
|
|
|
+ print(f" 📅 Account Age Range: {ranges['age_min']} to {ranges['age_max']} days")
|
|
|
|
+ print(f"\n⚠️ WARNING: Accounts with losses or high drawdown receive NEGATIVE scores!")
|
|
|
|
|
|
# Print results
|
|
# Print results
|
|
for i, (stats, score, breakdown) in enumerate(scored_accounts, 1):
|
|
for i, (stats, score, breakdown) in enumerate(scored_accounts, 1):
|
|
@@ -1113,14 +1158,16 @@ class HyperliquidAccountAnalyzer:
|
|
print(f" 📊 Relative Score: {best_score:.1f}/100")
|
|
print(f" 📊 Relative Score: {best_score:.1f}/100")
|
|
print(f" 🎯 Status: {best_stats.copyability_reason}")
|
|
print(f" 🎯 Status: {best_stats.copyability_reason}")
|
|
|
|
|
|
- if best_score >= 70:
|
|
|
|
|
|
+ if best_score >= 60:
|
|
recommendation = "🟢 HIGHLY RECOMMENDED"
|
|
recommendation = "🟢 HIGHLY RECOMMENDED"
|
|
- elif best_score >= 50:
|
|
|
|
|
|
+ elif best_score >= 40:
|
|
recommendation = "🟡 MODERATELY RECOMMENDED"
|
|
recommendation = "🟡 MODERATELY RECOMMENDED"
|
|
- elif best_score >= 30:
|
|
|
|
- recommendation = "🟠 PROCEED WITH CAUTION"
|
|
|
|
|
|
+ elif best_score >= 20:
|
|
|
|
+ recommendation = "🟠 PROCEED WITH EXTREME CAUTION"
|
|
|
|
+ elif best_score >= 0:
|
|
|
|
+ recommendation = "🔴 NOT RECOMMENDED (Risky)"
|
|
else:
|
|
else:
|
|
- recommendation = "🔴 NOT RECOMMENDED"
|
|
|
|
|
|
+ recommendation = "⛔ DANGEROUS (Negative Score)"
|
|
|
|
|
|
print(f" {recommendation}")
|
|
print(f" {recommendation}")
|
|
|
|
|
|
@@ -1129,10 +1176,22 @@ class HyperliquidAccountAnalyzer:
|
|
print(f" {description}")
|
|
print(f" {description}")
|
|
|
|
|
|
print(f"\n⚙️ Suggested copy trading settings:")
|
|
print(f"\n⚙️ Suggested copy trading settings:")
|
|
- print(f" 📊 Portfolio allocation: 5-15% (start conservative)")
|
|
|
|
- print(f" ⚡ Max leverage limit: 3-5x")
|
|
|
|
|
|
+ if best_score >= 60:
|
|
|
|
+ print(f" 📊 Portfolio allocation: 10-25% (confident allocation)")
|
|
|
|
+ print(f" ⚡ Max leverage limit: 5-10x")
|
|
|
|
+ elif best_score >= 40:
|
|
|
|
+ print(f" 📊 Portfolio allocation: 5-15% (moderate allocation)")
|
|
|
|
+ print(f" ⚡ Max leverage limit: 3-5x")
|
|
|
|
+ elif best_score >= 20:
|
|
|
|
+ print(f" 📊 Portfolio allocation: 2-5% (very small allocation)")
|
|
|
|
+ print(f" ⚡ Max leverage limit: 2-3x")
|
|
|
|
+ else:
|
|
|
|
+ print(f" 📊 Portfolio allocation: DO NOT COPY")
|
|
|
|
+ print(f" ⚡ ACCOUNT IS TOO RISKY FOR COPY TRADING")
|
|
|
|
+
|
|
print(f" 💰 Min position size: $25-50")
|
|
print(f" 💰 Min position size: $25-50")
|
|
print(f" 🔄 Expected trades: {best_stats.trading_frequency_per_day:.1f} per day")
|
|
print(f" 🔄 Expected trades: {best_stats.trading_frequency_per_day:.1f} per day")
|
|
|
|
+ print(f" 📅 Account age: {best_stats.analysis_period_days} days")
|
|
|
|
|
|
else:
|
|
else:
|
|
print(f"\n❌ NO COPYABLE ACCOUNTS FOUND")
|
|
print(f"\n❌ NO COPYABLE ACCOUNTS FOUND")
|
|
@@ -1141,17 +1200,21 @@ class HyperliquidAccountAnalyzer:
|
|
if non_copyable_accounts:
|
|
if non_copyable_accounts:
|
|
print(f"\n❌ {len(non_copyable_accounts)} UNSUITABLE ACCOUNTS (DO NOT COPY):")
|
|
print(f"\n❌ {len(non_copyable_accounts)} UNSUITABLE ACCOUNTS (DO NOT COPY):")
|
|
for i, (account, score, breakdown) in enumerate(non_copyable_accounts[:3], 1): # Show top 3 unsuitable
|
|
for i, (account, score, breakdown) in enumerate(non_copyable_accounts[:3], 1): # Show top 3 unsuitable
|
|
- print(f" {i}. {account.address[:10]}... - {account.copyability_reason} (Score: {score:.1f})")
|
|
|
|
|
|
+ score_indicator = "⛔ DANGEROUS" if score < 0 else "🔴 Risky" if score < 20 else "⚠️ Poor"
|
|
|
|
+ print(f" {i}. {account.address[:10]}... - {account.copyability_reason} ({score_indicator}: {score:.1f})")
|
|
|
|
|
|
if len(non_copyable_accounts) > 3:
|
|
if len(non_copyable_accounts) > 3:
|
|
print(f" ... and {len(non_copyable_accounts) - 3} more unsuitable accounts")
|
|
print(f" ... and {len(non_copyable_accounts) - 3} more unsuitable accounts")
|
|
|
|
|
|
- print(f"\n⚠️ IMPORTANT COPY TRADING GUIDELINES:")
|
|
|
|
- print(f" • Only copy accounts with 1-20 trades per day")
|
|
|
|
- print(f" • Avoid HFT bots (50+ trades/day) - impossible to follow")
|
|
|
|
- print(f" • Start with small allocation (5%) and increase gradually")
|
|
|
|
- print(f" • Monitor performance and adjust leverage accordingly")
|
|
|
|
- print(f" • Higher relative scores indicate better performance within this cohort")
|
|
|
|
|
|
+ print(f"\n⚠️ ENHANCED COPY TRADING GUIDELINES:")
|
|
|
|
+ print(f" • ✅ ONLY copy accounts with 30+ days of history")
|
|
|
|
+ print(f" • ✅ ONLY copy PROFITABLE accounts (positive PnL)")
|
|
|
|
+ print(f" • ✅ AVOID accounts with >15% max drawdown")
|
|
|
|
+ print(f" • ✅ Ideal frequency: 5-15 trades per day")
|
|
|
|
+ print(f" • ❌ NEVER copy accounts with negative scores")
|
|
|
|
+ print(f" • ❌ NEVER copy accounts losing money")
|
|
|
|
+ print(f" • ⚠️ Start with 2-5% allocation even for good accounts")
|
|
|
|
+ print(f" • 📊 Higher scores = more reliable performance")
|
|
print(f" • 🔄 ADVANTAGE: Perpetual traders can profit in BOTH bull & bear markets!")
|
|
print(f" • 🔄 ADVANTAGE: Perpetual traders can profit in BOTH bull & bear markets!")
|
|
print(f" • 📈📉 They go long (profit when price rises) AND short (profit when price falls)")
|
|
print(f" • 📈📉 They go long (profit when price rises) AND short (profit when price falls)")
|
|
print(f" • 💡 This means potential profits in any market condition")
|
|
print(f" • 💡 This means potential profits in any market condition")
|
|
@@ -1161,8 +1224,10 @@ class HyperliquidAccountAnalyzer:
|
|
print(f"\n🎯 DIRECTIONAL TRADING ANALYSIS OF COPYABLE ACCOUNTS:")
|
|
print(f"\n🎯 DIRECTIONAL TRADING ANALYSIS OF COPYABLE ACCOUNTS:")
|
|
for i, (stats, score, breakdown) in enumerate(copyable_accounts, 1):
|
|
for i, (stats, score, breakdown) in enumerate(copyable_accounts, 1):
|
|
short_capability = "✅ Excellent" if stats.short_percentage > 30 else "⚠️ Limited" if stats.short_percentage > 10 else "❌ Minimal"
|
|
short_capability = "✅ Excellent" if stats.short_percentage > 30 else "⚠️ Limited" if stats.short_percentage > 10 else "❌ Minimal"
|
|
|
|
+ risk_indicator = "⛔ DANGEROUS" if score < 0 else "🔴 Risky" if score < 20 else "⚠️ Caution" if score < 40 else "✅ Good"
|
|
print(f" {i}. {stats.address[:10]}... - {stats.short_percentage:.1f}% shorts ({short_capability} short capability)")
|
|
print(f" {i}. {stats.address[:10]}... - {stats.short_percentage:.1f}% shorts ({short_capability} short capability)")
|
|
- print(f" Style: {stats.trading_style}")
|
|
|
|
|
|
+ print(f" Score: {score:.1f}/100 ({risk_indicator}) | Style: {stats.trading_style}")
|
|
|
|
+ print(f" Age: {stats.analysis_period_days} days | PnL: ${stats.total_pnl:.0f} | Drawdown: {stats.max_drawdown:.1%}")
|
|
print(f" Advantage: Can profit when {', '.join(stats.top_tokens[:2])} prices move in EITHER direction")
|
|
print(f" Advantage: Can profit when {', '.join(stats.top_tokens[:2])} prices move in EITHER direction")
|
|
|
|
|
|
async def get_leaderboard(self, window: str = "7d", limit: int = 20) -> Optional[List[str]]:
|
|
async def get_leaderboard(self, window: str = "7d", limit: int = 20) -> Optional[List[str]]:
|