test_hedging_manager.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. "use strict";
  2. /**
  3. * Unit tests for HedgingManager
  4. */
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const HedgingManager_1 = require("../../src/core/HedgingManager");
  7. // Mock dependencies
  8. jest.mock('../../src/utils/Logger');
  9. jest.mock('../../src/core/RiskManager');
  10. jest.mock('../../src/core/OrderCoordinator');
  11. jest.mock('../../src/core/HedgingStrategyEngine');
  12. jest.mock('../../src/core/HedgingConfigManager');
  13. describe('HedgingManager', () => {
  14. let hedgingManager;
  15. let mockConfig;
  16. beforeEach(() => {
  17. mockConfig = {
  18. maxConcurrentSessions: 5,
  19. sessionTimeout: 60000,
  20. riskCheckInterval: 1000
  21. };
  22. hedgingManager = new HedgingManager_1.HedgingManager(mockConfig);
  23. });
  24. afterEach(() => {
  25. jest.clearAllMocks();
  26. });
  27. describe('Initialization', () => {
  28. it('should initialize with correct configuration', async () => {
  29. await hedgingManager.initialize();
  30. const status = hedgingManager.getStatus();
  31. expect(status.isRunning).toBe(false);
  32. expect(status.totalSessions).toBe(0);
  33. });
  34. it('should throw error if already initialized', async () => {
  35. await hedgingManager.initialize();
  36. await expect(hedgingManager.initialize()).rejects.toThrow('HedgingManager is already initialized');
  37. });
  38. });
  39. describe('Session Management', () => {
  40. beforeEach(async () => {
  41. await hedgingManager.initialize();
  42. });
  43. it('should create a new hedging session', async () => {
  44. const sessionRequest = {
  45. strategy: {
  46. symbol: 'ETH/USD',
  47. volumeDistribution: 'equal',
  48. priceRange: { min: 0.001, max: 0.01 },
  49. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  50. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  51. orderTypes: { primary: 'limit', fallback: 'market' }
  52. },
  53. accounts: ['account1', 'account2'],
  54. duration: 300000
  55. };
  56. const session = await hedgingManager.createSession(sessionRequest);
  57. expect(session).toBeDefined();
  58. expect(session.id).toBeDefined();
  59. expect(session.status).toBe('created');
  60. expect(session.strategy.symbol).toBe('ETH/USD');
  61. expect(session.accounts).toEqual(['account1', 'account2']);
  62. });
  63. it('should throw error when creating session with invalid request', async () => {
  64. const invalidRequest = {
  65. strategy: {
  66. symbol: '', // Invalid empty symbol
  67. volumeDistribution: 'equal',
  68. priceRange: { min: 0.001, max: 0.01 },
  69. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  70. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  71. orderTypes: { primary: 'limit', fallback: 'market' }
  72. },
  73. accounts: [],
  74. duration: 300000
  75. };
  76. await expect(hedgingManager.createSession(invalidRequest)).rejects.toThrow();
  77. });
  78. it('should start a hedging session', async () => {
  79. const sessionRequest = {
  80. strategy: {
  81. symbol: 'ETH/USD',
  82. volumeDistribution: 'equal',
  83. priceRange: { min: 0.001, max: 0.01 },
  84. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  85. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  86. orderTypes: { primary: 'limit', fallback: 'market' }
  87. },
  88. accounts: ['account1', 'account2'],
  89. duration: 300000
  90. };
  91. const session = await hedgingManager.createSession(sessionRequest);
  92. await hedgingManager.startSession(session.id);
  93. const updatedSession = hedgingManager.getSession(session.id);
  94. expect(updatedSession?.status).toBe('running');
  95. });
  96. it('should pause a running session', async () => {
  97. const sessionRequest = {
  98. strategy: {
  99. symbol: 'ETH/USD',
  100. volumeDistribution: 'equal',
  101. priceRange: { min: 0.001, max: 0.01 },
  102. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  103. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  104. orderTypes: { primary: 'limit', fallback: 'market' }
  105. },
  106. accounts: ['account1', 'account2'],
  107. duration: 300000
  108. };
  109. const session = await hedgingManager.createSession(sessionRequest);
  110. await hedgingManager.startSession(session.id);
  111. await hedgingManager.pauseSession(session.id);
  112. const updatedSession = hedgingManager.getSession(session.id);
  113. expect(updatedSession?.status).toBe('paused');
  114. });
  115. it('should resume a paused session', async () => {
  116. const sessionRequest = {
  117. strategy: {
  118. symbol: 'ETH/USD',
  119. volumeDistribution: 'equal',
  120. priceRange: { min: 0.001, max: 0.01 },
  121. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  122. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  123. orderTypes: { primary: 'limit', fallback: 'market' }
  124. },
  125. accounts: ['account1', 'account2'],
  126. duration: 300000
  127. };
  128. const session = await hedgingManager.createSession(sessionRequest);
  129. await hedgingManager.startSession(session.id);
  130. await hedgingManager.pauseSession(session.id);
  131. await hedgingManager.resumeSession(session.id);
  132. const updatedSession = hedgingManager.getSession(session.id);
  133. expect(updatedSession?.status).toBe('running');
  134. });
  135. it('should stop a session', async () => {
  136. const sessionRequest = {
  137. strategy: {
  138. symbol: 'ETH/USD',
  139. volumeDistribution: 'equal',
  140. priceRange: { min: 0.001, max: 0.01 },
  141. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  142. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  143. orderTypes: { primary: 'limit', fallback: 'market' }
  144. },
  145. accounts: ['account1', 'account2'],
  146. duration: 300000
  147. };
  148. const session = await hedgingManager.createSession(sessionRequest);
  149. await hedgingManager.startSession(session.id);
  150. await hedgingManager.stopSession(session.id);
  151. const updatedSession = hedgingManager.getSession(session.id);
  152. expect(updatedSession?.status).toBe('stopped');
  153. });
  154. it('should throw error when operating on non-existent session', async () => {
  155. await expect(hedgingManager.startSession('non-existent')).rejects.toThrow();
  156. await expect(hedgingManager.pauseSession('non-existent')).rejects.toThrow();
  157. await expect(hedgingManager.resumeSession('non-existent')).rejects.toThrow();
  158. await expect(hedgingManager.stopSession('non-existent')).rejects.toThrow();
  159. });
  160. it('should throw error when starting already running session', async () => {
  161. const sessionRequest = {
  162. strategy: {
  163. symbol: 'ETH/USD',
  164. volumeDistribution: 'equal',
  165. priceRange: { min: 0.001, max: 0.01 },
  166. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  167. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  168. orderTypes: { primary: 'limit', fallback: 'market' }
  169. },
  170. accounts: ['account1', 'account2'],
  171. duration: 300000
  172. };
  173. const session = await hedgingManager.createSession(sessionRequest);
  174. await hedgingManager.startSession(session.id);
  175. await expect(hedgingManager.startSession(session.id)).rejects.toThrow();
  176. });
  177. });
  178. describe('Session Retrieval', () => {
  179. beforeEach(async () => {
  180. await hedgingManager.initialize();
  181. });
  182. it('should get all sessions', async () => {
  183. const sessionRequest = {
  184. strategy: {
  185. symbol: 'ETH/USD',
  186. volumeDistribution: 'equal',
  187. priceRange: { min: 0.001, max: 0.01 },
  188. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  189. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  190. orderTypes: { primary: 'limit', fallback: 'market' }
  191. },
  192. accounts: ['account1', 'account2'],
  193. duration: 300000
  194. };
  195. const session1 = await hedgingManager.createSession(sessionRequest);
  196. const session2 = await hedgingManager.createSession(sessionRequest);
  197. const allSessions = hedgingManager.getAllSessions();
  198. expect(allSessions).toHaveLength(2);
  199. expect(allSessions.map(s => s.id)).toContain(session1.id);
  200. expect(allSessions.map(s => s.id)).toContain(session2.id);
  201. });
  202. it('should get session by ID', async () => {
  203. const sessionRequest = {
  204. strategy: {
  205. symbol: 'ETH/USD',
  206. volumeDistribution: 'equal',
  207. priceRange: { min: 0.001, max: 0.01 },
  208. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  209. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  210. orderTypes: { primary: 'limit', fallback: 'market' }
  211. },
  212. accounts: ['account1', 'account2'],
  213. duration: 300000
  214. };
  215. const session = await hedgingManager.createSession(sessionRequest);
  216. const retrievedSession = hedgingManager.getSession(session.id);
  217. expect(retrievedSession).toBeDefined();
  218. expect(retrievedSession?.id).toBe(session.id);
  219. });
  220. it('should return null for non-existent session', () => {
  221. const session = hedgingManager.getSession('non-existent');
  222. expect(session).toBeNull();
  223. });
  224. });
  225. describe('Order Management', () => {
  226. beforeEach(async () => {
  227. await hedgingManager.initialize();
  228. });
  229. it('should get session orders', async () => {
  230. const sessionRequest = {
  231. strategy: {
  232. symbol: 'ETH/USD',
  233. volumeDistribution: 'equal',
  234. priceRange: { min: 0.001, max: 0.01 },
  235. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  236. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  237. orderTypes: { primary: 'limit', fallback: 'market' }
  238. },
  239. accounts: ['account1', 'account2'],
  240. duration: 300000
  241. };
  242. const session = await hedgingManager.createSession(sessionRequest);
  243. const orders = hedgingManager.getSessionOrders(session.id);
  244. expect(orders).toBeDefined();
  245. expect(Array.isArray(orders)).toBe(true);
  246. });
  247. it('should return empty array for non-existent session orders', () => {
  248. const orders = hedgingManager.getSessionOrders('non-existent');
  249. expect(orders).toEqual([]);
  250. });
  251. });
  252. describe('Risk Management', () => {
  253. beforeEach(async () => {
  254. await hedgingManager.initialize();
  255. });
  256. it('should get session risk status', async () => {
  257. const sessionRequest = {
  258. strategy: {
  259. symbol: 'ETH/USD',
  260. volumeDistribution: 'equal',
  261. priceRange: { min: 0.001, max: 0.01 },
  262. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  263. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  264. orderTypes: { primary: 'limit', fallback: 'market' }
  265. },
  266. accounts: ['account1', 'account2'],
  267. duration: 300000
  268. };
  269. const session = await hedgingManager.createSession(sessionRequest);
  270. const riskStatus = hedgingManager.getSessionRiskStatus(session.id);
  271. expect(riskStatus).toBeDefined();
  272. expect(riskStatus.overallRisk).toBeDefined();
  273. expect(riskStatus.activeBreaches).toBeDefined();
  274. expect(riskStatus.acknowledgedBreaches).toBeDefined();
  275. });
  276. it('should get session risk breaches', async () => {
  277. const sessionRequest = {
  278. strategy: {
  279. symbol: 'ETH/USD',
  280. volumeDistribution: 'equal',
  281. priceRange: { min: 0.001, max: 0.01 },
  282. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  283. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  284. orderTypes: { primary: 'limit', fallback: 'market' }
  285. },
  286. accounts: ['account1', 'account2'],
  287. duration: 300000
  288. };
  289. const session = await hedgingManager.createSession(sessionRequest);
  290. const breaches = hedgingManager.getSessionRiskBreaches(session.id);
  291. expect(breaches).toBeDefined();
  292. expect(Array.isArray(breaches)).toBe(true);
  293. });
  294. it('should acknowledge risk breach', async () => {
  295. const sessionRequest = {
  296. strategy: {
  297. symbol: 'ETH/USD',
  298. volumeDistribution: 'equal',
  299. priceRange: { min: 0.001, max: 0.01 },
  300. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  301. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  302. orderTypes: { primary: 'limit', fallback: 'market' }
  303. },
  304. accounts: ['account1', 'account2'],
  305. duration: 300000
  306. };
  307. const session = await hedgingManager.createSession(sessionRequest);
  308. const breaches = hedgingManager.getSessionRiskBreaches(session.id);
  309. if (breaches.length > 0) {
  310. const breach = breaches[0];
  311. await hedgingManager.acknowledgeRiskBreach(session.id, breach.id);
  312. const updatedBreaches = hedgingManager.getSessionRiskBreaches(session.id);
  313. const updatedBreach = updatedBreaches.find(b => b.id === breach.id);
  314. expect(updatedBreach?.acknowledged).toBe(true);
  315. }
  316. });
  317. it('should throw error when acknowledging non-existent breach', async () => {
  318. const sessionRequest = {
  319. strategy: {
  320. symbol: 'ETH/USD',
  321. volumeDistribution: 'equal',
  322. priceRange: { min: 0.001, max: 0.01 },
  323. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  324. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  325. orderTypes: { primary: 'limit', fallback: 'market' }
  326. },
  327. accounts: ['account1', 'account2'],
  328. duration: 300000
  329. };
  330. const session = await hedgingManager.createSession(sessionRequest);
  331. await expect(hedgingManager.acknowledgeRiskBreach(session.id, 'non-existent')).rejects.toThrow();
  332. });
  333. });
  334. describe('Lifecycle Management', () => {
  335. it('should start and stop the manager', async () => {
  336. await hedgingManager.initialize();
  337. await hedgingManager.start();
  338. const status = hedgingManager.getStatus();
  339. expect(status.isRunning).toBe(true);
  340. await hedgingManager.stop();
  341. const stoppedStatus = hedgingManager.getStatus();
  342. expect(stoppedStatus.isRunning).toBe(false);
  343. });
  344. it('should throw error when starting already running manager', async () => {
  345. await hedgingManager.initialize();
  346. await hedgingManager.start();
  347. await expect(hedgingManager.start()).rejects.toThrow();
  348. });
  349. it('should throw error when stopping non-running manager', async () => {
  350. await hedgingManager.initialize();
  351. await expect(hedgingManager.stop()).rejects.toThrow();
  352. });
  353. });
  354. describe('Status and Statistics', () => {
  355. beforeEach(async () => {
  356. await hedgingManager.initialize();
  357. });
  358. it('should return correct status', () => {
  359. const status = hedgingManager.getStatus();
  360. expect(status).toBeDefined();
  361. expect(typeof status.isRunning).toBe('boolean');
  362. expect(typeof status.totalSessions).toBe('number');
  363. expect(typeof status.activeSessions).toBe('number');
  364. expect(typeof status.pausedSessions).toBe('number');
  365. expect(typeof status.stoppedSessions).toBe('number');
  366. });
  367. it('should return correct statistics', () => {
  368. const stats = hedgingManager.getStatistics();
  369. expect(stats).toBeDefined();
  370. expect(typeof stats.totalSessions).toBe('number');
  371. expect(typeof stats.totalOrders).toBe('number');
  372. expect(typeof stats.totalVolume).toBe('number');
  373. expect(typeof stats.averageExecutionTime).toBe('number');
  374. expect(typeof stats.successRate).toBe('number');
  375. });
  376. });
  377. describe('Error Handling', () => {
  378. it('should handle initialization errors gracefully', async () => {
  379. const invalidConfig = {
  380. maxConcurrentSessions: -1, // Invalid negative value
  381. sessionTimeout: 60000,
  382. riskCheckInterval: 1000
  383. };
  384. const invalidManager = new HedgingManager_1.HedgingManager(invalidConfig);
  385. await expect(invalidManager.initialize()).rejects.toThrow();
  386. });
  387. it('should handle session creation errors gracefully', async () => {
  388. await hedgingManager.initialize();
  389. const invalidRequest = {
  390. strategy: null, // Invalid null strategy
  391. accounts: ['account1'],
  392. duration: 300000
  393. };
  394. await expect(hedgingManager.createSession(invalidRequest)).rejects.toThrow();
  395. });
  396. });
  397. describe('Event Emission', () => {
  398. beforeEach(async () => {
  399. await hedgingManager.initialize();
  400. });
  401. it('should emit session created event', async () => {
  402. const sessionRequest = {
  403. strategy: {
  404. symbol: 'ETH/USD',
  405. volumeDistribution: 'equal',
  406. priceRange: { min: 0.001, max: 0.01 },
  407. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  408. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  409. orderTypes: { primary: 'limit', fallback: 'market' }
  410. },
  411. accounts: ['account1', 'account2'],
  412. duration: 300000
  413. };
  414. const eventSpy = jest.fn();
  415. hedgingManager.on('sessionCreated', eventSpy);
  416. await hedgingManager.createSession(sessionRequest);
  417. expect(eventSpy).toHaveBeenCalled();
  418. });
  419. it('should emit session started event', async () => {
  420. const sessionRequest = {
  421. strategy: {
  422. symbol: 'ETH/USD',
  423. volumeDistribution: 'equal',
  424. priceRange: { min: 0.001, max: 0.01 },
  425. timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } },
  426. riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 },
  427. orderTypes: { primary: 'limit', fallback: 'market' }
  428. },
  429. accounts: ['account1', 'account2'],
  430. duration: 300000
  431. };
  432. const session = await hedgingManager.createSession(sessionRequest);
  433. const eventSpy = jest.fn();
  434. hedgingManager.on('sessionStarted', eventSpy);
  435. await hedgingManager.startSession(session.id);
  436. expect(eventSpy).toHaveBeenCalled();
  437. });
  438. });
  439. });
  440. //# sourceMappingURL=test_hedging_manager.js.map