run-delta-neutral-simple.ts 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043
  1. #!/usr/bin/env tsx
  2. /**
  3. * 简洁版Delta中性刷量策略
  4. * 只显示关键信息,日志简洁清晰
  5. */
  6. import { DeltaNeutralVolumeStrategy, DeltaNeutralConfig } from '../src/strategies/DeltaNeutralVolumeStrategy';
  7. import { DeltaNeutralConfigManager } from '../src/strategies/DeltaNeutralConfig';
  8. import { PacificaSigningClient } from '../src/services/PacificaSigningClient';
  9. import { RiskManager, RiskManagerConfig } from '../src/core/RiskManager';
  10. import { MarketDataManager } from '../src/services/MarketDataManager';
  11. import { OrderCleanupService, CleanupOptions } from '../src/services/OrderCleanupService';
  12. import { DynamicPositionManager, PositionAdjustmentConfig } from '../src/services/DynamicPositionManager';
  13. import { Account } from '../src/models/Account';
  14. import { readFileSync, writeFileSync, existsSync } from 'fs';
  15. import { v4 as uuidv4 } from 'uuid';
  16. interface OrderLifecycle {
  17. orderId: string;
  18. clientOrderId: string;
  19. accountId: string; // 记录创建订单的账户ID
  20. symbol: string;
  21. side: 'bid' | 'ask';
  22. amount: string;
  23. price: string;
  24. status: 'pending' | 'filled' | 'cancelled' | 'expired';
  25. createdAt: number;
  26. lastChecked: number;
  27. checkCount: number;
  28. maxChecks: number;
  29. }
  30. interface SimpleStats {
  31. totalOrders: number;
  32. filledOrders: number;
  33. cancelledOrders: number;
  34. expiredOrders: number;
  35. totalVolume: number;
  36. totalPnL: number;
  37. startTime: number;
  38. lastUpdateTime: number;
  39. consecutiveErrors: number;
  40. maxConsecutiveErrors: number;
  41. apiCallCount: number;
  42. lastApiError: number;
  43. lastSuccessfulTrade: number;
  44. retryCount: number;
  45. totalRetries: number;
  46. }
  47. class SimpleDeltaNeutralStrategy extends DeltaNeutralVolumeStrategy {
  48. private orderLifecycle: Map<string, OrderLifecycle> = new Map();
  49. private lastApiCall: number = 0;
  50. private apiCallInterval: number = 2000; // 2秒间隔
  51. private orderCheckInterval: NodeJS.Timeout | null = null;
  52. private volumeTradingInterval: NodeJS.Timeout | null = null;
  53. private deltaMonitoringInterval: NodeJS.Timeout | null = null;
  54. private statusDisplayInterval: NodeJS.Timeout | null = null;
  55. private statsSaveInterval: NodeJS.Timeout | null = null;
  56. private healthCheckInterval: NodeJS.Timeout | null = null;
  57. // 账户配对 - Delta中性核心机制
  58. private accountPairs: Array<[Account, Account]> = [];
  59. // 多账户客户端映射 - 每个账户一个客户端
  60. private clientMap: Map<string, PacificaSigningClient> = new Map();
  61. // 账户列表(保存引用用于动态仓位管理)
  62. private strategyAccounts: Account[] = [];
  63. // 风险管理器
  64. private riskManager: RiskManager | null = null;
  65. private sessionId: string;
  66. // 市场数据管理器 - WebSocket实时行情
  67. private marketDataManager: MarketDataManager | null = null;
  68. private currentBTCPrice: number = 0;
  69. private isPriceReady: boolean = false;
  70. // 动态仓位管理器 - 保证金使用率过高时自动减仓
  71. private dynamicPositionManager: DynamicPositionManager | null = null;
  72. // 简洁版统计信息
  73. private stats: SimpleStats = {
  74. totalOrders: 0,
  75. filledOrders: 0,
  76. cancelledOrders: 0,
  77. expiredOrders: 0,
  78. totalVolume: 0,
  79. totalPnL: 0,
  80. startTime: Date.now(),
  81. lastUpdateTime: Date.now(),
  82. consecutiveErrors: 0,
  83. maxConsecutiveErrors: 3,
  84. apiCallCount: 0,
  85. lastApiError: 0,
  86. lastSuccessfulTrade: 0,
  87. retryCount: 0,
  88. totalRetries: 0
  89. };
  90. // 账户盈亏跟踪
  91. private accountPnL: Map<string, {
  92. totalProfit: number; // 累计利润(USDC)
  93. totalVolume: number; // 累计成交量(BTC)
  94. winTrades: number; // 盈利交易次数
  95. lossTrades: number; // 亏损交易次数
  96. lastUpdateTime: number; // 最后更新时间
  97. initialBalance: number; // 初始余额
  98. currentBalance: number; // 当前余额
  99. direction: 'long' | 'short' | 'neutral'; // 主要方向
  100. performance: number; // 绩效得分(用于动态角色分配)
  101. }> = new Map();
  102. // 市场趋势跟踪
  103. private marketTrend: 'bullish' | 'bearish' | 'neutral' = 'neutral';
  104. private priceHistory: number[] = []; // 保存最近100个价格
  105. private buyRatio: number = 0.5; // 买单比例
  106. private sellRatio: number = 0.5; // 卖单比例
  107. private lastBTCPrice: number = 0; // 最新BTC价格
  108. // 配置参数
  109. private readonly STATS_SAVE_INTERVAL = 600000; // 10分钟保存一次统计
  110. private readonly HEALTH_CHECK_INTERVAL = 300000; // 5分钟健康检查
  111. private readonly MAX_RUNTIME = 6 * 60 * 60 * 1000; // 6小时最大运行时间
  112. private readonly ERROR_RECOVERY_DELAY = 60000; // 60秒错误恢复延迟
  113. private readonly STATS_FILE = './data/simple-stats.json';
  114. private readonly TRADING_PAUSE_DURATION = 120000; // 120秒交易暂停
  115. private readonly MAX_RETRY_ATTEMPTS = 2; // 最大重试次数
  116. private readonly RETRY_DELAY = 10000; // 重试延迟
  117. // 状态控制
  118. private isTradingPaused = false;
  119. private lastTradingPause = 0;
  120. private isInitialized = false;
  121. constructor(
  122. config: DeltaNeutralConfig,
  123. pacificaClient: PacificaSigningClient,
  124. accounts: Account[]
  125. ) {
  126. super(config, pacificaClient, accounts);
  127. this.sessionId = `session_${Date.now()}`;
  128. this.strategyAccounts = accounts; // 保存账户引用
  129. // 为每个账户创建独立的客户端
  130. accounts.forEach(account => {
  131. const client = new PacificaSigningClient(account.getPrivateKey());
  132. this.clientMap.set(account.getId(), client);
  133. });
  134. this.loadStats();
  135. this.initializeRiskManager(config);
  136. this.initializeMarketDataManager();
  137. this.initializeDynamicPositionManager();
  138. }
  139. /**
  140. * 初始化风险管理器
  141. */
  142. private initializeRiskManager(config: DeltaNeutralConfig): void {
  143. const riskConfig: RiskManagerConfig = {
  144. maxTotalVolume: config.maxVolumePositionSize * 10, // 最大总量
  145. maxNetExposure: config.maxDeltaDeviation, // 最大净敞口
  146. maxDailyLoss: config.stopLossThreshold, // 最大每日亏损
  147. positionLimit: config.maxVolumePositionSize, // 仓位限制
  148. stopLossThreshold: config.stopLossThreshold, // 止损阈值
  149. monitoringInterval: 60000 // 60秒监控间隔
  150. };
  151. this.riskManager = new RiskManager(riskConfig);
  152. this.riskManager.on('breach_detected', (breach) => {
  153. this.log(`风险告警: ${breach.type} - ${breach.severity}`, 'error');
  154. if (breach.severity === 'critical') {
  155. this.pauseTrading();
  156. }
  157. });
  158. }
  159. /**
  160. * 初始化市场数据管理器
  161. * 使用WebSocket订阅实时行情,替代REST轮询
  162. * 传入signing client以支持认证订阅(如account balance)
  163. */
  164. private initializeMarketDataManager(): void {
  165. this.marketDataManager = new MarketDataManager(this.pacificaClient);
  166. // 监听实时价格更新
  167. this.marketDataManager.on('price_update', (priceData) => {
  168. if (priceData.symbol === 'BTC') {
  169. this.currentBTCPrice = parseFloat(priceData.mark);
  170. this.lastBTCPrice = this.currentBTCPrice; // 同步更新最新价格
  171. if (!this.isPriceReady) {
  172. this.isPriceReady = true;
  173. this.log(`BTC实时价格已就绪: $${this.currentBTCPrice.toFixed(2)}`, 'success');
  174. }
  175. }
  176. });
  177. // 监听WebSocket连接状态
  178. this.marketDataManager.on('connected', () => {
  179. this.log('WebSocket市场数据已连接', 'success');
  180. });
  181. this.marketDataManager.on('disconnected', () => {
  182. this.log('WebSocket市场数据断开,切换到REST轮询', 'warning');
  183. this.isPriceReady = false;
  184. });
  185. }
  186. /**
  187. * 初始化动态仓位管理器
  188. * 当保证金使用率过高时自动减仓
  189. */
  190. private initializeDynamicPositionManager(): void {
  191. // 从配置文件读取设置
  192. let config: PositionAdjustmentConfig;
  193. try {
  194. const strategyConfig = JSON.parse(readFileSync('./config/trading-strategy.json', 'utf-8'));
  195. const posConfig = strategyConfig.dynamicPositionAdjustment;
  196. config = {
  197. enabled: posConfig.enabled ?? true,
  198. maxMarginUsageThreshold: posConfig.maxMarginUsageThreshold ?? 0.85,
  199. targetMarginRelease: posConfig.targetMarginRelease ?? 0.2,
  200. minPositionRatio: posConfig.minPositionRatio ?? 0.3,
  201. checkInterval: posConfig.checkInterval ?? 10000
  202. };
  203. } catch (error) {
  204. // 使用默认配置
  205. config = {
  206. enabled: true,
  207. maxMarginUsageThreshold: 0.85,
  208. targetMarginRelease: 0.2,
  209. minPositionRatio: 0.3,
  210. checkInterval: 10000
  211. };
  212. }
  213. this.dynamicPositionManager = new DynamicPositionManager(config);
  214. this.log('动态仓位管理器已初始化', 'success');
  215. }
  216. /**
  217. * 简洁版日志输出
  218. */
  219. private log(message: string, type: 'info' | 'success' | 'warning' | 'error' = 'info'): void {
  220. const icons = {
  221. info: '📊',
  222. success: '✅',
  223. warning: '⚠️',
  224. error: '❌'
  225. };
  226. // 只显示重要信息,减少时间戳
  227. if (type === 'error' || type === 'success') {
  228. console.log(`${icons[type]} ${message}`);
  229. }
  230. }
  231. /**
  232. * 加载统计信息
  233. */
  234. private loadStats(): void {
  235. try {
  236. if (existsSync(this.STATS_FILE)) {
  237. const savedStats = JSON.parse(readFileSync(this.STATS_FILE, 'utf-8'));
  238. this.stats = { ...this.stats, ...savedStats };
  239. this.log(`加载历史统计: 运行${Math.floor((Date.now() - this.stats.startTime) / 1000 / 60)}分钟`, 'info');
  240. }
  241. } catch (error) {
  242. // 静默处理加载错误
  243. }
  244. }
  245. /**
  246. * 保存统计信息
  247. */
  248. private saveStats(): void {
  249. try {
  250. this.stats.lastUpdateTime = Date.now();
  251. writeFileSync(this.STATS_FILE, JSON.stringify(this.stats, null, 2));
  252. } catch (error) {
  253. // 静默处理保存错误
  254. }
  255. }
  256. /**
  257. * 带重试的API调用
  258. */
  259. private async callApiWithRetry<T>(
  260. apiCall: () => Promise<T>,
  261. operation: string,
  262. maxRetries: number = this.MAX_RETRY_ATTEMPTS
  263. ): Promise<T> {
  264. let lastError: Error | null = null;
  265. for (let attempt = 1; attempt <= maxRetries; attempt++) {
  266. try {
  267. await this.waitForApiCallInterval();
  268. const result = await apiCall();
  269. this.stats.apiCallCount++;
  270. this.stats.retryCount = 0;
  271. return result;
  272. } catch (error) {
  273. lastError = error as Error;
  274. this.stats.retryCount++;
  275. this.stats.totalRetries++;
  276. if (attempt < maxRetries) {
  277. const delay = this.RETRY_DELAY * attempt;
  278. await new Promise(resolve => setTimeout(resolve, delay));
  279. }
  280. }
  281. }
  282. throw lastError;
  283. }
  284. /**
  285. * 健康检查
  286. */
  287. private async performHealthCheck(): Promise<void> {
  288. try {
  289. const runtime = Date.now() - this.stats.startTime;
  290. // 检查最大运行时间
  291. if (runtime > this.MAX_RUNTIME) {
  292. this.log(`达到最大运行时间,准备停止`, 'warning');
  293. await this.stop();
  294. process.exit(0);
  295. }
  296. // 检查连续错误
  297. if (this.stats.consecutiveErrors >= this.stats.maxConsecutiveErrors) {
  298. this.log(`连续错误过多,暂停交易`, 'error');
  299. await this.pauseTrading();
  300. return;
  301. }
  302. // 检查API连接
  303. await this.callApiWithRetry(
  304. () => this.pacificaClient.getPrices(),
  305. '健康检查API连接'
  306. );
  307. // 重置连续错误计数
  308. if (this.stats.consecutiveErrors > 0) {
  309. this.stats.consecutiveErrors = 0;
  310. this.log('API连接恢复', 'success');
  311. }
  312. // 检查交易暂停状态
  313. if (this.isTradingPaused && Date.now() - this.lastTradingPause > this.TRADING_PAUSE_DURATION) {
  314. this.log('恢复交易', 'info');
  315. this.isTradingPaused = false;
  316. }
  317. } catch (error) {
  318. this.stats.consecutiveErrors++;
  319. this.stats.lastApiError = Date.now();
  320. this.log(`健康检查失败`, 'error');
  321. }
  322. }
  323. /**
  324. * 暂停交易
  325. */
  326. private async pauseTrading(): Promise<void> {
  327. this.log('暂停交易,等待错误恢复...', 'warning');
  328. this.isTradingPaused = true;
  329. this.lastTradingPause = Date.now();
  330. // 暂停交易定时器
  331. if (this.volumeTradingInterval) {
  332. clearInterval(this.volumeTradingInterval);
  333. this.volumeTradingInterval = null;
  334. }
  335. // 等待错误恢复
  336. setTimeout(async () => {
  337. this.log('尝试恢复交易...', 'info');
  338. await this.resumeTrading();
  339. }, this.ERROR_RECOVERY_DELAY);
  340. }
  341. /**
  342. * 恢复交易
  343. */
  344. private async resumeTrading(): Promise<void> {
  345. try {
  346. // 重新检查API连接
  347. await this.callApiWithRetry(
  348. () => this.pacificaClient.getPrices(),
  349. '恢复交易API检查'
  350. );
  351. // 恢复交易定时器
  352. this.startVolumeTrading();
  353. this.log('交易恢复成功', 'success');
  354. } catch (error) {
  355. this.log(`交易恢复失败`, 'error');
  356. await this.pauseTrading();
  357. }
  358. }
  359. /**
  360. * 重写开始刷量交易方法
  361. */
  362. protected async startVolumeTrading(): Promise<void> {
  363. this.log('启动简洁版刷量交易系统', 'info');
  364. // 启动订单生命周期管理
  365. this.startOrderLifecycleManagement();
  366. // 启动状态显示
  367. this.startStatusDisplay();
  368. // 启动统计保存
  369. this.startStatsSaving();
  370. // 启动健康检查
  371. this.startHealthCheck();
  372. // 启动动态仓位管理
  373. await this.startDynamicPositionManager();
  374. // 刷量交易 - 15秒间隔
  375. this.volumeTradingInterval = setInterval(async () => {
  376. try {
  377. if (!this.isTradingPaused && this.isInitialized) {
  378. await this.executeVolumeTradingCycle();
  379. }
  380. } catch (error) {
  381. this.stats.consecutiveErrors++;
  382. this.log(`刷量交易失败`, 'warning');
  383. }
  384. }, 15000); // 15秒间隔
  385. // Delta监控 - 120秒间隔
  386. this.deltaMonitoringInterval = setInterval(async () => {
  387. try {
  388. if (this.isInitialized) {
  389. await this.monitorAndRebalance();
  390. }
  391. } catch (error) {
  392. this.stats.consecutiveErrors++;
  393. this.log(`Delta监控失败`, 'warning');
  394. }
  395. }, 120000); // 120秒间隔
  396. }
  397. /**
  398. * 启动订单生命周期管理
  399. */
  400. private startOrderLifecycleManagement(): void {
  401. this.orderCheckInterval = setInterval(async () => {
  402. try {
  403. await this.checkOrderLifecycle();
  404. } catch (error) {
  405. this.stats.consecutiveErrors++;
  406. this.log(`订单检查失败`, 'warning');
  407. }
  408. }, 60000); // 60秒检查一次
  409. }
  410. /**
  411. * 启动状态显示
  412. */
  413. private startStatusDisplay(): void {
  414. this.statusDisplayInterval = setInterval(() => {
  415. this.displayStatus();
  416. }, 600000); // 每10分钟显示一次
  417. }
  418. /**
  419. * 启动统计保存
  420. */
  421. private startStatsSaving(): void {
  422. this.statsSaveInterval = setInterval(() => {
  423. this.saveStats();
  424. }, this.STATS_SAVE_INTERVAL);
  425. }
  426. /**
  427. * 启动健康检查
  428. */
  429. private startHealthCheck(): void {
  430. this.healthCheckInterval = setInterval(async () => {
  431. await this.performHealthCheck();
  432. }, this.HEALTH_CHECK_INTERVAL);
  433. }
  434. /**
  435. * 启动动态仓位管理器
  436. */
  437. private async startDynamicPositionManager(): Promise<void> {
  438. if (!this.dynamicPositionManager) {
  439. console.log('⚠️ 动态仓位管理器未初始化');
  440. return;
  441. }
  442. // 为每个账户初始化管理器
  443. for (const account of this.strategyAccounts) {
  444. const client = this.clientMap.get(account.getId());
  445. if (client) {
  446. this.dynamicPositionManager.initializeAccount(account, client);
  447. console.log(`✅ 为账户 ${account.getName()} 初始化动态仓位管理`);
  448. }
  449. }
  450. // 启动监控
  451. await this.dynamicPositionManager.startMonitoring(this.strategyAccounts);
  452. console.log('✅ 动态仓位管理监控已启动');
  453. this.log('动态仓位管理监控已启动', 'success');
  454. }
  455. /**
  456. * 显示策略状态
  457. */
  458. private displayStatus(): void {
  459. const runtime = Math.floor((Date.now() - this.stats.startTime) / 1000);
  460. const runtimeStr = `${Math.floor(runtime / 3600)}时${Math.floor((runtime % 3600) / 60)}分${runtime % 60}秒`;
  461. console.log('\n' + '='.repeat(50));
  462. console.log('📈 简洁版策略运行状态');
  463. console.log('='.repeat(50));
  464. console.log(`⏰ 运行时间: ${runtimeStr}`);
  465. console.log(`📊 总订单数: ${this.stats.totalOrders}`);
  466. console.log(`✅ 已成交: ${this.stats.filledOrders}`);
  467. console.log(`❌ 已取消: ${this.stats.cancelledOrders}`);
  468. console.log(`📈 总交易量: ${this.stats.totalVolume.toFixed(6)} BTC`);
  469. console.log(`💰 总PNL: ${this.stats.totalPnL.toFixed(2)} USDC`);
  470. console.log(`🔄 活跃订单: ${this.orderLifecycle.size}`);
  471. console.log(`🌐 API调用: ${this.stats.apiCallCount}`);
  472. console.log(`⚠️ 连续错误: ${this.stats.consecutiveErrors}`);
  473. console.log(`⏸️ 交易状态: ${this.isTradingPaused ? '暂停' : '正常'}`);
  474. // 显示WebSocket价格状态
  475. if (this.isPriceReady) {
  476. console.log(`💹 BTC价格: $${this.currentBTCPrice.toFixed(2)} (WebSocket实时)`);
  477. } else {
  478. console.log(`💹 价格源: REST API (WebSocket未就绪)`);
  479. }
  480. // 显示账户配对信息
  481. console.log(`🔗 账户配对: ${this.accountPairs.length}对`);
  482. if (this.stats.totalOrders > 0) {
  483. const successRate = ((this.stats.filledOrders / this.stats.totalOrders) * 100).toFixed(1);
  484. console.log(`🎯 成交率: ${successRate}%`);
  485. }
  486. // 显示账户盈亏报告
  487. this.displayAccountPnL();
  488. // 显示健康状态
  489. const healthStatus = this.stats.consecutiveErrors === 0 ? '🟢 健康' : '🔴 异常';
  490. console.log(`🏥 健康状态: ${healthStatus}`);
  491. console.log('='.repeat(50) + '\n');
  492. }
  493. /**
  494. * 显示账户盈亏报告
  495. */
  496. private displayAccountPnL(): void {
  497. if (this.accountPnL.size === 0) {
  498. return;
  499. }
  500. console.log('\n💼 账户盈亏报告');
  501. console.log('-'.repeat(50));
  502. let totalProfit = 0;
  503. let totalVolume = 0;
  504. let profitableAccounts = 0;
  505. // 收集账户数据
  506. const accountData: Array<{
  507. id: string;
  508. profit: number;
  509. volume: number;
  510. winRate: number;
  511. direction: string;
  512. }> = [];
  513. this.accountPnL.forEach((pnl, accountId) => {
  514. const winRate = pnl.performance;
  515. totalProfit += pnl.totalProfit;
  516. totalVolume += pnl.totalVolume;
  517. if (pnl.totalProfit > 0) {
  518. profitableAccounts++;
  519. }
  520. accountData.push({
  521. id: accountId.slice(0, 8),
  522. profit: pnl.totalProfit,
  523. volume: pnl.totalVolume,
  524. winRate: winRate,
  525. direction: pnl.direction
  526. });
  527. });
  528. // 按利润排序
  529. accountData.sort((a, b) => b.profit - a.profit);
  530. // 显示前5个账户
  531. accountData.slice(0, 5).forEach(acc => {
  532. const profitIcon = acc.profit > 0 ? '✅' : '❌';
  533. const directionIcon = acc.direction === 'long' ? '🔺' : acc.direction === 'short' ? '🔻' : '⚖️';
  534. console.log(
  535. `${profitIcon} ${acc.id} ${directionIcon}: ` +
  536. `盈亏 ${acc.profit.toFixed(2)} USDC | ` +
  537. `成交量 ${acc.volume.toFixed(6)} BTC | ` +
  538. `胜率 ${acc.winRate.toFixed(1)}%`
  539. );
  540. });
  541. console.log('-'.repeat(50));
  542. console.log(`📊 总体盈亏: ${totalProfit.toFixed(2)} USDC`);
  543. console.log(`✅ 盈利账户: ${profitableAccounts}/${this.accountPnL.size} (${(profitableAccounts/this.accountPnL.size*100).toFixed(1)}%)`);
  544. console.log(`📈 总成交量: ${totalVolume.toFixed(6)} BTC`);
  545. // 检查是否达到目标
  546. if (profitableAccounts > 0) {
  547. console.log(`🎯 已达成目标: 至少有${profitableAccounts}个方向的账户盈利!`);
  548. }
  549. }
  550. /**
  551. * 检查订单生命周期
  552. */
  553. private async checkOrderLifecycle(): Promise<void> {
  554. const now = Date.now();
  555. const ordersToRemove: string[] = [];
  556. for (const [orderId, order] of this.orderLifecycle) {
  557. try {
  558. // 检查订单是否超时
  559. const timeSinceCreated = now - order.createdAt;
  560. const maxOrderLifetime = 600000; // 600秒最大生命周期
  561. if (timeSinceCreated > maxOrderLifetime) {
  562. this.log(`订单超时取消: ${orderId}`, 'warning');
  563. await this.cancelOrderSafely(orderId, order.symbol);
  564. this.stats.cancelledOrders++;
  565. ordersToRemove.push(orderId);
  566. continue;
  567. }
  568. // 检查订单状态
  569. if (order.status === 'pending' && order.checkCount < order.maxChecks) {
  570. await this.checkOrderStatus(order);
  571. order.checkCount++;
  572. order.lastChecked = now;
  573. }
  574. // 如果检查次数过多,标记为过期
  575. if (order.checkCount >= order.maxChecks) {
  576. this.log(`订单检查超限: ${orderId}`, 'warning');
  577. order.status = 'expired';
  578. this.stats.expiredOrders++;
  579. ordersToRemove.push(orderId);
  580. }
  581. } catch (error) {
  582. this.log(`检查订单失败: ${orderId}`, 'error');
  583. ordersToRemove.push(orderId);
  584. }
  585. }
  586. // 清理过期订单
  587. for (const orderId of ordersToRemove) {
  588. this.orderLifecycle.delete(orderId);
  589. }
  590. }
  591. /**
  592. * 检查单个订单状态
  593. */
  594. private async checkOrderStatus(order: OrderLifecycle): Promise<void> {
  595. try {
  596. const openOrdersResponse = await this.callApiWithRetry(
  597. () => this.pacificaClient.getOpenOrders(),
  598. '检查订单状态'
  599. );
  600. const openOrders = Array.isArray(openOrdersResponse) ? openOrdersResponse : openOrdersResponse.data || [];
  601. const orderExists = openOrders.some((o: any) => o.order_id === parseInt(order.orderId));
  602. if (!orderExists) {
  603. order.status = 'filled';
  604. this.stats.filledOrders++;
  605. this.stats.totalVolume += parseFloat(order.amount);
  606. this.stats.lastSuccessfulTrade = Date.now();
  607. // 更新账户PnL跟踪
  608. if (order.accountId) {
  609. this.updateAccountPnL(
  610. order.accountId,
  611. order.side,
  612. parseFloat(order.amount),
  613. parseFloat(order.price),
  614. true
  615. );
  616. }
  617. this.log(`订单成交: ${order.side.toUpperCase()} ${order.amount} BTC @ ${order.price}`, 'success');
  618. }
  619. } catch (error) {
  620. this.log(`检查订单状态失败: ${order.orderId}`, 'error');
  621. }
  622. }
  623. /**
  624. * 安全取消订单
  625. */
  626. private async cancelOrderSafely(orderId: string, symbol: string): Promise<void> {
  627. try {
  628. await this.callApiWithRetry(
  629. () => this.pacificaClient.cancelOrder(orderId, symbol),
  630. `取消订单 ${orderId}`
  631. );
  632. } catch (error) {
  633. this.log(`取消订单失败: ${orderId}`, 'error');
  634. }
  635. }
  636. /**
  637. * 更新账户PnL跟踪
  638. */
  639. private updateAccountPnL(
  640. accountId: string,
  641. side: 'bid' | 'ask',
  642. amount: number,
  643. price: number,
  644. success: boolean
  645. ): void {
  646. // 获取或创建账户PnL记录
  647. let pnl = this.accountPnL.get(accountId);
  648. if (!pnl) {
  649. pnl = {
  650. totalProfit: 0,
  651. totalVolume: 0,
  652. winTrades: 0,
  653. lossTrades: 0,
  654. lastUpdateTime: Date.now(),
  655. initialBalance: 0,
  656. currentBalance: 0,
  657. direction: 'neutral' as 'long' | 'short' | 'neutral',
  658. performance: 0
  659. };
  660. this.accountPnL.set(accountId, pnl);
  661. }
  662. // 更新交易量
  663. pnl.totalVolume += amount;
  664. pnl.lastUpdateTime = Date.now();
  665. if (success) {
  666. // 使用当前价格计算盈亏
  667. const currentMarketPrice = this.lastBTCPrice || price;
  668. const spread = Math.abs(currentMarketPrice - price) / currentMarketPrice;
  669. // 买低卖高策略的盈亏计算
  670. if (side === 'bid') {
  671. // 买单:买入价格低于市场价,记为盈利
  672. const profit = spread * amount * price;
  673. pnl.totalProfit += profit;
  674. pnl.winTrades++;
  675. pnl.direction = 'long'; // 买入倾向
  676. this.log(`💰 买单盈利: ${profit.toFixed(2)} USDC (价差 ${(spread * 100).toFixed(2)}%)`, 'info');
  677. } else {
  678. // 卖单:卖出价格高于市场价,记为盈利
  679. const profit = spread * amount * price;
  680. pnl.totalProfit += profit;
  681. pnl.winTrades++;
  682. pnl.direction = 'short'; // 卖出倾向
  683. this.log(`💰 卖单盈利: ${profit.toFixed(2)} USDC (价差 ${(spread * 100).toFixed(2)}%)`, 'info');
  684. }
  685. } else {
  686. pnl.lossTrades++;
  687. }
  688. // 计算绩效分数
  689. const totalTrades = pnl.winTrades + pnl.lossTrades;
  690. if (totalTrades > 0) {
  691. pnl.performance = (pnl.winTrades / totalTrades) * 100;
  692. }
  693. }
  694. /**
  695. * API调用间隔控制
  696. */
  697. private async waitForApiCallInterval(): Promise<void> {
  698. const now = Date.now();
  699. const timeSinceLastCall = now - this.lastApiCall;
  700. if (timeSinceLastCall < this.apiCallInterval) {
  701. const waitTime = this.apiCallInterval - timeSinceLastCall;
  702. await new Promise(resolve => setTimeout(resolve, waitTime));
  703. }
  704. this.lastApiCall = Date.now();
  705. }
  706. /**
  707. * 重写刷量交易执行
  708. * 优先使用WebSocket实时价格,如果不可用则回退到REST API
  709. */
  710. protected async executeVolumeTrades(): Promise<void> {
  711. try {
  712. // 检查是否应该暂停交易
  713. if (this.isTradingPaused || !this.isInitialized) {
  714. return;
  715. }
  716. let currentPrice: number;
  717. // 优先使用WebSocket实时价格
  718. if (this.isPriceReady && this.currentBTCPrice > 0) {
  719. currentPrice = this.currentBTCPrice;
  720. // 无需API调用,直接使用缓存的实时价格
  721. } else {
  722. // 回退到REST API
  723. this.log('使用REST API获取价格 (WebSocket未就绪)', 'warning');
  724. const prices = await this.callApiWithRetry(
  725. () => this.pacificaClient.getPrices(),
  726. '获取价格信息'
  727. );
  728. const btcPrice = prices.data.find((p: any) => p.symbol === 'BTC');
  729. currentPrice = parseFloat(btcPrice.mark);
  730. }
  731. const totalAccountValue = await this.getTotalAccountValue();
  732. const volumePositionSize = Math.min(
  733. this.config.maxVolumePositionSize,
  734. this.config.volumePositionRatio * totalAccountValue / currentPrice
  735. );
  736. // 使用账户配对执行Delta中性刷量
  737. // 每对账户: 一个买入, 一个卖出, 保证净仓位为零
  738. for (const [buyAccount, sellAccount] of this.accountPairs) {
  739. await this.executePairedVolumeTrade(
  740. buyAccount,
  741. sellAccount,
  742. volumePositionSize,
  743. currentPrice
  744. );
  745. }
  746. } catch (error) {
  747. this.log(`刷量交易失败`, 'warning');
  748. }
  749. }
  750. /**
  751. * 执行配对刷量交易
  752. * 确保买入和卖出同步执行,保持Delta中性
  753. */
  754. private async executePairedVolumeTrade(
  755. buyAccount: Account,
  756. sellAccount: Account,
  757. volumePositionSize: number,
  758. currentPrice: number
  759. ): Promise<void> {
  760. try {
  761. // 计算交易数量 - 满足$10最低订单价值要求
  762. const LOT_SIZE = 0.00001; // BTC最小订单大小 (lot size)
  763. const MIN_ORDER_VALUE = 10; // $10 USDC最低订单价值
  764. // 计算满足$10最低要求的BTC数量
  765. const minBtcAmount = MIN_ORDER_VALUE / currentPrice;
  766. const MIN_ORDER_SIZE = Math.ceil(minBtcAmount / LOT_SIZE) * LOT_SIZE;
  767. let tradeSize = Math.max(
  768. MIN_ORDER_SIZE,
  769. Math.min(
  770. volumePositionSize * 0.5, // 每次交易50%的刷量仓位
  771. this.config.maxVolumePositionSize // 最大刷量仓位
  772. )
  773. );
  774. // 确保是lot size的整数倍
  775. tradeSize = Math.floor(tradeSize / LOT_SIZE) * LOT_SIZE;
  776. tradeSize = Math.max(tradeSize, MIN_ORDER_SIZE);
  777. // 验证订单价值
  778. const orderValue = tradeSize * currentPrice;
  779. if (orderValue < MIN_ORDER_VALUE) {
  780. this.log(`订单价值过低: $${orderValue.toFixed(2)} < $10, 跳过`, 'warning');
  781. return;
  782. }
  783. // 余额检查
  784. const buyClient = this.clientMap.get(buyAccount.getId());
  785. const sellClient = this.clientMap.get(sellAccount.getId());
  786. if (!buyClient || !sellClient) {
  787. this.log('账户客户端未找到,跳过交易', 'warning');
  788. return;
  789. }
  790. // 检查买入账户余额
  791. const buyBalance = await this.checkAccountBalance(buyAccount, buyClient, orderValue);
  792. if (!buyBalance) {
  793. this.log(`买入账户余额不足: ${buyAccount.getId().slice(0,8)}`, 'warning');
  794. this.handleInsufficientBalance();
  795. return;
  796. }
  797. // 检查卖出账户是否有足够的BTC
  798. const sellBalance = await this.checkAccountBalance(sellAccount, sellClient, tradeSize, true);
  799. if (!sellBalance) {
  800. this.log(`卖出账户BTC不足: ${sellAccount.getId().slice(0,8)}`, 'warning');
  801. this.handleInsufficientBalance();
  802. return;
  803. }
  804. // 风险检查 - 在执行交易前检查仓位大小
  805. if (this.riskManager) {
  806. const buyRiskBreach = await this.riskManager.checkPositionSizeRisk(
  807. this.sessionId,
  808. buyAccount.getId(),
  809. tradeSize
  810. );
  811. const sellRiskBreach = await this.riskManager.checkPositionSizeRisk(
  812. this.sessionId,
  813. sellAccount.getId(),
  814. tradeSize
  815. );
  816. if (buyRiskBreach || sellRiskBreach) {
  817. this.log(`风险检查未通过,跳过本次交易`, 'warning');
  818. return;
  819. }
  820. }
  821. // 同步执行买入和卖出订单 - 这是Delta中性的关键
  822. await Promise.all([
  823. this.executeAccountVolumeTradeWithLifecycle(buyAccount, tradeSize, currentPrice, 'bid'),
  824. this.executeAccountVolumeTradeWithLifecycle(sellAccount, tradeSize, currentPrice, 'ask')
  825. ]);
  826. this.log(`配对交易: ${buyAccount.getId().slice(0,8)}买/${sellAccount.getId().slice(0,8)}卖`, 'success');
  827. } catch (error) {
  828. this.log(`配对交易失败: ${buyAccount.getId().slice(0,8)} <-> ${sellAccount.getId().slice(0,8)}`, 'warning');
  829. }
  830. }
  831. /**
  832. * 带生命周期管理的账户刷量交易
  833. * @param side - 'bid' (买入) 或 'ask' (卖出), 由配对机制指定
  834. */
  835. private async executeAccountVolumeTradeWithLifecycle(
  836. account: Account,
  837. tradeSize: number,
  838. currentPrice: number,
  839. side: 'bid' | 'ask'
  840. ): Promise<void> {
  841. try {
  842. // 获取账户专属客户端
  843. const client = this.clientMap.get(account.getId());
  844. if (!client) {
  845. this.log(`账户客户端未找到: ${account.getId()}`, 'error');
  846. return;
  847. }
  848. // 智能选择订单类型
  849. if (this.config.preferredOrderType === 'limit') {
  850. await this.createLimitOrderWithLifecycle(account, client, side, tradeSize, currentPrice);
  851. } else {
  852. await this.createVolumeMarketOrder(account, client, side, tradeSize);
  853. }
  854. } catch (error) {
  855. this.log(`账户交易失败: ${account.getId()} (${side})`, 'warning');
  856. }
  857. }
  858. /**
  859. * 创建带生命周期管理的limit订单(优先限价单降低手续费)
  860. */
  861. private async createLimitOrderWithLifecycle(
  862. account: Account,
  863. client: PacificaSigningClient,
  864. side: 'bid' | 'ask',
  865. amount: number,
  866. currentPrice: number
  867. ): Promise<void> {
  868. try {
  869. // 使用不对称价差实现买低卖高套利
  870. let spread: number;
  871. // 从配置文件读取价差设置(实际部署时从配置加载)
  872. const asymmetricSpread = true; // 启用不对称价差
  873. const bidSpread = 0.015; // 买单价差1.5%
  874. const askSpread = 0.015; // 卖单价差1.5%
  875. if (asymmetricSpread) {
  876. // 买低卖高策略:买单使用更大的负价差,卖单使用更大的正价差
  877. spread = side === 'bid' ? bidSpread : askSpread;
  878. // 记录价差利润预期
  879. const expectedProfit = spread * 100;
  880. this.log(`💰 套利价差: ${side === 'bid' ? '买' : '卖'}单 -${expectedProfit.toFixed(1)}% (${side === 'bid' ? '买低' : '卖高'})`, 'info');
  881. } else {
  882. // 传统对称价差
  883. spread = this.config.limitOrderSpread || 0.005;
  884. }
  885. const price = side === 'bid'
  886. ? (currentPrice * (1 - spread)).toFixed(0)
  887. : (currentPrice * (1 + spread)).toFixed(0);
  888. const clientOrderId = uuidv4();
  889. const order = {
  890. symbol: 'BTC',
  891. amount: amount.toFixed(8),
  892. side,
  893. price,
  894. tif: 'GTC', // Good-Till-Cancel (挂单等待成交)
  895. reduce_only: false,
  896. client_order_id: clientOrderId
  897. };
  898. const result = await this.callApiWithRetry(
  899. () => client.createLimitOrder(order),
  900. `创建${side}订单 (${account.getId().slice(0,8)})`
  901. );
  902. if (result.success && result.data?.order_id) {
  903. const orderId = result.data.order_id.toString();
  904. // 创建订单生命周期记录 - 10秒超时
  905. const orderLifecycle: OrderLifecycle = {
  906. orderId,
  907. clientOrderId,
  908. accountId: account.getId(), // 记录账户ID
  909. symbol: 'BTC',
  910. side,
  911. amount: order.amount,
  912. price: order.price,
  913. status: 'pending',
  914. createdAt: Date.now(),
  915. lastChecked: Date.now(),
  916. checkCount: 0,
  917. maxChecks: 1 // 10秒超时(检查1次后转市价单)
  918. };
  919. this.orderLifecycle.set(orderId, orderLifecycle);
  920. this.stats.totalOrders++;
  921. this.log(`限价单: ${side.toUpperCase()} ${amount.toFixed(6)} BTC @ $${price} (${account.getId().slice(0,8)})`, 'info');
  922. // 启动订单监控(如果还未启动)
  923. if (!this.orderCheckInterval) {
  924. this.startOrderMonitoring();
  925. }
  926. }
  927. } catch (error) {
  928. this.log(`限价单失败: ${side}, 降级为市价单`, 'warning');
  929. // 限价单失败,降级为市价单
  930. await this.createVolumeMarketOrder(account, client, side, amount);
  931. }
  932. }
  933. /**
  934. * 启动订单状态监控
  935. */
  936. private startOrderMonitoring(): void {
  937. this.orderCheckInterval = setInterval(async () => {
  938. await this.checkPendingOrders();
  939. }, 10000); // 每10秒检查一次
  940. this.log('订单监控已启动 (10秒间隔)', 'info');
  941. }
  942. /**
  943. * 检查待处理订单状态
  944. */
  945. private async checkPendingOrders(): Promise<void> {
  946. const now = Date.now();
  947. const ordersToCheck = Array.from(this.orderLifecycle.entries())
  948. .filter(([_, order]) => order.status === 'pending');
  949. for (const [orderId, order] of ordersToCheck) {
  950. try {
  951. order.checkCount++;
  952. order.lastChecked = now;
  953. // 检查是否超时
  954. const ageSeconds = (now - order.createdAt) / 1000;
  955. if (order.checkCount >= order.maxChecks) {
  956. // 超时未成交,撤单并使用市价单重试
  957. this.log(`限价单超时 (${ageSeconds.toFixed(0)}s): ${order.side.toUpperCase()} @ $${order.price}, 转市价单`, 'warning');
  958. await this.cancelAndRetryWithMarket(orderId, order);
  959. }
  960. } catch (error) {
  961. this.log(`检查订单${orderId}失败`, 'warning');
  962. }
  963. }
  964. }
  965. /**
  966. * 撤单并使用市价单重试
  967. */
  968. private async cancelAndRetryWithMarket(orderId: string, order: OrderLifecycle): Promise<void> {
  969. try {
  970. // 使用订单记录中的账户ID找到正确的账户和客户端
  971. const targetAccount = this.accounts.find(acc => acc.getId() === order.accountId);
  972. const targetClient = targetAccount ? this.clientMap.get(targetAccount.getId()) : null;
  973. if (!targetAccount || !targetClient) {
  974. this.log(`找不到订单对应的账户: ${order.accountId.slice(0,8)}`, 'error');
  975. return;
  976. }
  977. // 尝试撤销订单
  978. try {
  979. await targetClient.cancelOrder(orderId, 'BTC');
  980. this.log(`已撤销限价单: ${orderId} (${targetAccount.getId().slice(0,8)})`, 'info');
  981. } catch (error) {
  982. // 订单可能已成交或不存在,忽略错误
  983. this.log(`撤单失败(可能已成交): ${orderId}`, 'warning');
  984. }
  985. // 标记为已取消
  986. order.status = 'cancelled';
  987. this.stats.cancelledOrders++;
  988. // 使用市价单重试相同数量
  989. const amount = parseFloat(order.amount);
  990. this.log(`市价单重试: ${order.side.toUpperCase()} ${amount.toFixed(6)} BTC (${targetAccount.getId().slice(0,8)})`, 'info');
  991. await this.createVolumeMarketOrder(
  992. targetAccount,
  993. targetClient,
  994. order.side,
  995. amount
  996. );
  997. } catch (error) {
  998. this.log(`撤单重试失败: ${orderId}`, 'error');
  999. }
  1000. }
  1001. /**
  1002. * 创建市场订单
  1003. */
  1004. private async createVolumeMarketOrder(
  1005. account: Account,
  1006. client: PacificaSigningClient,
  1007. side: 'bid' | 'ask',
  1008. amount: number
  1009. ): Promise<void> {
  1010. try {
  1011. // 使用不对称滑点实现买低卖高
  1012. const bidSlippage = '0.3'; // 买单使用较小滑点(0.3%)
  1013. const askSlippage = '0.7'; // 卖单使用较大滑点(0.7%)
  1014. const slippage = side === 'bid' ? bidSlippage : askSlippage;
  1015. this.log(`💹 市价单滑点策略: ${side === 'bid' ? '买' : '卖'}单 ${slippage}% 滑点`, 'info');
  1016. const clientOrderId = uuidv4();
  1017. const order = {
  1018. symbol: 'BTC',
  1019. amount: amount.toFixed(8),
  1020. side,
  1021. slippage_percent: slippage,
  1022. reduce_only: false,
  1023. client_order_id: clientOrderId
  1024. };
  1025. const result = await this.callApiWithRetry(
  1026. () => client.createMarketOrder(order),
  1027. `创建市场${side}订单 (${account.getId().slice(0,8)})`
  1028. );
  1029. if (result.success && result.data?.order_id) {
  1030. const orderId = result.data.order_id.toString();
  1031. const orderLifecycle: OrderLifecycle = {
  1032. orderId,
  1033. clientOrderId,
  1034. accountId: account.getId(), // 记录账户ID
  1035. symbol: 'BTC',
  1036. side,
  1037. amount: order.amount,
  1038. price: '0', // market order has no fixed price
  1039. status: 'pending',
  1040. createdAt: Date.now(),
  1041. lastChecked: Date.now(),
  1042. checkCount: 0,
  1043. maxChecks: 10
  1044. };
  1045. this.orderLifecycle.set(orderId, orderLifecycle);
  1046. this.stats.totalOrders++;
  1047. this.log(`创建市场订单: ${side.toUpperCase()} ${amount.toFixed(6)} BTC (${account.getId().slice(0,8)})`, 'info');
  1048. }
  1049. } catch (error) {
  1050. this.log(`创建市场订单失败: ${side}`, 'error');
  1051. }
  1052. }
  1053. /**
  1054. * 重写初始化方法 - 完全基于WebSocket获取数据
  1055. */
  1056. public async initialize(): Promise<void> {
  1057. try {
  1058. this.log('开始初始化简洁版策略...', 'info');
  1059. // 1. 初始化账户配对 - Delta中性核心
  1060. this.initializeAccountPairs();
  1061. // 2. 初始化并启动市场数据管理器
  1062. if (this.marketDataManager) {
  1063. await this.marketDataManager.initialize();
  1064. this.log('市场数据管理器已启动 (WebSocket自动订阅)', 'success');
  1065. // 等待价格数据就绪 (最多10秒)
  1066. console.log('⏳ 等待WebSocket价格数据就绪...');
  1067. for (let i = 0; i < 100; i++) {
  1068. if (this.isPriceReady) {
  1069. break;
  1070. }
  1071. await new Promise(resolve => setTimeout(resolve, 100));
  1072. }
  1073. // 检查价格数据是否就绪
  1074. if (!this.isPriceReady) {
  1075. throw new Error('WebSocket价格数据未就绪,初始化失败');
  1076. }
  1077. console.log(`✅ WebSocket价格数据就绪: $${this.currentBTCPrice.toFixed(2)}`);
  1078. // 获取账户余额 (使用REST API,仅初始化时调用一次)
  1079. // WebSocket balance订阅需要特殊认证,暂时使用REST API
  1080. console.log('📊 通过REST API获取账户余额...');
  1081. const accountInfo = await this.pacificaClient.getAccountInfo();
  1082. const balanceData = {
  1083. total: parseFloat(accountInfo.balance),
  1084. available: parseFloat(accountInfo.available_to_spend),
  1085. locked: parseFloat(accountInfo.balance) - parseFloat(accountInfo.available_to_spend),
  1086. timestamp: Date.now()
  1087. };
  1088. console.log(`✅ 账户余额: ${balanceData.available.toFixed(2)} USDC (可用), ${balanceData.total.toFixed(2)} USDC (总计)`);
  1089. // 3. 从配置文件读取策略配置
  1090. const strategyConfig = JSON.parse(readFileSync('./config/trading-strategy.json', 'utf-8'));
  1091. // 4. 计算仓位大小(基于WebSocket实时数据)
  1092. const LOT_SIZE = 0.00001; // BTC最小订单大小
  1093. const MIN_ORDER_VALUE = 10; // $10最低订单价值
  1094. // 计算满足$10最低要求的BTC数量
  1095. const minBtcAmount = MIN_ORDER_VALUE / this.currentBTCPrice;
  1096. const minOrderSize = Math.ceil(minBtcAmount / LOT_SIZE) * LOT_SIZE;
  1097. const availableBalance = balanceData.available * strategyConfig.positions.balanceUsageRatio;
  1098. // 基础仓位
  1099. const basePositionValue = Math.max(
  1100. availableBalance * strategyConfig.positions.basePositionRatio,
  1101. MIN_ORDER_VALUE * 1.05
  1102. );
  1103. const basePositionSize = basePositionValue / this.currentBTCPrice;
  1104. const adjustedBaseSize = Math.ceil(basePositionSize / LOT_SIZE) * LOT_SIZE;
  1105. // 刷量仓位
  1106. const volumePositionValue = Math.max(
  1107. availableBalance * strategyConfig.positions.volumePositionRatio,
  1108. MIN_ORDER_VALUE
  1109. );
  1110. const volumePositionSize = volumePositionValue / this.currentBTCPrice;
  1111. const adjustedVolumeSize = Math.ceil(volumePositionSize / LOT_SIZE) * LOT_SIZE;
  1112. console.log(`💰 计算仓位: 可用=${availableBalance.toFixed(2)} USDC, 基础=${adjustedBaseSize.toFixed(8)} BTC, 刷量=${adjustedVolumeSize.toFixed(8)} BTC`);
  1113. // 5. 构建并更新策略配置
  1114. const configManager = new DeltaNeutralConfigManager();
  1115. const updatedConfig: DeltaNeutralConfig = {
  1116. basePositionRatio: strategyConfig.positions.basePositionRatio,
  1117. basePositionSize: adjustedBaseSize,
  1118. volumePositionRatio: strategyConfig.positions.volumePositionRatio,
  1119. maxVolumePositionSize: adjustedVolumeSize,
  1120. maxDeltaDeviation: strategyConfig.deltaRebalancing.maxDeltaDeviation,
  1121. rebalanceThreshold: strategyConfig.deltaRebalancing.rebalanceThreshold,
  1122. preferredOrderType: strategyConfig.orderStrategy.preferredOrderType as 'limit' | 'market',
  1123. limitOrderSpread: strategyConfig.orderStrategy.limitOrderSpread,
  1124. emergencyMarketThreshold: strategyConfig.orderStrategy.emergencyMarketThreshold,
  1125. maxDrawdown: strategyConfig.risk.maxDrawdown,
  1126. maxPositionSize: adjustedBaseSize * strategyConfig.risk.positionSizeMultiplier,
  1127. stopLossThreshold: strategyConfig.risk.stopLossThreshold
  1128. };
  1129. configManager.updateConfig(updatedConfig);
  1130. this.updateStrategyConfig(updatedConfig); // 更新策略实例的配置
  1131. console.log(`✅ 策略配置完成: ${updatedConfig.preferredOrderType}单优先, 价差=${(updatedConfig.limitOrderSpread * 100).toFixed(3)}%`);
  1132. }
  1133. // 初始化并启动风险管理器
  1134. if (this.riskManager) {
  1135. await this.riskManager.initialize();
  1136. await this.riskManager.startMonitoring(this.sessionId);
  1137. this.log('风险管理器已启动', 'success');
  1138. }
  1139. // 启动配对刷量交易循环
  1140. this.startPairedVolumeTrading();
  1141. // 启动Delta再平衡监控
  1142. this.startDeltaRebalancing();
  1143. // 启动动态仓位管理器
  1144. await this.startDynamicPositionManager();
  1145. this.isInitialized = true;
  1146. this.log('简洁版策略初始化成功', 'success');
  1147. } catch (error) {
  1148. this.log(`策略初始化失败`, 'error');
  1149. throw error;
  1150. }
  1151. }
  1152. /**
  1153. * 启动配对刷量交易
  1154. */
  1155. private startPairedVolumeTrading(): void {
  1156. // 启动刷量交易循环 - 每10秒执行一次
  1157. this.volumeTradingInterval = setInterval(async () => {
  1158. if (this.isTradingPaused) {
  1159. return;
  1160. }
  1161. try {
  1162. await this.executeVolumeTrades();
  1163. } catch (error) {
  1164. this.log('刷量交易周期失败', 'warning');
  1165. }
  1166. }, 10000); // 使用10秒间隔,减少API调用
  1167. this.log('配对刷量交易已启动 (10秒间隔)', 'success');
  1168. }
  1169. /**
  1170. * 启动Delta再平衡监控
  1171. * 定期检查并修正仓位不平衡
  1172. */
  1173. private startDeltaRebalancing(): void {
  1174. // 每60秒检查一次Delta平衡
  1175. this.deltaMonitoringInterval = setInterval(async () => {
  1176. if (this.isTradingPaused) {
  1177. return;
  1178. }
  1179. try {
  1180. await this.checkAndRebalanceDelta();
  1181. } catch (error) {
  1182. this.log('Delta再平衡检查失败', 'warning');
  1183. }
  1184. }, 60000);
  1185. this.log('Delta再平衡监控已启动 (60秒间隔)', 'success');
  1186. }
  1187. /**
  1188. * 检查并执行Delta再平衡
  1189. */
  1190. private async checkAndRebalanceDelta(): Promise<void> {
  1191. try {
  1192. // 获取所有账户的实时仓位
  1193. const positions = await this.getAllAccountPositions();
  1194. if (positions.length === 0) {
  1195. return;
  1196. }
  1197. // 计算总净Delta
  1198. let totalNetDelta = 0;
  1199. let totalExposure = 0;
  1200. for (const pos of positions) {
  1201. totalNetDelta += pos.netPosition;
  1202. totalExposure += Math.abs(pos.netPosition);
  1203. }
  1204. // 如果没有仓位,无需再平衡
  1205. if (totalExposure === 0) {
  1206. return;
  1207. }
  1208. // 计算Delta偏差百分比
  1209. const deltaDeviationPercent = Math.abs(totalNetDelta / totalExposure);
  1210. this.log(`Delta状态: 净Delta=${totalNetDelta.toFixed(8)} BTC, 偏差=${(deltaDeviationPercent * 100).toFixed(2)}%`, 'info');
  1211. // 如果偏差超过阈值(例如5%),触发再平衡
  1212. const REBALANCE_THRESHOLD = 0.05; // 5%阈值
  1213. if (deltaDeviationPercent > REBALANCE_THRESHOLD) {
  1214. this.log(`⚠️ Delta偏差过大 (${(deltaDeviationPercent * 100).toFixed(2)}%), 开始再平衡...`, 'warning');
  1215. await this.executeDeltaRebalance(positions, totalNetDelta);
  1216. }
  1217. } catch (error) {
  1218. this.log('Delta再平衡执行失败', 'error');
  1219. }
  1220. }
  1221. /**
  1222. * 获取所有账户的实时仓位
  1223. */
  1224. private async getAllAccountPositions(): Promise<Array<{account: Account, client: PacificaSigningClient, netPosition: number, amount: string, side: string}>> {
  1225. const positions = [];
  1226. for (const account of this.accounts) {
  1227. const client = this.clientMap.get(account.getId());
  1228. if (!client) {
  1229. continue;
  1230. }
  1231. try {
  1232. const result = await client.getAccountPositions();
  1233. if (result.data && Array.isArray(result.data)) {
  1234. const btcPos = result.data.find((p: any) => p.symbol === 'BTC');
  1235. if (btcPos) {
  1236. // bid = long (正), ask = short (负)
  1237. const netPosition = btcPos.side === 'bid'
  1238. ? parseFloat(btcPos.amount)
  1239. : -parseFloat(btcPos.amount);
  1240. positions.push({
  1241. account,
  1242. client,
  1243. netPosition,
  1244. amount: btcPos.amount,
  1245. side: btcPos.side
  1246. });
  1247. }
  1248. }
  1249. } catch (error) {
  1250. this.log(`获取账户${account.getId().slice(0,8)}仓位失败`, 'warning');
  1251. }
  1252. }
  1253. return positions;
  1254. }
  1255. /**
  1256. * 执行Delta再平衡
  1257. * 通过调整账户仓位使净Delta接近零
  1258. */
  1259. private async executeDeltaRebalance(
  1260. positions: Array<{account: Account, client: PacificaSigningClient, netPosition: number, amount: string, side: string}>,
  1261. totalNetDelta: number
  1262. ): Promise<void> {
  1263. try {
  1264. // 计算需要平衡的数量(净Delta的一半,分配到两个方向)
  1265. const rebalanceAmount = Math.abs(totalNetDelta) / 2;
  1266. // 确保满足最小订单要求
  1267. const LOT_SIZE = 0.00001;
  1268. const MIN_ORDER_VALUE = 10;
  1269. const currentPrice = this.currentBTCPrice || 113000;
  1270. const minBtcAmount = MIN_ORDER_VALUE / currentPrice;
  1271. const MIN_ORDER_SIZE = Math.ceil(minBtcAmount / LOT_SIZE) * LOT_SIZE;
  1272. let adjustedRebalanceAmount = Math.floor(rebalanceAmount / LOT_SIZE) * LOT_SIZE;
  1273. if (adjustedRebalanceAmount < MIN_ORDER_SIZE) {
  1274. this.log(`再平衡数量过小 (${adjustedRebalanceAmount.toFixed(8)} < ${MIN_ORDER_SIZE.toFixed(8)}), 跳过`, 'info');
  1275. return;
  1276. }
  1277. // 找出净多头和净空头账户
  1278. const longPositions = positions.filter(p => p.netPosition > 0);
  1279. const shortPositions = positions.filter(p => p.netPosition < 0);
  1280. if (totalNetDelta > 0) {
  1281. // 净多头过多:减少多头仓位,增加空头仓位
  1282. this.log(`执行再平衡: 减少${adjustedRebalanceAmount.toFixed(8)} BTC多头`, 'info');
  1283. // 在最大多头账户上减仓
  1284. if (longPositions.length > 0) {
  1285. const maxLongAccount = longPositions.reduce((max, p) =>
  1286. p.netPosition > max.netPosition ? p : max
  1287. );
  1288. await this.executeRebalanceOrder(
  1289. maxLongAccount.account,
  1290. maxLongAccount.client,
  1291. adjustedRebalanceAmount,
  1292. 'ask' // 平多头 = 卖出
  1293. );
  1294. }
  1295. // 在最小空头账户上加仓
  1296. if (shortPositions.length > 0) {
  1297. const minShortAccount = shortPositions.reduce((min, p) =>
  1298. Math.abs(p.netPosition) < Math.abs(min.netPosition) ? p : min
  1299. );
  1300. await this.executeRebalanceOrder(
  1301. minShortAccount.account,
  1302. minShortAccount.client,
  1303. adjustedRebalanceAmount,
  1304. 'ask' // 增加空头 = 卖出
  1305. );
  1306. } else {
  1307. // 如果没有空头账户,在另一个多头账户上开空
  1308. if (longPositions.length > 1) {
  1309. const minLongAccount = longPositions.reduce((min, p) =>
  1310. p.netPosition < min.netPosition ? p : min
  1311. );
  1312. await this.executeRebalanceOrder(
  1313. minLongAccount.account,
  1314. minLongAccount.client,
  1315. adjustedRebalanceAmount,
  1316. 'ask'
  1317. );
  1318. }
  1319. }
  1320. } else {
  1321. // 净空头过多:减少空头仓位,增加多头仓位
  1322. this.log(`执行再平衡: 减少${adjustedRebalanceAmount.toFixed(8)} BTC空头`, 'info');
  1323. // 在最大空头账户上减仓
  1324. if (shortPositions.length > 0) {
  1325. const maxShortAccount = shortPositions.reduce((max, p) =>
  1326. Math.abs(p.netPosition) > Math.abs(max.netPosition) ? p : max
  1327. );
  1328. await this.executeRebalanceOrder(
  1329. maxShortAccount.account,
  1330. maxShortAccount.client,
  1331. adjustedRebalanceAmount,
  1332. 'bid' // 平空头 = 买入
  1333. );
  1334. }
  1335. // 在最小多头账户上加仓
  1336. if (longPositions.length > 0) {
  1337. const minLongAccount = longPositions.reduce((min, p) =>
  1338. p.netPosition < min.netPosition ? p : min
  1339. );
  1340. await this.executeRebalanceOrder(
  1341. minLongAccount.account,
  1342. minLongAccount.client,
  1343. adjustedRebalanceAmount,
  1344. 'bid' // 增加多头 = 买入
  1345. );
  1346. } else {
  1347. // 如果没有多头账户,在另一个空头账户上开多
  1348. if (shortPositions.length > 1) {
  1349. const minShortAccount = shortPositions.reduce((min, p) =>
  1350. Math.abs(p.netPosition) < Math.abs(min.netPosition) ? p : min
  1351. );
  1352. await this.executeRebalanceOrder(
  1353. minShortAccount.account,
  1354. minShortAccount.client,
  1355. adjustedRebalanceAmount,
  1356. 'bid'
  1357. );
  1358. }
  1359. }
  1360. }
  1361. this.log('✅ Delta再平衡完成', 'success');
  1362. } catch (error) {
  1363. this.log('Delta再平衡执行失败', 'error');
  1364. }
  1365. }
  1366. /**
  1367. * 执行再平衡订单
  1368. */
  1369. private async executeRebalanceOrder(
  1370. account: Account,
  1371. client: PacificaSigningClient,
  1372. amount: number,
  1373. side: 'bid' | 'ask'
  1374. ): Promise<void> {
  1375. try {
  1376. const clientOrderId = uuidv4();
  1377. const order = {
  1378. symbol: 'BTC',
  1379. amount: amount.toFixed(8),
  1380. side,
  1381. slippage_percent: '0.5',
  1382. reduce_only: false,
  1383. client_order_id: clientOrderId
  1384. };
  1385. const result = await this.callApiWithRetry(
  1386. () => client.createMarketOrder(order),
  1387. `再平衡订单 (${account.getId().slice(0,8)} ${side})`
  1388. );
  1389. if (result.success) {
  1390. this.log(`✅ 再平衡订单: ${account.getId().slice(0,8)} ${side.toUpperCase()} ${amount.toFixed(8)} BTC`, 'success');
  1391. }
  1392. } catch (error) {
  1393. this.log(`再平衡订单失败: ${account.getId().slice(0,8)} ${side}`, 'error');
  1394. }
  1395. }
  1396. /**
  1397. * 初始化账户配对机制
  1398. * 确保Delta中性: 每个买单都有对应的卖单
  1399. */
  1400. private initializeAccountPairs(): void {
  1401. if (this.accounts.length < 2) {
  1402. throw new Error('需要至少2个账户进行Delta中性对冲');
  1403. }
  1404. if (this.accounts.length % 2 !== 0) {
  1405. this.log(`警告: 账户数量为奇数(${this.accounts.length}), 最后一个账户将不参与配对`, 'warning');
  1406. }
  1407. this.accountPairs = [];
  1408. for (let i = 0; i < this.accounts.length - 1; i += 2) {
  1409. this.accountPairs.push([this.accounts[i], this.accounts[i + 1]]);
  1410. }
  1411. this.log(`账户配对完成: ${this.accountPairs.length}对账户`, 'success');
  1412. this.accountPairs.forEach((pair, index) => {
  1413. this.log(` 配对${index + 1}: ${pair[0].getId()} (买) <-> ${pair[1].getId()} (卖)`, 'info');
  1414. });
  1415. }
  1416. /**
  1417. * 检查账户余额
  1418. * @param account 账户
  1419. * @param client 客户端
  1420. * @param requiredAmount 需要的金额(USDC或BTC)
  1421. * @param isBTC 是否检查BTC余额
  1422. */
  1423. private async checkAccountBalance(
  1424. account: Account,
  1425. client: PacificaSigningClient,
  1426. requiredAmount: number,
  1427. isBTC: boolean = false
  1428. ): Promise<boolean> {
  1429. try {
  1430. const accountInfo = await client.getAccountInfo();
  1431. if (isBTC) {
  1432. // 检查BTC持仓
  1433. const positions = await client.getAccountPositions();
  1434. const btcPositions = Array.isArray(positions.data) ?
  1435. positions.data.filter((p: any) => p.symbol === 'BTC') : [];
  1436. const totalBTC = btcPositions.reduce((sum: number, p: any) => {
  1437. return sum + Math.abs(parseFloat(p.amount));
  1438. }, 0);
  1439. return totalBTC >= requiredAmount;
  1440. } else {
  1441. // 检查USDC余额
  1442. const availableBalance = parseFloat(accountInfo.data.available_to_spend);
  1443. const marginBuffer = 1.1; // 10%缓冲
  1444. return availableBalance >= requiredAmount * marginBuffer;
  1445. }
  1446. } catch (error) {
  1447. this.log(`检查余额失败: ${account.getId().slice(0,8)}`, 'error');
  1448. return false;
  1449. }
  1450. }
  1451. /**
  1452. * 处理余额不足
  1453. */
  1454. private handleInsufficientBalance(): void {
  1455. if (!this.insufficientBalanceCount) {
  1456. this.insufficientBalanceCount = 0;
  1457. }
  1458. this.insufficientBalanceCount++;
  1459. // 如果连续5次余额不足,暂停交易
  1460. if (this.insufficientBalanceCount >= 5) {
  1461. this.log('⚠️ 连续余额不足,暂停交易60秒', 'warning');
  1462. this.isTradingPaused = true;
  1463. // 60秒后恢复
  1464. setTimeout(() => {
  1465. this.isTradingPaused = false;
  1466. this.insufficientBalanceCount = 0;
  1467. this.log('✅ 恢复交易', 'success');
  1468. }, 60000);
  1469. }
  1470. }
  1471. private insufficientBalanceCount: number = 0;
  1472. /**
  1473. * 重写停止方法
  1474. */
  1475. public async stop(): Promise<void> {
  1476. this.log('停止简洁版策略...', 'info');
  1477. // 清理定时器
  1478. if (this.volumeTradingInterval) {
  1479. clearInterval(this.volumeTradingInterval);
  1480. this.volumeTradingInterval = null;
  1481. }
  1482. if (this.deltaMonitoringInterval) {
  1483. clearInterval(this.deltaMonitoringInterval);
  1484. this.deltaMonitoringInterval = null;
  1485. }
  1486. if (this.orderCheckInterval) {
  1487. clearInterval(this.orderCheckInterval);
  1488. this.orderCheckInterval = null;
  1489. }
  1490. if (this.statusDisplayInterval) {
  1491. clearInterval(this.statusDisplayInterval);
  1492. this.statusDisplayInterval = null;
  1493. }
  1494. if (this.statsSaveInterval) {
  1495. clearInterval(this.statsSaveInterval);
  1496. this.statsSaveInterval = null;
  1497. }
  1498. if (this.healthCheckInterval) {
  1499. clearInterval(this.healthCheckInterval);
  1500. this.healthCheckInterval = null;
  1501. }
  1502. // 停止动态仓位管理器
  1503. if (this.dynamicPositionManager) {
  1504. this.dynamicPositionManager.shutdown();
  1505. this.log('动态仓位管理器已停止', 'success');
  1506. }
  1507. // 使用 OrderCleanupService 取消所有开放订单
  1508. console.log('\n🧹 正在从 Pacifica API 获取开放订单...');
  1509. this.log('清理所有开放订单...', 'info');
  1510. try {
  1511. const cleanupService = new OrderCleanupService();
  1512. // 为每个账户初始化清理服务
  1513. for (const account of this.accounts) {
  1514. cleanupService.initializeAccount(account);
  1515. }
  1516. // 强制清理所有订单
  1517. const cleanupOptions: CleanupOptions = {
  1518. cleanAll: true,
  1519. includeReduceOnly: true, // 包括保护性订单
  1520. mode: 'force', // 强制模式
  1521. dryRun: false
  1522. };
  1523. console.log('从 API 获取订单并取消...');
  1524. const results = await cleanupService.cleanupMultipleAccounts(
  1525. this.accounts,
  1526. cleanupOptions
  1527. );
  1528. // 汇总清理结果
  1529. let totalOrders = 0;
  1530. let totalCancelled = 0;
  1531. for (const result of results.values()) {
  1532. totalOrders += result.totalOrders;
  1533. totalCancelled += result.cancelledOrders;
  1534. }
  1535. console.log(`📊 从 API 获取到 ${totalOrders} 个开放订单`);
  1536. if (totalCancelled > 0) {
  1537. console.log(`✅ 已取消 ${totalCancelled} 个开放订单`);
  1538. this.log(`已取消 ${totalCancelled} 个开放订单`, 'success');
  1539. } else {
  1540. console.log('✅ 没有需要清理的订单');
  1541. this.log('没有需要清理的订单', 'info');
  1542. }
  1543. await cleanupService.shutdown();
  1544. } catch (error) {
  1545. this.log('⚠️ 订单清理失败,尝试手动取消', 'warning');
  1546. // 降级处理:手动取消内存中的订单
  1547. for (const [orderId, order] of this.orderLifecycle) {
  1548. if (order.status === 'pending') {
  1549. try {
  1550. await this.cancelOrderSafely(orderId, order.symbol);
  1551. } catch (error) {
  1552. this.log(`取消订单失败: ${orderId}`, 'error');
  1553. }
  1554. }
  1555. }
  1556. }
  1557. // 清理生命周期记录
  1558. this.orderLifecycle.clear();
  1559. // 停止市场数据管理器
  1560. if (this.marketDataManager) {
  1561. await this.marketDataManager.disconnect();
  1562. this.log('市场数据管理器已停止', 'info');
  1563. }
  1564. // 停止风险管理器
  1565. if (this.riskManager) {
  1566. await this.riskManager.stopMonitoring(this.sessionId);
  1567. await this.riskManager.shutdown();
  1568. this.log('风险管理器已停止', 'info');
  1569. }
  1570. // 保存最终统计
  1571. this.saveStats();
  1572. // 显示最终统计
  1573. this.displayFinalStats();
  1574. // 调用父类停止方法
  1575. await super.stop();
  1576. this.log('简洁版策略已停止', 'success');
  1577. }
  1578. /**
  1579. * 显示最终统计
  1580. */
  1581. private displayFinalStats(): void {
  1582. const runtime = Math.floor((Date.now() - this.stats.startTime) / 1000);
  1583. const runtimeStr = `${Math.floor(runtime / 3600)}时${Math.floor((runtime % 3600) / 60)}分${runtime % 60}秒`;
  1584. console.log('\n' + '='.repeat(50));
  1585. console.log('📊 简洁版策略最终统计');
  1586. console.log('='.repeat(50));
  1587. console.log(`⏰ 总运行时间: ${runtimeStr}`);
  1588. console.log(`📊 总订单数: ${this.stats.totalOrders}`);
  1589. console.log(`✅ 已成交: ${this.stats.filledOrders}`);
  1590. console.log(`❌ 已取消: ${this.stats.cancelledOrders}`);
  1591. console.log(`📈 总交易量: ${this.stats.totalVolume.toFixed(6)} BTC`);
  1592. console.log(`💰 总PNL: ${this.stats.totalPnL.toFixed(2)} USDC`);
  1593. console.log(`🌐 API调用: ${this.stats.apiCallCount}`);
  1594. console.log(`🔄 总重试次数: ${this.stats.totalRetries}`);
  1595. console.log(`⚠️ 最大连续错误: ${this.stats.consecutiveErrors}`);
  1596. if (this.stats.totalOrders > 0) {
  1597. const successRate = ((this.stats.filledOrders / this.stats.totalOrders) * 100).toFixed(1);
  1598. const avgVolume = (this.stats.totalVolume / this.stats.filledOrders).toFixed(6);
  1599. console.log(`🎯 成交率: ${successRate}%`);
  1600. console.log(`📊 平均交易量: ${avgVolume} BTC`);
  1601. }
  1602. console.log('='.repeat(50) + '\n');
  1603. }
  1604. }
  1605. async function runSimpleDeltaNeutralStrategy() {
  1606. console.log('🚀 启动简洁版Delta中性刷量策略...\n');
  1607. try {
  1608. // 1. 加载配置
  1609. console.log('📋 加载配置文件...');
  1610. const accountsConfig = JSON.parse(readFileSync('./config/accounts.json', 'utf-8'));
  1611. const strategyConfig = JSON.parse(readFileSync('./config/trading-strategy.json', 'utf-8'));
  1612. console.log(`✅ 账户配置: ${accountsConfig.length} 个账户`);
  1613. console.log(`✅ 策略配置: ${strategyConfig.orderStrategy.preferredOrderType}单优先\n`);
  1614. // 2. 创建账户对象(余额由WebSocket更新)
  1615. const accounts: Account[] = accountsConfig.map((acc: any) => new Account({
  1616. id: acc.id,
  1617. name: acc.name,
  1618. privateKey: acc.privateKey,
  1619. apiKey: acc.apiKey || 'not_required',
  1620. address: acc.address,
  1621. isActive: acc.isActive !== false,
  1622. balance: {
  1623. total: 0, // 将由WebSocket更新
  1624. available: 0, // 将由WebSocket更新
  1625. used: 0
  1626. },
  1627. positions: []
  1628. }));
  1629. // 3. 创建简洁版策略实例
  1630. console.log('🎯 创建简洁版策略实例...');
  1631. const privateKey = accountsConfig[0].privateKey;
  1632. const pacificaClient = new PacificaSigningClient(privateKey);
  1633. // 创建初始配置(仓位大小将在initialize()中由WebSocket数据计算)
  1634. const initialConfig: DeltaNeutralConfig = {
  1635. basePositionRatio: strategyConfig.positions.basePositionRatio,
  1636. basePositionSize: 0, // 将由initialize()计算
  1637. volumePositionRatio: strategyConfig.positions.volumePositionRatio,
  1638. maxVolumePositionSize: 0, // 将由initialize()计算
  1639. maxDeltaDeviation: strategyConfig.deltaRebalancing.maxDeltaDeviation,
  1640. rebalanceThreshold: strategyConfig.deltaRebalancing.rebalanceThreshold,
  1641. preferredOrderType: strategyConfig.orderStrategy.preferredOrderType as 'limit' | 'market',
  1642. limitOrderSpread: strategyConfig.orderStrategy.limitOrderSpread,
  1643. emergencyMarketThreshold: strategyConfig.orderStrategy.emergencyMarketThreshold,
  1644. maxDrawdown: strategyConfig.risk.maxDrawdown,
  1645. maxPositionSize: 0, // 将由initialize()计算
  1646. stopLossThreshold: strategyConfig.risk.stopLossThreshold
  1647. };
  1648. const strategy = new SimpleDeltaNeutralStrategy(
  1649. initialConfig,
  1650. pacificaClient,
  1651. accounts
  1652. );
  1653. console.log('✅ 简洁版策略创建成功\n');
  1654. // 8. 清理历史订单(策略启动前)
  1655. console.log('\n🧹 清理历史开放订单...');
  1656. try {
  1657. const cleanupService = new OrderCleanupService();
  1658. // 为每个账户初始化清理服务
  1659. for (const account of accounts) {
  1660. cleanupService.initializeAccount(account);
  1661. }
  1662. // 清理选项:清理所有非 reduce_only 订单
  1663. const cleanupOptions: CleanupOptions = {
  1664. cleanAll: true,
  1665. includeReduceOnly: false, // 保留保护性订单
  1666. mode: 'selective',
  1667. dryRun: false
  1668. };
  1669. console.log('正在从 Pacifica API 获取开放订单...');
  1670. const results = await cleanupService.cleanupMultipleAccounts(
  1671. accounts,
  1672. cleanupOptions
  1673. );
  1674. // 汇总清理结果
  1675. let totalOrders = 0;
  1676. let totalCancelled = 0;
  1677. let totalFailed = 0;
  1678. for (const [accountId, result] of results.entries()) {
  1679. totalOrders += result.totalOrders;
  1680. totalCancelled += result.cancelledOrders;
  1681. totalFailed += result.failedOrders;
  1682. if (result.totalOrders > 0) {
  1683. console.log(` 账户 ${accountId.slice(0, 8)}: ${result.totalOrders} 个订单, 已取消 ${result.cancelledOrders}, 失败 ${result.failedOrders}`);
  1684. }
  1685. }
  1686. if (totalOrders > 0) {
  1687. console.log(`✅ 历史订单清理完成: ${totalCancelled}/${totalOrders} 个订单已取消`);
  1688. if (totalFailed > 0) {
  1689. console.log(`⚠️ ${totalFailed} 个订单取消失败`);
  1690. }
  1691. } else {
  1692. console.log('✅ 没有历史开放订单需要清理');
  1693. }
  1694. await cleanupService.shutdown();
  1695. } catch (error) {
  1696. console.log('⚠️ 历史订单清理失败,继续启动策略');
  1697. console.log(`错误: ${error instanceof Error ? error.message : '未知错误'}`);
  1698. }
  1699. // 9. 初始化策略
  1700. await strategy.initialize();
  1701. console.log('✅ 策略初始化完成');
  1702. // 10. 启动策略
  1703. console.log('\n📊 简洁版策略已启动,开始简洁运行...');
  1704. console.log('按 Ctrl+C 停止策略');
  1705. // 11. 优雅关闭处理
  1706. process.on('SIGINT', async () => {
  1707. console.log('\n🛑 收到停止信号,正在优雅关闭策略...');
  1708. try {
  1709. await strategy.stop();
  1710. console.log('✅ 策略已安全停止');
  1711. process.exit(0);
  1712. } catch (error) {
  1713. console.error('❌ 停止策略时发生错误:', error);
  1714. process.exit(1);
  1715. }
  1716. });
  1717. // 12. 错误处理
  1718. process.on('uncaughtException', async (error) => {
  1719. console.error('❌ 未捕获的异常:', error);
  1720. try {
  1721. await strategy.stop();
  1722. } catch (stopError) {
  1723. console.error('❌ 停止策略时发生错误:', stopError);
  1724. }
  1725. process.exit(1);
  1726. });
  1727. process.on('unhandledRejection', async (reason) => {
  1728. console.error('❌ 未处理的Promise拒绝:', reason);
  1729. try {
  1730. await strategy.stop();
  1731. } catch (stopError) {
  1732. console.error('❌ 停止策略时发生错误:', stopError);
  1733. }
  1734. process.exit(1);
  1735. });
  1736. console.log('\n🎉 简洁版Delta中性刷量策略已启动!');
  1737. console.log('策略将持续简洁运行,自动处理API错误和网络问题...');
  1738. } catch (error) {
  1739. console.error('❌ 策略启动失败:', error);
  1740. process.exit(1);
  1741. }
  1742. }
  1743. // 运行策略
  1744. runSimpleDeltaNeutralStrategy().catch(console.error);