1
+ import { HyperliquidAPI } from '../../src' ;
2
+ import type { LeaderboardFilter , TimeWindow , TraderPosition , LeaderboardEntry } from '../../src' ;
3
+ import { t } from 'tasai' ;
4
+
5
+ const highlight = t . bold . cyan . toFunction ( ) ;
6
+ const header = t . bold . underline . magenta . toFunction ( ) ;
7
+ const subHeader = t . bold . yellow . toFunction ( ) ;
8
+ const positive = t . green . toFunction ( ) ;
9
+ const negative = t . red . toFunction ( ) ;
10
+ const value = t . bold . white . toFunction ( ) ;
11
+
12
+ async function runTraderAnalysis ( ) {
13
+ const api = new HyperliquidAPI ( ) ;
14
+
15
+ const filter : LeaderboardFilter = {
16
+ timeWindow : 'month' as TimeWindow ,
17
+ minAccountValue : 100_000 ,
18
+ minVolume : 1_000_000 ,
19
+ maxVolume : 100_000_000 ,
20
+ minPnL : 10_000 ,
21
+ minRoi : 0.5 ,
22
+ maxAccounts : 3
23
+ } ;
24
+
25
+ try {
26
+ console . log ( header ( "Fetching and analyzing top traders..." ) ) ;
27
+ const analysis = await analyzeTradersData ( api , filter ) ;
28
+
29
+ console . log ( header ( "\nTop Traders:" ) ) ;
30
+ analysis . topTraders . forEach ( ( trader , index ) => {
31
+ console . log ( subHeader ( `\n#${ index + 1 } :` ) ) ;
32
+ console . log ( `Address: ${ highlight ( trader . ethAddress ) } ` ) ;
33
+ console . log ( `Account Value: ${ value ( '$' + formatNumber ( trader . accountValue ) ) } ` ) ;
34
+ console . log ( `PnL: ${ formatPnL ( trader . windowPerformances [ 0 ] [ 1 ] . pnl ) } ` ) ;
35
+ console . log ( `ROI: ${ formatPercentage ( trader . windowPerformances [ 0 ] [ 1 ] . roi ) } ` ) ;
36
+ console . log ( `Volume: ${ value ( '$' + formatNumber ( trader . windowPerformances [ 0 ] [ 1 ] . vlm ) ) } ` ) ;
37
+ console . log ( `Open Positions: ${ value ( trader . totalOpenPositions ) } ` ) ;
38
+ console . log ( `Trade Count: ${ value ( trader . tradeCount ) } ` ) ;
39
+ } ) ;
40
+
41
+ console . log ( header ( "\nOverall Analysis:" ) ) ;
42
+ console . log ( `Shared Assets: ${ highlight ( analysis . analysis . sharedAssets . join ( ', ' ) ) } ` ) ;
43
+ console . log ( `Overall Sentiment: ${ formatSentiment ( analysis . analysis . overallSentiment ) } ` ) ;
44
+ console . log ( `Risk Analysis: ${ value ( analysis . analysis . riskAnalysis ) } ` ) ;
45
+ console . log ( `Trading Activity: ${ value ( analysis . analysis . tradingActivity ) } ` ) ;
46
+
47
+ } catch ( error ) {
48
+ console . error ( negative ( "Error running trader analysis:" ) , error ) ;
49
+ } finally {
50
+ api . disconnect ( ) ;
51
+ }
52
+ }
53
+
54
+ async function analyzeTradersData ( api : HyperliquidAPI , filter : LeaderboardFilter , sortBy : 'pnl' | 'roi' | 'vlm' | 'accountValue' = 'pnl' ) {
55
+ const leaderboard = await api . leaderboard . getLeaderboard ( ) ;
56
+ const filteredLeaderboard = await api . leaderboard . filterLeaderboard ( leaderboard , filter ) ;
57
+ const sortedLeaderboard = api . leaderboard . sortLeaderboard ( filteredLeaderboard , sortBy , filter . timeWindow ) ;
58
+ const topTraders = sortedLeaderboard . slice ( 0 , filter . maxAccounts ) ;
59
+
60
+ const extendedTraderInfo = await Promise . all (
61
+ topTraders . map ( trader => getExtendedTraderInfo ( api , trader , filter . timeWindow || 'allTime' ) )
62
+ ) ;
63
+
64
+ return {
65
+ topTraders : extendedTraderInfo ,
66
+ analysis : {
67
+ sharedAssets : findSharedAssets ( extendedTraderInfo . map ( info => info . openPositions ) ) ,
68
+ overallSentiment : calculateOverallSentiment ( extendedTraderInfo . map ( info => info . openPositions ) ) ,
69
+ riskAnalysis : analyzeRisk ( extendedTraderInfo . map ( info => info . openPositions ) ) ,
70
+ tradingActivity : analyzeTradingActivity ( extendedTraderInfo ) ,
71
+ } ,
72
+ } ;
73
+ }
74
+
75
+ async function getExtendedTraderInfo ( api : HyperliquidAPI , trader : LeaderboardEntry , timeWindow : TimeWindow ) : Promise < any > {
76
+ const openPositions = await api . leaderboard . getTraderOpenPositions ( trader . ethAddress ) ;
77
+ const tradeCount = await api . leaderboard . getTraderTradeCount ( trader . ethAddress , getStartTimeForWindow ( timeWindow ) , Date . now ( ) ) ;
78
+
79
+ return {
80
+ ...trader ,
81
+ openPositions,
82
+ totalOpenPositions : openPositions . perp . length + openPositions . spot . length ,
83
+ tradeCount : tradeCount . total ,
84
+ } ;
85
+ }
86
+
87
+ function findSharedAssets ( positions : Array < { perp : TraderPosition [ ] ; spot : TraderPosition [ ] } > ) : string [ ] {
88
+ const assetCounts : Record < string , number > = { } ;
89
+ positions . forEach ( traderPositions => {
90
+ [ ...traderPositions . perp , ...traderPositions . spot ] . forEach ( position => {
91
+ assetCounts [ position . asset ] = ( assetCounts [ position . asset ] || 0 ) + 1 ;
92
+ } ) ;
93
+ } ) ;
94
+ return Object . entries ( assetCounts )
95
+ . filter ( ( [ _ , count ] ) => count > 1 )
96
+ . map ( ( [ asset , _ ] ) => asset ) ;
97
+ }
98
+
99
+ function calculateOverallSentiment (
100
+ positions : Array < { perp : TraderPosition [ ] ; spot : TraderPosition [ ] } >
101
+ ) : 'bullish' | 'bearish' | 'neutral' {
102
+ let totalSentiment = 0 ;
103
+ let positionCount = 0 ;
104
+
105
+ positions . forEach ( traderPositions => {
106
+ traderPositions . perp . forEach ( position => {
107
+ totalSentiment += Math . sign ( position . size ) ;
108
+ positionCount ++ ;
109
+ } ) ;
110
+ } ) ;
111
+
112
+ const averageSentiment = totalSentiment / positionCount ;
113
+ if ( averageSentiment > 0.2 ) return 'bullish' ;
114
+ if ( averageSentiment < - 0.2 ) return 'bearish' ;
115
+ return 'neutral' ;
116
+ }
117
+
118
+ function analyzeRisk ( positions : Array < { perp : TraderPosition [ ] ; spot : TraderPosition [ ] } > ) : string {
119
+ let highLeverageCount = 0 ;
120
+ let totalPositions = 0 ;
121
+
122
+ positions . forEach ( traderPositions => {
123
+ traderPositions . perp . forEach ( position => {
124
+ if ( position . leverage > 10 ) highLeverageCount ++ ;
125
+ totalPositions ++ ;
126
+ } ) ;
127
+ } ) ;
128
+
129
+ const highLeverageRatio = highLeverageCount / totalPositions ;
130
+ if ( highLeverageRatio > 0.5 ) return 'High risk: Many positions use high leverage' ;
131
+ if ( highLeverageRatio > 0.2 ) return 'Moderate risk: Some positions use high leverage' ;
132
+ return 'Low risk: Most positions use conservative leverage' ;
133
+ }
134
+
135
+ function analyzeTradingActivity ( traders : any [ ] ) : string {
136
+ const avgTradeCount = traders . reduce ( ( sum , trader ) => sum + trader . tradeCount , 0 ) / traders . length ;
137
+ const avgOpenPositions = traders . reduce ( ( sum , trader ) => sum + trader . totalOpenPositions , 0 ) / traders . length ;
138
+
139
+ return `Average trade count: ${ avgTradeCount . toFixed ( 2 ) } . Average open positions: ${ avgOpenPositions . toFixed ( 2 ) } .` ;
140
+ }
141
+
142
+ function getStartTimeForWindow ( timeWindow : TimeWindow ) : number {
143
+ const now = Date . now ( ) ;
144
+ switch ( timeWindow ) {
145
+ case 'day' : return now - 24 * 60 * 60 * 1000 ;
146
+ case 'week' : return now - 7 * 24 * 60 * 60 * 1000 ;
147
+ case 'month' : return now - 30 * 24 * 60 * 60 * 1000 ;
148
+ default : return 0 ; // For 'allTime', return 0 to get all trades
149
+ }
150
+ }
151
+
152
+ function formatNumber ( value : string | number , decimals : number = 2 ) : string {
153
+ return parseFloat ( value . toString ( ) ) . toLocaleString ( 'en-US' , { maximumFractionDigits : decimals } ) ;
154
+ }
155
+
156
+ function formatPercentage ( value : string | number ) : string {
157
+ const percentage = ( parseFloat ( value . toString ( ) ) * 100 ) . toFixed ( 2 ) ;
158
+ return percentage . startsWith ( '-' ) ? negative ( `${ percentage } %` ) : positive ( `${ percentage } %` ) ;
159
+ }
160
+
161
+ function formatPnL ( value : string | number ) : string {
162
+ const formatted = `$${ formatNumber ( value ) } ` ;
163
+ return parseFloat ( value . toString ( ) ) >= 0 ? positive ( formatted ) : negative ( formatted ) ;
164
+ }
165
+
166
+ function formatSentiment ( sentiment : 'bullish' | 'bearish' | 'neutral' ) : string {
167
+ switch ( sentiment ) {
168
+ case 'bullish' : return positive ( sentiment ) ;
169
+ case 'bearish' : return negative ( sentiment ) ;
170
+ default : return value ( sentiment ) ;
171
+ }
172
+ }
173
+
174
+ runTraderAnalysis ( ) . then ( ( ) => {
175
+ console . log ( highlight ( "\nAnalysis complete. Exiting..." ) ) ;
176
+ process . exit ( 0 ) ;
177
+ } ) . catch ( error => {
178
+ console . error ( negative ( "Unhandled error:" ) , error ) ;
179
+ process . exit ( 1 ) ;
180
+ } ) ;
0 commit comments