HedgingAccountPool.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /**
  2. * Hedging Account Pool Implementation
  3. *
  4. * Manages pools of accounts for hedging operations across multiple platforms.
  5. * Provides load balancing, failover, and health monitoring for Delta-neutral strategies.
  6. */
  7. import { Platform, Account } from '@/types/credential'
  8. import { AccountStatus } from './AccountStatus'
  9. // ============================================================================
  10. // Types and Interfaces
  11. // ============================================================================
  12. export interface HedgingAccountPoolConfig {
  13. /**
  14. * Primary accounts for normal hedging operations
  15. */
  16. primaryAccounts: string[]
  17. /**
  18. * Backup accounts for failover scenarios
  19. */
  20. backupAccounts: string[]
  21. /**
  22. * Test accounts for development and testing
  23. */
  24. testAccounts: string[]
  25. /**
  26. * Load balancing strategy
  27. */
  28. loadBalanceStrategy: LoadBalanceStrategy
  29. /**
  30. * Health check configuration
  31. */
  32. healthCheck: {
  33. enabled: boolean
  34. intervalMs: number
  35. failureThreshold: number
  36. }
  37. /**
  38. * Failover configuration
  39. */
  40. failover: {
  41. enabled: boolean
  42. autoFailoverTimeoutMs: number
  43. maxFailoverAttempts: number
  44. }
  45. }
  46. export enum LoadBalanceStrategy {
  47. ROUND_ROBIN = 'round-robin',
  48. RANDOM = 'random',
  49. WEIGHTED = 'weighted',
  50. LEAST_USED = 'least-used'
  51. }
  52. export interface AccountPoolStatus {
  53. platform: Platform
  54. totalAccounts: number
  55. activeAccounts: number
  56. failedAccounts: number
  57. lastHealthCheck: Date
  58. loadBalanceIndex: number
  59. }
  60. export interface AccountSelection {
  61. accountId: string
  62. platform: Platform
  63. selectionReason: string
  64. isFailover: boolean
  65. poolStatus: AccountPoolStatus
  66. }
  67. // ============================================================================
  68. // Main Implementation
  69. // ============================================================================
  70. export class HedgingAccountPool {
  71. private config: HedgingAccountPoolConfig
  72. private accountStatuses: Map<string, AccountStatus> = new Map()
  73. private loadBalanceIndexes: Map<Platform, number> = new Map()
  74. private healthCheckInterval?: NodeJS.Timeout
  75. private accountUsageStats: Map<string, number> = new Map()
  76. constructor(config: HedgingAccountPoolConfig) {
  77. this.config = config
  78. this.initializeLoadBalanceIndexes()
  79. if (config.healthCheck.enabled) {
  80. this.startHealthChecking()
  81. }
  82. }
  83. // ============================================================================
  84. // Account Selection for Hedging Operations
  85. // ============================================================================
  86. /**
  87. * Select best account for hedging operation
  88. */
  89. async selectAccount(platform: Platform, criteria?: AccountSelectionCriteria): Promise<AccountSelection> {
  90. const availableAccounts = this.getAvailableAccounts(platform)
  91. if (availableAccounts.length === 0) {
  92. throw new Error(`No available accounts for platform: ${platform}`)
  93. }
  94. let selectedAccountId: string
  95. let isFailover = false
  96. let selectionReason: string
  97. // Try primary accounts first
  98. const primaryAccounts = availableAccounts.filter(id =>
  99. this.config.primaryAccounts.includes(id)
  100. )
  101. if (primaryAccounts.length > 0) {
  102. selectedAccountId = this.selectFromPool(primaryAccounts, platform)
  103. selectionReason = `Primary account selected via ${this.config.loadBalanceStrategy}`
  104. } else {
  105. // Failover to backup accounts
  106. const backupAccounts = availableAccounts.filter(id =>
  107. this.config.backupAccounts.includes(id)
  108. )
  109. if (backupAccounts.length > 0) {
  110. selectedAccountId = this.selectFromPool(backupAccounts, platform)
  111. selectionReason = `Failover to backup account via ${this.config.loadBalanceStrategy}`
  112. isFailover = true
  113. } else {
  114. throw new Error(`No primary or backup accounts available for platform: ${platform}`)
  115. }
  116. }
  117. // Update usage statistics
  118. const currentUsage = this.accountUsageStats.get(selectedAccountId) || 0
  119. this.accountUsageStats.set(selectedAccountId, currentUsage + 1)
  120. return {
  121. accountId: selectedAccountId,
  122. platform,
  123. selectionReason,
  124. isFailover,
  125. poolStatus: this.getPoolStatus(platform)
  126. }
  127. }
  128. /**
  129. * Select multiple accounts for distributed hedging
  130. */
  131. async selectMultipleAccounts(
  132. platform: Platform,
  133. count: number,
  134. criteria?: AccountSelectionCriteria
  135. ): Promise<AccountSelection[]> {
  136. const availableAccounts = this.getAvailableAccounts(platform)
  137. if (availableAccounts.length < count) {
  138. throw new Error(`Insufficient accounts available. Requested: ${count}, Available: ${availableAccounts.length}`)
  139. }
  140. const selections: AccountSelection[] = []
  141. const usedAccounts = new Set<string>()
  142. for (let i = 0; i < count; i++) {
  143. const availableForSelection = availableAccounts.filter(id => !usedAccounts.has(id))
  144. if (availableForSelection.length === 0) {
  145. break
  146. }
  147. const accountId = this.selectFromPool(availableForSelection, platform)
  148. usedAccounts.add(accountId)
  149. const isPrimary = this.config.primaryAccounts.includes(accountId)
  150. selections.push({
  151. accountId,
  152. platform,
  153. selectionReason: `Multi-account selection ${i + 1}/${count} - ${isPrimary ? 'primary' : 'backup'}`,
  154. isFailover: !isPrimary,
  155. poolStatus: this.getPoolStatus(platform)
  156. })
  157. // Update usage statistics
  158. const currentUsage = this.accountUsageStats.get(accountId) || 0
  159. this.accountUsageStats.set(accountId, currentUsage + 1)
  160. }
  161. return selections
  162. }
  163. // ============================================================================
  164. // Account Health Management
  165. // ============================================================================
  166. /**
  167. * Register account status
  168. */
  169. registerAccount(accountId: string, platform: Platform): void {
  170. if (!this.accountStatuses.has(accountId)) {
  171. this.accountStatuses.set(accountId, new AccountStatus(accountId, platform))
  172. }
  173. }
  174. /**
  175. * Update account health status
  176. */
  177. updateAccountHealth(accountId: string, isHealthy: boolean, details?: any): void {
  178. const status = this.accountStatuses.get(accountId)
  179. if (status) {
  180. status.updateHealth(isHealthy, details)
  181. }
  182. }
  183. /**
  184. * Mark account as failed
  185. */
  186. markAccountFailed(accountId: string, error: Error): void {
  187. const status = this.accountStatuses.get(accountId)
  188. if (status) {
  189. status.recordFailure(error)
  190. }
  191. }
  192. /**
  193. * Mark account operation as successful
  194. */
  195. markAccountSuccess(accountId: string): void {
  196. const status = this.accountStatuses.get(accountId)
  197. if (status) {
  198. status.recordSuccess()
  199. }
  200. }
  201. // ============================================================================
  202. // Pool Management
  203. // ============================================================================
  204. /**
  205. * Get pool status for platform
  206. */
  207. getPoolStatus(platform: Platform): AccountPoolStatus {
  208. const allAccounts = [...this.config.primaryAccounts, ...this.config.backupAccounts]
  209. .filter(accountId => {
  210. const status = this.accountStatuses.get(accountId)
  211. return status && status.platform === platform
  212. })
  213. const activeAccounts = allAccounts.filter(accountId => {
  214. const status = this.accountStatuses.get(accountId)
  215. return status && status.isActive()
  216. })
  217. const failedAccounts = allAccounts.filter(accountId => {
  218. const status = this.accountStatuses.get(accountId)
  219. return status && !status.isActive()
  220. })
  221. return {
  222. platform,
  223. totalAccounts: allAccounts.length,
  224. activeAccounts: activeAccounts.length,
  225. failedAccounts: failedAccounts.length,
  226. lastHealthCheck: new Date(),
  227. loadBalanceIndex: this.loadBalanceIndexes.get(platform) || 0
  228. }
  229. }
  230. /**
  231. * Get detailed pool statistics
  232. */
  233. getPoolStatistics(): PoolStatistics {
  234. const platforms = [Platform.PACIFICA, Platform.ASTER, Platform.BINANCE]
  235. const platformStats: Record<Platform, AccountPoolStatus> = {} as any
  236. platforms.forEach(platform => {
  237. platformStats[platform] = this.getPoolStatus(platform)
  238. })
  239. const totalUsage = Array.from(this.accountUsageStats.values()).reduce((sum, usage) => sum + usage, 0)
  240. return {
  241. platformStats,
  242. loadBalanceStrategy: this.config.loadBalanceStrategy,
  243. totalUsage,
  244. accountUsageStats: new Map(this.accountUsageStats),
  245. healthCheckEnabled: this.config.healthCheck.enabled,
  246. failoverEnabled: this.config.failover.enabled
  247. }
  248. }
  249. // ============================================================================
  250. // Private Helper Methods
  251. // ============================================================================
  252. private getAvailableAccounts(platform: Platform): string[] {
  253. const allAccounts = [...this.config.primaryAccounts, ...this.config.backupAccounts]
  254. return allAccounts.filter(accountId => {
  255. const status = this.accountStatuses.get(accountId)
  256. return status && status.platform === platform && status.isActive()
  257. })
  258. }
  259. private selectFromPool(accountIds: string[], platform: Platform): string {
  260. if (accountIds.length === 0) {
  261. throw new Error('No accounts available for selection')
  262. }
  263. if (accountIds.length === 1) {
  264. return accountIds[0]
  265. }
  266. switch (this.config.loadBalanceStrategy) {
  267. case LoadBalanceStrategy.ROUND_ROBIN:
  268. return this.selectRoundRobin(accountIds, platform)
  269. case LoadBalanceStrategy.RANDOM:
  270. return this.selectRandom(accountIds)
  271. case LoadBalanceStrategy.WEIGHTED:
  272. return this.selectWeighted(accountIds)
  273. case LoadBalanceStrategy.LEAST_USED:
  274. return this.selectLeastUsed(accountIds)
  275. default:
  276. return this.selectRoundRobin(accountIds, platform)
  277. }
  278. }
  279. private selectRoundRobin(accountIds: string[], platform: Platform): string {
  280. const currentIndex = this.loadBalanceIndexes.get(platform) || 0
  281. const selectedIndex = currentIndex % accountIds.length
  282. this.loadBalanceIndexes.set(platform, selectedIndex + 1)
  283. return accountIds[selectedIndex]
  284. }
  285. private selectRandom(accountIds: string[]): string {
  286. const randomIndex = Math.floor(Math.random() * accountIds.length)
  287. return accountIds[randomIndex]
  288. }
  289. private selectWeighted(accountIds: string[]): string {
  290. // Simple weighted selection based on inverse failure rate
  291. const weights = accountIds.map(accountId => {
  292. const status = this.accountStatuses.get(accountId)
  293. const failureRate = status ? status.getFailureRate() : 0
  294. return Math.max(0.1, 1 - failureRate) // Minimum weight of 0.1
  295. })
  296. const totalWeight = weights.reduce((sum, weight) => sum + weight, 0)
  297. let random = Math.random() * totalWeight
  298. for (let i = 0; i < accountIds.length; i++) {
  299. random -= weights[i]
  300. if (random <= 0) {
  301. return accountIds[i]
  302. }
  303. }
  304. return accountIds[accountIds.length - 1]
  305. }
  306. private selectLeastUsed(accountIds: string[]): string {
  307. let leastUsedAccount = accountIds[0]
  308. let minUsage = this.accountUsageStats.get(leastUsedAccount) || 0
  309. for (const accountId of accountIds) {
  310. const usage = this.accountUsageStats.get(accountId) || 0
  311. if (usage < minUsage) {
  312. minUsage = usage
  313. leastUsedAccount = accountId
  314. }
  315. }
  316. return leastUsedAccount
  317. }
  318. private initializeLoadBalanceIndexes(): void {
  319. this.loadBalanceIndexes.set(Platform.PACIFICA, 0)
  320. this.loadBalanceIndexes.set(Platform.ASTER, 0)
  321. this.loadBalanceIndexes.set(Platform.BINANCE, 0)
  322. }
  323. private startHealthChecking(): void {
  324. this.healthCheckInterval = setInterval(() => {
  325. this.performHealthCheck()
  326. }, this.config.healthCheck.intervalMs)
  327. }
  328. private async performHealthCheck(): Promise<void> {
  329. const allAccounts = [...this.config.primaryAccounts, ...this.config.backupAccounts]
  330. for (const accountId of allAccounts) {
  331. const status = this.accountStatuses.get(accountId)
  332. if (status) {
  333. // Simple health check - could be enhanced with actual API calls
  334. const isHealthy = status.getFailureRate() < 0.5 // Less than 50% failure rate
  335. status.updateHealth(isHealthy, { lastHealthCheck: new Date() })
  336. }
  337. }
  338. }
  339. /**
  340. * Cleanup resources
  341. */
  342. destroy(): void {
  343. if (this.healthCheckInterval) {
  344. clearInterval(this.healthCheckInterval)
  345. this.healthCheckInterval = undefined
  346. }
  347. }
  348. }
  349. // ============================================================================
  350. // Supporting Types
  351. // ============================================================================
  352. export interface AccountSelectionCriteria {
  353. preferPrimary?: boolean
  354. excludeAccounts?: string[]
  355. minHealthScore?: number
  356. maxUsageCount?: number
  357. }
  358. export interface PoolStatistics {
  359. platformStats: Record<Platform, AccountPoolStatus>
  360. loadBalanceStrategy: LoadBalanceStrategy
  361. totalUsage: number
  362. accountUsageStats: Map<string, number>
  363. healthCheckEnabled: boolean
  364. failoverEnabled: boolean
  365. }