1
- import { HyperliquidAPI } from "../../src/api" ;
2
-
3
- const api = new HyperliquidAPI ( ) ;
4
-
5
- // This will fetch from the API and cache the result
6
- const leaderboard1 = await api . leaderboard . getFilteredAndSortedLeaderboard ( {
7
- timeWindow : 'month' ,
8
- minAccountValue : 100_000 ,
9
- maxAccounts : 1
10
- } , 'roi' ) ;
11
-
12
- // This will use the cached data
13
- const leaderboard2 = await api . leaderboard . getFilteredAndSortedLeaderboard ( {
14
- timeWindow : 'week' ,
15
- minVolume : 1_000_000 ,
16
- maxAccounts : 1
17
- } , 'pnl' ) ;
18
-
19
- // If you need to clear the cache manually
20
- await api . leaderboard . clearCache ( ) ;
21
-
22
- // This will fetch from the API again and cache the new result
23
- const leaderboard3 = await api . leaderboard . getFilteredAndSortedLeaderboard ( {
24
- timeWindow : 'day' ,
25
- minRoi : 0.05 ,
26
- maxAccounts : 1
27
- } , 'accountValue' ) ;
28
-
29
- console . log ( JSON . stringify ( leaderboard1 , null , 2 ) ) ;
30
- console . log ( JSON . stringify ( leaderboard2 , null , 2 ) ) ;
31
- console . log ( JSON . stringify ( leaderboard3 , null , 2 ) ) ;
1
+ import { HyperliquidAPI } from "../../src" ;
2
+ import type { LeaderboardFilter , TimeWindow , TraderPosition } from "../../src" ;
3
+ import { Color , t } from "tasai" ;
4
+ const highlight = t . bold . cyan . toFunction ( ) ;
5
+ const header = t . bold . underline . magenta . toFunction ( ) ;
6
+ const subHeader = t . bold . yellow . toFunction ( ) ;
7
+ const positive = t . green . toFunction ( ) ;
8
+ const negative = t . red . toFunction ( ) ;
9
+ const ticker = t . bold . magenta . toFunction ( ) ;
10
+ const value = t . bold . white . toFunction ( ) ;
11
+ const divider = "─" . repeat ( 50 ) ;
12
+
13
+ const leverageColor = ( leverage : number ) => {
14
+ if ( leverage < 3 ) return t . green . toFunction ( ) ;
15
+ if ( leverage < 10 ) return t . fromColor ( Color . ORANGE ) . toFunction ( ) ; // Orange
16
+ return t . brightRed . toFunction ( ) ;
17
+ } ;
18
+
19
+
20
+ async function runLeaderboardAnalysis ( ) {
21
+ const api = new HyperliquidAPI ( ) ;
22
+
23
+ const filter : LeaderboardFilter = {
24
+ timeWindow : 'month' as TimeWindow ,
25
+ minAccountValue : 100_000 ,
26
+ minVolume : 1_000_000 ,
27
+ maxVolume : 100_000_000 ,
28
+ minPnL : 10_000 ,
29
+ minRoi : 0.5 ,
30
+ maxAccounts : 3
31
+ } ;
32
+
33
+ try {
34
+ console . log ( header ( "Fetching leaderboard data..." ) ) ;
35
+ const leaderboard = await api . leaderboard . getLeaderboard ( ) ;
36
+ console . log ( highlight ( `Total traders: ${ leaderboard . leaderboardRows . length } ` ) ) ;
37
+
38
+ console . log ( header ( "\nFiltering leaderboard, this may take a while..." ) ) ;
39
+ const filteredLeaderboard = await api . leaderboard . filterLeaderboard ( leaderboard , filter ) ;
40
+
41
+ console . log ( header ( "\nSorting leaderboard..." ) ) ;
42
+ const sortedLeaderboard = api . leaderboard . sortLeaderboard ( filteredLeaderboard , 'pnl' , filter . timeWindow ) ;
43
+ const topTraders = sortedLeaderboard . slice ( 0 , filter . maxAccounts ) ;
44
+
45
+ if ( topTraders . length === 0 ) {
46
+ console . log ( negative ( "No traders found matching the specified criteria." ) ) ;
47
+ } else {
48
+ console . log ( header ( "\nTop Traders:" ) ) ;
49
+ for ( const [ index , trader ] of topTraders . entries ( ) ) {
50
+ const performance = trader . windowPerformances . find ( ( [ window ] ) => window === filter . timeWindow ) ?. [ 1 ] ;
51
+
52
+ console . log ( divider ) ;
53
+ console . log ( subHeader ( `\n#${ index + 1 } :` ) ) ;
54
+ console . log ( highlight ( `Address: ${ trader . ethAddress } ` ) ) ;
55
+ console . log ( `Account Value: ${ value ( '$' + formatNumber ( trader . accountValue ) ) } ` ) ;
56
+ console . log ( `PnL: ${ formatPnL ( performance ?. pnl ) } ` ) ;
57
+ console . log ( `ROI: ${ formatPercentage ( performance ?. roi ) } ` ) ;
58
+ console . log ( `Volume: ${ value ( '$' + formatNumber ( performance ?. vlm ) ) } ` ) ;
59
+
60
+ // Fetch and display current positions
61
+ const positions = await api . leaderboard . getTraderOpenPositions ( trader . ethAddress ) ;
62
+ console . log ( subHeader ( "\nCurrent Positions:" ) ) ;
63
+ if ( positions . perp . length > 0 || positions . spot . length > 0 ) {
64
+ if ( positions . perp . length > 0 ) {
65
+ console . log ( highlight ( " Perpetual:" ) ) ;
66
+ positions . perp . forEach ( displayPosition ) ;
67
+ }
68
+ if ( positions . spot . length > 0 ) {
69
+ console . log ( highlight ( " Spot:" ) ) ;
70
+ positions . spot . forEach ( displayPosition ) ;
71
+ }
72
+ } else {
73
+ console . log ( " No open positions" ) ;
74
+ }
75
+
76
+ // Fetch and display best trade
77
+ const bestTrade = await getBestTrade ( api , trader . ethAddress , filter . timeWindow ! ) ;
78
+ if ( bestTrade ) {
79
+ console . log ( subHeader ( "\nBest Trade:" ) ) ;
80
+ console . log ( ` Asset: ${ ticker ( bestTrade . coin ) } ` ) ;
81
+ console . log ( ` Side: ${ bestTrade . side === 'buy' ? positive ( bestTrade . side ) : negative ( bestTrade . side ) } ` ) ;
82
+ console . log ( ` Price: ${ value ( '$' + formatNumber ( bestTrade . px ) ) } ` ) ;
83
+ console . log ( ` Size: ${ value ( formatNumber ( bestTrade . sz , 4 ) ) } ` ) ;
84
+ console . log ( ` PnL: ${ formatPnL ( bestTrade . closedPnl ) } ` ) ;
85
+ console . log ( ` Time: ${ new Date ( bestTrade . time ) . toLocaleString ( ) } ` ) ;
86
+ } else {
87
+ console . log ( negative ( "\nNo trades found for this period" ) ) ;
88
+ }
89
+ }
90
+ }
91
+ } catch ( error ) {
92
+ console . error ( negative ( "Error running leaderboard analysis:" ) , error ) ;
93
+ } finally {
94
+ api . disconnect ( ) ;
95
+ }
96
+ }
97
+
98
+ function displayPosition ( position : TraderPosition ) {
99
+ console . log ( ` ${ ticker ( position . asset ) } : ${ value ( formatNumber ( position . size , 4 ) ) } @ $${ value ( formatNumber ( position . entryPrice ) ) } ` ) ;
100
+ if ( position . unrealizedPnl ) {
101
+ console . log ( ` Unrealized PnL: ${ formatPnL ( position . unrealizedPnl . toString ( ) ) } ` ) ;
102
+ }
103
+ if ( position . leverage && position . leverage !== 1 ) {
104
+ const leverageStyled = leverageColor ( position . leverage ) ( `${ position . leverage } x` ) ;
105
+ console . log ( ` Leverage: ${ leverageStyled } ` ) ;
106
+ }
107
+ }
108
+
109
+ async function getBestTrade ( api : HyperliquidAPI , trader : string , timeWindow : TimeWindow ) {
110
+ const endTime = Date . now ( ) ;
111
+ const startTime = getStartTimeForWindow ( timeWindow ) ;
112
+ const trades = await api . info . getUserFillsByTime ( trader , startTime , endTime ) ;
113
+ return trades . reduce ( ( best , current ) => {
114
+ if ( ! best || parseFloat ( current . closedPnl ) > parseFloat ( best . closedPnl ) ) {
115
+ return current ;
116
+ }
117
+ return best ;
118
+ } , null as any ) ;
119
+ }
120
+
121
+ function getStartTimeForWindow ( timeWindow : TimeWindow ) : number {
122
+ const now = Date . now ( ) ;
123
+ switch ( timeWindow ) {
124
+ case 'day' : return now - 24 * 60 * 60 * 1000 ;
125
+ case 'week' : return now - 7 * 24 * 60 * 60 * 1000 ;
126
+ case 'month' : return now - 30 * 24 * 60 * 60 * 1000 ;
127
+ default : return 0 ; // For 'allTime', return 0 to get all trades
128
+ }
129
+ }
130
+
131
+ function formatNumber ( value : string | number | undefined , decimals : number = 2 ) : string {
132
+ return parseFloat ( value ?. toString ( ) || '0' ) . toLocaleString ( 'en-US' , { maximumFractionDigits : decimals } ) ;
133
+ }
134
+
135
+ function formatPercentage ( value : string | number | undefined ) : string {
136
+ const percentage = ( parseFloat ( value ?. toString ( ) || '0' ) * 100 ) . toFixed ( 2 ) ;
137
+ return percentage . startsWith ( '-' ) ? negative ( `${ percentage } %` ) : positive ( `${ percentage } %` ) ;
138
+ }
139
+
140
+ function formatPnL ( value : string | number | undefined ) : string {
141
+ const formatted = `$${ formatNumber ( value ) } ` ;
142
+ return parseFloat ( value ?. toString ( ) || '0' ) >= 0 ? positive ( formatted ) : negative ( formatted ) ;
143
+ }
144
+
145
+ runLeaderboardAnalysis ( ) . then ( ( ) => {
146
+ console . log ( highlight ( "\nAnalysis complete. Exiting..." ) ) ;
147
+ process . exit ( 0 ) ;
148
+ } ) . catch ( error => {
149
+ console . error ( negative ( "Unhandled error:" ) , error ) ;
150
+ process . exit ( 1 ) ;
151
+ } ) ;
0 commit comments