@nexstone/rift-cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +201 -0
  2. package/bin/run.js +22 -0
  3. package/dist/commands/algo.d.ts +32 -0
  4. package/dist/commands/algo.js +719 -0
  5. package/dist/commands/audit.d.ts +13 -0
  6. package/dist/commands/audit.js +37 -0
  7. package/dist/commands/auth-status.d.ts +14 -0
  8. package/dist/commands/auth-status.js +118 -0
  9. package/dist/commands/auth.d.ts +14 -0
  10. package/dist/commands/auth.js +275 -0
  11. package/dist/commands/backtest.d.ts +26 -0
  12. package/dist/commands/backtest.js +283 -0
  13. package/dist/commands/collect/start.d.ts +11 -0
  14. package/dist/commands/collect/start.js +78 -0
  15. package/dist/commands/collect/status.d.ts +6 -0
  16. package/dist/commands/collect/status.js +60 -0
  17. package/dist/commands/compare.d.ts +16 -0
  18. package/dist/commands/compare.js +130 -0
  19. package/dist/commands/config.d.ts +16 -0
  20. package/dist/commands/config.js +143 -0
  21. package/dist/commands/cost.d.ts +20 -0
  22. package/dist/commands/cost.js +104 -0
  23. package/dist/commands/cross-asset.d.ts +14 -0
  24. package/dist/commands/cross-asset.js +39 -0
  25. package/dist/commands/data/fetch.d.ts +15 -0
  26. package/dist/commands/data/fetch.js +82 -0
  27. package/dist/commands/data/list.d.ts +6 -0
  28. package/dist/commands/data/list.js +28 -0
  29. package/dist/commands/data-inventory.d.ts +9 -0
  30. package/dist/commands/data-inventory.js +24 -0
  31. package/dist/commands/deposit.d.ts +10 -0
  32. package/dist/commands/deposit.js +222 -0
  33. package/dist/commands/doctor.d.ts +6 -0
  34. package/dist/commands/doctor.js +87 -0
  35. package/dist/commands/funding-browser.d.ts +12 -0
  36. package/dist/commands/funding-browser.js +33 -0
  37. package/dist/commands/guide.d.ts +6 -0
  38. package/dist/commands/guide.js +15 -0
  39. package/dist/commands/home.d.ts +23 -0
  40. package/dist/commands/home.js +210 -0
  41. package/dist/commands/init.d.ts +7 -0
  42. package/dist/commands/init.js +122 -0
  43. package/dist/commands/install.d.ts +9 -0
  44. package/dist/commands/install.js +89 -0
  45. package/dist/commands/interactive.d.ts +17 -0
  46. package/dist/commands/interactive.js +179 -0
  47. package/dist/commands/lessons.d.ts +12 -0
  48. package/dist/commands/lessons.js +33 -0
  49. package/dist/commands/montecarlo.d.ts +19 -0
  50. package/dist/commands/montecarlo.js +168 -0
  51. package/dist/commands/more.d.ts +11 -0
  52. package/dist/commands/more.js +227 -0
  53. package/dist/commands/new.d.ts +14 -0
  54. package/dist/commands/new.js +306 -0
  55. package/dist/commands/pairs.d.ts +22 -0
  56. package/dist/commands/pairs.js +147 -0
  57. package/dist/commands/perp/close.d.ts +12 -0
  58. package/dist/commands/perp/close.js +57 -0
  59. package/dist/commands/perp/long.d.ts +14 -0
  60. package/dist/commands/perp/long.js +38 -0
  61. package/dist/commands/perp/short.d.ts +14 -0
  62. package/dist/commands/perp/short.js +27 -0
  63. package/dist/commands/perp/status.d.ts +9 -0
  64. package/dist/commands/perp/status.js +26 -0
  65. package/dist/commands/portfolio/alerts.d.ts +6 -0
  66. package/dist/commands/portfolio/alerts.js +47 -0
  67. package/dist/commands/portfolio/backtest.d.ts +12 -0
  68. package/dist/commands/portfolio/backtest.js +178 -0
  69. package/dist/commands/portfolio/create.d.ts +7 -0
  70. package/dist/commands/portfolio/create.js +195 -0
  71. package/dist/commands/portfolio/start.d.ts +9 -0
  72. package/dist/commands/portfolio/start.js +64 -0
  73. package/dist/commands/portfolio/status.d.ts +6 -0
  74. package/dist/commands/portfolio/status.js +128 -0
  75. package/dist/commands/portfolio/stop.d.ts +6 -0
  76. package/dist/commands/portfolio/stop.js +81 -0
  77. package/dist/commands/portfolio-backtest.d.ts +13 -0
  78. package/dist/commands/portfolio-backtest.js +37 -0
  79. package/dist/commands/portfolio-matrix.d.ts +12 -0
  80. package/dist/commands/portfolio-matrix.js +30 -0
  81. package/dist/commands/quick-test.d.ts +17 -0
  82. package/dist/commands/quick-test.js +45 -0
  83. package/dist/commands/research.d.ts +57 -0
  84. package/dist/commands/research.js +1976 -0
  85. package/dist/commands/scout.d.ts +14 -0
  86. package/dist/commands/scout.js +184 -0
  87. package/dist/commands/serve.d.ts +9 -0
  88. package/dist/commands/serve.js +1176 -0
  89. package/dist/commands/setup/proxy.d.ts +10 -0
  90. package/dist/commands/setup/proxy.js +267 -0
  91. package/dist/commands/spot/buy.d.ts +14 -0
  92. package/dist/commands/spot/buy.js +38 -0
  93. package/dist/commands/spot/sell.d.ts +14 -0
  94. package/dist/commands/spot/sell.js +39 -0
  95. package/dist/commands/strategies/list.d.ts +6 -0
  96. package/dist/commands/strategies/list.js +34 -0
  97. package/dist/commands/sweep.d.ts +19 -0
  98. package/dist/commands/sweep.js +137 -0
  99. package/dist/commands/sync.d.ts +17 -0
  100. package/dist/commands/sync.js +54 -0
  101. package/dist/commands/test-trade.d.ts +6 -0
  102. package/dist/commands/test-trade.js +97 -0
  103. package/dist/commands/trade.d.ts +26 -0
  104. package/dist/commands/trade.js +274 -0
  105. package/dist/commands/transfer.d.ts +13 -0
  106. package/dist/commands/transfer.js +65 -0
  107. package/dist/commands/verify.d.ts +16 -0
  108. package/dist/commands/verify.js +38 -0
  109. package/dist/commands/walkforward.d.ts +20 -0
  110. package/dist/commands/walkforward.js +191 -0
  111. package/dist/commands/withdraw.d.ts +12 -0
  112. package/dist/commands/withdraw.js +55 -0
  113. package/dist/commands/workbench-create.d.ts +13 -0
  114. package/dist/commands/workbench-create.js +39 -0
  115. package/dist/lib/account-mode.d.ts +44 -0
  116. package/dist/lib/account-mode.js +96 -0
  117. package/dist/lib/analyzer.d.ts +4 -0
  118. package/dist/lib/analyzer.js +62 -0
  119. package/dist/lib/base-command.d.ts +35 -0
  120. package/dist/lib/base-command.js +49 -0
  121. package/dist/lib/credentials.d.ts +46 -0
  122. package/dist/lib/credentials.js +137 -0
  123. package/dist/lib/engine-passthrough.d.ts +28 -0
  124. package/dist/lib/engine-passthrough.js +60 -0
  125. package/dist/lib/fees.d.ts +52 -0
  126. package/dist/lib/fees.js +97 -0
  127. package/dist/lib/python-bridge.d.ts +24 -0
  128. package/dist/lib/python-bridge.js +182 -0
  129. package/dist/lib/setup-status.d.ts +32 -0
  130. package/dist/lib/setup-status.js +121 -0
  131. package/dist/lib/status-footer.d.ts +35 -0
  132. package/dist/lib/status-footer.js +101 -0
  133. package/dist/lib/tui.d.ts +130 -0
  134. package/dist/lib/tui.js +300 -0
  135. package/dist/lib/walletconnect.d.ts +70 -0
  136. package/dist/lib/walletconnect.js +407 -0
  137. package/package.json +49 -0
@@ -0,0 +1,6 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ export default class TestTrade extends GatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,97 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ import { loadCredentials, hasFullSetup, getAccountAddress } from '../lib/credentials.js';
3
+ import { runEngine } from '../lib/python-bridge.js';
4
+ import { createInterface } from 'node:readline';
5
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
6
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
7
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
8
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
9
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
10
+ function ask(question) {
11
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
12
+ return new Promise(resolve => {
13
+ rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
14
+ });
15
+ }
16
+ export default class TestTrade extends GatedCommand {
17
+ static description = 'Place a minimum-size test trade to verify exchange connectivity';
18
+ static examples = [
19
+ '$ rift test-trade',
20
+ ];
21
+ async run() {
22
+ if (!hasFullSetup()) {
23
+ this.log('');
24
+ this.log(` ${red('Not set up.')} Run ${cyan('rift auth setup')} first.`);
25
+ this.log('');
26
+ return;
27
+ }
28
+ const creds = loadCredentials();
29
+ this.log('');
30
+ this.log(` ${bold('╔═══════════════════════════════════════════╗')}`);
31
+ this.log(` ${bold('║ RIFT Exchange Test ║')}`);
32
+ this.log(` ${bold('╚═══════════════════════════════════════════╝')}`);
33
+ this.log('');
34
+ this.log(` This will:`);
35
+ this.log(` ${cyan('1.')} Connect to Hyperliquid`);
36
+ this.log(` ${cyan('2.')} Place a minimum-size BTC long ($10)`);
37
+ this.log(` ${cyan('3.')} Verify the stop loss is placed`);
38
+ this.log(` ${cyan('4.')} Wait 10 seconds`);
39
+ this.log(` ${cyan('5.')} Close the position`);
40
+ this.log(` ${cyan('6.')} Report results`);
41
+ this.log('');
42
+ const mainAddr = getAccountAddress(creds);
43
+ this.log(` ${dim('Wallet:')} ${mainAddr}`);
44
+ this.log(` ${dim('Cost:')} ~$0.07 in fees (2x $0.035 per side)`);
45
+ this.log('');
46
+ const confirm = await ask(` ${cyan('Run test?')} ${dim('(yes/no)')}: `);
47
+ if (confirm.toLowerCase() !== 'yes' && confirm.toLowerCase() !== 'y') {
48
+ this.log(dim('\n Cancelled.\n'));
49
+ return;
50
+ }
51
+ this.log('');
52
+ const engineArgs = [
53
+ '--private-key', creds.private_key,
54
+ '--account', mainAddr,
55
+ ];
56
+ try {
57
+ await runEngine('test-trade', [
58
+ '--account', mainAddr,
59
+ ], (msg) => {
60
+ if (msg.type === 'status') {
61
+ const icon = String(msg.msg).includes('✔') ? '' : ' ';
62
+ this.log(`${icon}${msg.msg}`);
63
+ }
64
+ else if (msg.type === 'error') {
65
+ this.log(` ${red('✘')} ${msg.msg}`);
66
+ }
67
+ else if (msg.type === 'result') {
68
+ this.log('');
69
+ this.log(` ${bold('═'.repeat(45))}`);
70
+ this.log('');
71
+ if (msg.success) {
72
+ this.log(` ${green('✔ TEST PASSED')} — Exchange connectivity verified`);
73
+ this.log('');
74
+ this.log(` ${dim('Entry price:')} $${msg.entry_price}`);
75
+ this.log(` ${dim('Exit price:')} $${msg.exit_price}`);
76
+ this.log(` ${dim('P&L:')} $${msg.pnl}`);
77
+ this.log(` ${dim('Stop loss:')} ${msg.stop_placed ? green('✔ placed') : red('✘ failed')}`);
78
+ this.log(` ${dim('Close:')} ${msg.close_success ? green('✔ clean') : red('✘ failed')}`);
79
+ }
80
+ else {
81
+ this.log(` ${red('✘ TEST FAILED')} — ${msg.error || 'Unknown error'}`);
82
+ }
83
+ this.log('');
84
+ this.log(` ${bold('═'.repeat(45))}`);
85
+ this.log('');
86
+ }
87
+ }, { HYPERLIQUID_PRIVATE_KEY: creds.private_key });
88
+ }
89
+ catch (error) {
90
+ this.log(` ${red('✘')} Test failed: ${error?.message}`);
91
+ }
92
+ finally {
93
+ // Clean up private key from environment
94
+ delete process.env.HYPERLIQUID_PRIVATE_KEY;
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,26 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ export default class Trade extends GatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ pair: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ direction: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ size: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
+ stop: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ leverage: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ private dashboardActive;
16
+ private ds;
17
+ private tickTimer;
18
+ private sessionStart;
19
+ private static readonly DASHBOARD_HEIGHT;
20
+ run(): Promise<void>;
21
+ private executeTrade;
22
+ private handleTradeEvent;
23
+ private renderTradeReplay;
24
+ private clearDashboard;
25
+ private renderDashboard;
26
+ }
@@ -0,0 +1,274 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { createInterface } from 'node:readline';
3
+ import { GatedCommand } from '../lib/base-command.js';
4
+ import { runEngine } from '../lib/python-bridge.js';
5
+ import { loadCredentials, hasFullSetup, getAccountAddress } from '../lib/credentials.js';
6
+ import { BUILDER_FEE_DISPLAY } from '../lib/fees.js';
7
+ import { green, red, yellow, cyan, bold, dim, greenBg, colorPnl, sparkline, proximityBar, fundingCountdown, createDashboardState, updateDashboardState, } from '../lib/tui.js';
8
+ function ask(question) {
9
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
10
+ return new Promise(resolve => {
11
+ rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
12
+ });
13
+ }
14
+ export default class Trade extends GatedCommand {
15
+ static description = 'Place a manual trade with stop loss and live monitoring';
16
+ static examples = [
17
+ '$ rift trade ETH long --size 500',
18
+ '$ rift trade SOL short --size 1000 --stop 3',
19
+ '$ rift trade',
20
+ ];
21
+ static args = {
22
+ pair: Args.string({ description: 'Coin (e.g. BTC, ETH, SOL)', required: false }),
23
+ direction: Args.string({ description: 'long or short', required: false }),
24
+ };
25
+ static flags = {
26
+ size: Flags.integer({ description: 'Position size in USD', default: 0 }),
27
+ stop: Flags.string({ description: 'Stop loss % (default: 2)', default: '2' }),
28
+ leverage: Flags.integer({ description: 'Leverage', default: 1 }),
29
+ // Internal: skip the "Type GO to execute" confirmation prompt. Used
30
+ // by `rift perp long/short` which already require the user to type
31
+ // the direction explicitly in the verb (e.g. `rift perp long BTC
32
+ // --size 10`), so the second confirmation is redundant. The flag is
33
+ // intentionally undocumented in the user-facing description — it
34
+ // exists for internal delegation, not for users to type directly.
35
+ yes: Flags.boolean({ description: 'Skip the GO confirmation prompt (for internal delegation)', default: false, hidden: true }),
36
+ };
37
+ dashboardActive = false;
38
+ ds = createDashboardState();
39
+ tickTimer = null;
40
+ sessionStart = 0;
41
+ static DASHBOARD_HEIGHT = 14;
42
+ async run() {
43
+ const { args, flags } = await this.parse(Trade);
44
+ if (!hasFullSetup()) {
45
+ this.log(`\n ${red('✘')} Trade requires wallet setup. Run: ${cyan('rift auth setup')}\n`);
46
+ return;
47
+ }
48
+ const creds = loadCredentials();
49
+ if (!creds) {
50
+ this.log(`\n ${red('✘')} No credentials.\n`);
51
+ return;
52
+ }
53
+ // Interactive mode if no args
54
+ let pair = args.pair || '';
55
+ let direction = args.direction || '';
56
+ let sizeUsd = flags.size || 0;
57
+ const stopPct = parseFloat(flags.stop) / 100;
58
+ if (!pair) {
59
+ this.log('');
60
+ this.log(` ${bold('Quick Trade')}`);
61
+ this.log(` ${dim('─'.repeat(40))}`);
62
+ this.log('');
63
+ pair = (await ask(` ${cyan('Coin')} ${dim('(BTC)')}: `)) || 'BTC';
64
+ }
65
+ if (!direction) {
66
+ direction = await ask(` ${cyan('Direction')} ${dim('(long/short)')}: `);
67
+ if (!direction || !['long', 'short'].includes(direction.toLowerCase())) {
68
+ this.log(` ${dim('Cancelled.')}`);
69
+ return;
70
+ }
71
+ }
72
+ if (!sizeUsd) {
73
+ const sizeStr = await ask(` ${cyan('Size in USD')} ${dim('(500)')}: `);
74
+ sizeUsd = parseInt(sizeStr) || 500;
75
+ }
76
+ pair = pair.toUpperCase();
77
+ direction = direction.toLowerCase();
78
+ const dirColor = direction === 'long' ? green : red;
79
+ // Confirmation
80
+ this.log('');
81
+ this.log(` ${bold('╔════════════════════════════════════════╗')}`);
82
+ this.log(` ${bold('║')} ${dirColor(direction.toUpperCase())} ${bold(pair)} $${sizeUsd.toLocaleString()}${' '.repeat(Math.max(1, 22 - pair.length - String(sizeUsd).length))}${bold('║')}`);
83
+ this.log(` ${bold('║')} Stop: ${(stopPct * 100).toFixed(1)}% Fee: ${BUILDER_FEE_DISPLAY}${' '.repeat(17)}${bold('║')}`);
84
+ this.log(` ${bold('║')} Leverage: ${flags.leverage}x${' '.repeat(27)}${bold('║')}`);
85
+ this.log(` ${bold('╚════════════════════════════════════════╝')}`);
86
+ this.log('');
87
+ if (!flags.yes) {
88
+ const confirm = await ask(` ${cyan('Type "GO" to execute')}: `);
89
+ if (confirm !== 'GO') {
90
+ this.log(dim('\n Cancelled.\n'));
91
+ return;
92
+ }
93
+ }
94
+ this.log('');
95
+ await this.executeTrade(pair, direction, sizeUsd, stopPct, flags.leverage, creds);
96
+ }
97
+ async executeTrade(pair, direction, sizeUsd, stopPct, leverage, creds) {
98
+ this.sessionStart = Date.now();
99
+ this.dashboardActive = false;
100
+ this.ds = createDashboardState();
101
+ this.ds.isLive = true;
102
+ process.env.HYPERLIQUID_PRIVATE_KEY = creds.private_key;
103
+ const engineArgs = [
104
+ pair, direction,
105
+ '--size', String(sizeUsd),
106
+ '--stop', String(stopPct),
107
+ '--leverage', String(leverage),
108
+ '--account', getAccountAddress(creds),
109
+ ];
110
+ // Dashboard tick
111
+ this.tickTimer = setInterval(() => {
112
+ if (this.dashboardActive)
113
+ this.renderDashboard();
114
+ }, 200);
115
+ // Ctrl+C closes position (not detach — this is manual trading)
116
+ const { getEngineProcess } = await import('../lib/python-bridge.js');
117
+ let enginePromise = null;
118
+ const sigintHandler = () => {
119
+ if (enginePromise) {
120
+ const proc = getEngineProcess(enginePromise);
121
+ if (proc && !proc.killed)
122
+ proc.kill('SIGTERM');
123
+ }
124
+ };
125
+ process.on('SIGINT', sigintHandler);
126
+ try {
127
+ enginePromise = runEngine('manual-trade', engineArgs, (msg) => {
128
+ const state = msg.state;
129
+ if (msg.type === 'status') {
130
+ if (!this.dashboardActive) {
131
+ this.log(` ${dim(String(msg.msg))}`);
132
+ }
133
+ }
134
+ else if (msg.type === 'trade') {
135
+ this.handleTradeEvent(msg);
136
+ }
137
+ else if (msg.type === 'heartbeat') {
138
+ if (state) {
139
+ updateDashboardState(this.ds, state, msg, 'heartbeat');
140
+ this.renderDashboard();
141
+ }
142
+ }
143
+ else if (msg.type === 'shutdown') {
144
+ if (this.tickTimer) {
145
+ clearInterval(this.tickTimer);
146
+ this.tickTimer = null;
147
+ }
148
+ if (this.dashboardActive)
149
+ this.clearDashboard();
150
+ if (msg.trade_replay) {
151
+ this.renderTradeReplay(msg.trade_replay);
152
+ }
153
+ // Shareable card
154
+ const card = msg.shareable_card;
155
+ if (card) {
156
+ this.log('');
157
+ this.log(dim(' Shareable:'));
158
+ this.log('');
159
+ for (const line of card.split('\n')) {
160
+ this.log(` ${line}`);
161
+ }
162
+ }
163
+ this.log('');
164
+ }
165
+ else if (msg.type === 'error') {
166
+ this.log(` ${red('✘')} ${msg.msg}`);
167
+ }
168
+ });
169
+ await enginePromise;
170
+ }
171
+ catch (error) {
172
+ const errMsg = String(error?.message ?? '');
173
+ if (!errMsg.includes('SIGTERM') && !errMsg.includes('SIGINT') && !errMsg.includes('null')) {
174
+ this.log(` ${red('✘')} ${errMsg.split('\n')[0] || 'Unknown error'}`);
175
+ }
176
+ }
177
+ finally {
178
+ if (this.tickTimer) {
179
+ clearInterval(this.tickTimer);
180
+ this.tickTimer = null;
181
+ }
182
+ process.removeListener('SIGINT', sigintHandler);
183
+ delete process.env.HYPERLIQUID_PRIVATE_KEY;
184
+ }
185
+ }
186
+ handleTradeEvent(msg) {
187
+ const action = msg.action;
188
+ if (this.dashboardActive)
189
+ this.clearDashboard();
190
+ if (action === 'open') {
191
+ const side = msg.side ?? '';
192
+ const price = msg.price ?? 0;
193
+ const size = msg.size ?? 0;
194
+ const sl = msg.stop_loss ?? 0;
195
+ const sideColor = side === 'long' ? green : red;
196
+ this.log(` ${greenBg(' TRADE ')} ${sideColor('▶')} ${bold(side.toUpperCase())} ${size.toFixed(4)} @ $${price.toLocaleString()}`);
197
+ this.log(` ${dim('Stop:')} $${sl.toLocaleString()} ${dim('(on Hyperliquid)')}`);
198
+ this.log(` ${dim('Press Ctrl+C to close position')}`);
199
+ this.log('');
200
+ }
201
+ else if (action === 'stop_loss') {
202
+ this.log(` ${red('✘')} Stop loss triggered ${dim('(Hyperliquid server-side)')}`);
203
+ }
204
+ }
205
+ renderTradeReplay(replay) {
206
+ const isWin = replay.result === 'WIN';
207
+ const resultColor = isWin ? green : red;
208
+ const side = replay.side.toUpperCase();
209
+ this.log('');
210
+ this.log(` ${greenBg(' RESULT ')} ${resultColor(side)} — ${resultColor(replay.result)}`);
211
+ this.log(` Entry: $${replay.entry_price} Exit: $${replay.exit_price}`);
212
+ this.log(` ${bold('P&L:')} ${colorPnl(replay.total_pnl)} (${colorPnl(replay.pnl_pct, '%')})`);
213
+ if (replay.funding_pnl)
214
+ this.log(` Funding: ${colorPnl(replay.funding_pnl)}`);
215
+ this.log(` Duration: ${replay.duration}`);
216
+ }
217
+ clearDashboard() {
218
+ if (this.dashboardActive) {
219
+ const h = Trade.DASHBOARD_HEIGHT;
220
+ process.stdout.write(`\x1b[${h}A`);
221
+ for (let i = 0; i < h; i++)
222
+ process.stdout.write('\x1b[2K\n');
223
+ process.stdout.write(`\x1b[${h}A`);
224
+ this.dashboardActive = false;
225
+ }
226
+ }
227
+ renderDashboard() {
228
+ const ds = this.ds;
229
+ const pos = ds.position;
230
+ const elapsed = Math.floor((Date.now() - this.sessionStart) / 1000);
231
+ const mins = Math.floor(elapsed / 60);
232
+ const secs = elapsed % 60;
233
+ const duration = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
234
+ const lines = [];
235
+ // Header
236
+ lines.push(` ${greenBg(' TRADE ')} ${dim(ds.pair || '')} ${dim('│')} ${dim(duration)} ${dim('│ Ctrl+C to close')}`);
237
+ // Price
238
+ const priceStr = ds.price > 0 ? `$${ds.price.toFixed(2)}` : '$...';
239
+ const deltaRaw = ds.priceDelta > 0 ? green(`+$${ds.priceDelta.toFixed(0)}`)
240
+ : ds.priceDelta < 0 ? red(`-$${Math.abs(ds.priceDelta).toFixed(0)}`)
241
+ : dim('$0');
242
+ const chart = sparkline(ds.priceHistory, 28);
243
+ lines.push(` ${bold(priceStr)} ${deltaRaw} ${chart}`);
244
+ lines.push('');
245
+ // Position
246
+ if (pos) {
247
+ const sideColor = pos.side === 'long' ? green : red;
248
+ lines.push(` ${sideColor('●')} ${sideColor(String(pos.side).toUpperCase())} ${pos.size?.toFixed(4)} @ $${pos.entry_price?.toLocaleString()}`);
249
+ lines.push(` ${dim('Unrealized:')} ${colorPnl(ds.unrealizedPnl)} ${dim('Funding:')} ${colorPnl(ds.totalFunding)}`);
250
+ if (ds.stopProximity > 0.1) {
251
+ const proxLabel = ds.stopProximity > 0.8 ? red('DANGER') : ds.stopProximity > 0.5 ? yellow('CAUTION') : dim('safe');
252
+ lines.push(` ${dim('SL')} ${proximityBar(ds.stopProximity)} ${proxLabel}`);
253
+ }
254
+ if (ds.fundingCountdownMin > 0) {
255
+ lines.push(` ${fundingCountdown(ds.fundingCountdownMin, ds.predictedFunding)}`);
256
+ }
257
+ }
258
+ lines.push('');
259
+ lines.push(` ${dim('EQUITY')} ${bold('$' + ds.totalEquity.toLocaleString())} ${colorPnl(ds.totalPnlPct, '%')}`);
260
+ // Fixed height
261
+ const H = Trade.DASHBOARD_HEIGHT;
262
+ while (lines.length < H)
263
+ lines.push('');
264
+ if (lines.length > H)
265
+ lines.length = H;
266
+ if (this.dashboardActive) {
267
+ process.stdout.write(`\x1b[${H}A`);
268
+ }
269
+ for (const line of lines) {
270
+ process.stdout.write(`\x1b[2K${line}\n`);
271
+ }
272
+ this.dashboardActive = true;
273
+ }
274
+ }
@@ -0,0 +1,13 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ export default class Transfer extends GatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ amount: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ 'to-perps': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'to-spot': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,65 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { GatedCommand } from '../lib/base-command.js';
3
+ import { requestTransfer, postToHyperliquid, getExistingSession } from '../lib/walletconnect.js';
4
+ import { loadCredentials } from '../lib/credentials.js';
5
+ import { green, red, cyan, dim } from '../lib/tui.js';
6
+ export default class Transfer extends GatedCommand {
7
+ static description = 'Transfer USDC between spot and perps on Hyperliquid';
8
+ static examples = [
9
+ '$ rift transfer 100 --to-perps',
10
+ '$ rift transfer 50 --to-spot',
11
+ ];
12
+ static args = {
13
+ amount: Args.string({ description: 'USDC amount to transfer', required: true }),
14
+ };
15
+ static flags = {
16
+ 'to-perps': Flags.boolean({ description: 'Transfer from Spot → Perps', default: false }),
17
+ 'to-spot': Flags.boolean({ description: 'Transfer from Perps → Spot', default: false }),
18
+ };
19
+ async run() {
20
+ const { args, flags } = await this.parse(Transfer);
21
+ const amount = args.amount;
22
+ const isMainnet = true;
23
+ // Determine direction
24
+ let toPerp = true;
25
+ if (flags['to-spot']) {
26
+ toPerp = false;
27
+ }
28
+ else if (!flags['to-perps'] && !flags['to-spot']) {
29
+ // Default to to-perps if neither specified
30
+ toPerp = true;
31
+ }
32
+ const direction = toPerp ? 'Spot → Perps' : 'Perps → Spot';
33
+ const creds = loadCredentials();
34
+ if (!creds) {
35
+ this.log(` ${red('✘')} No wallet configured. Run: ${cyan('rift auth setup')}`);
36
+ return;
37
+ }
38
+ // Check for existing WalletConnect session
39
+ const session = await getExistingSession();
40
+ if (!session) {
41
+ this.log(` ${red('✘')} No wallet session. Run: ${cyan('rift auth setup')} to reconnect.`);
42
+ return;
43
+ }
44
+ this.log('');
45
+ this.log(` Transferring $${amount} (${direction})...`);
46
+ this.log(` ${dim('→ Approve in your wallet (check your phone)')}`);
47
+ this.log('');
48
+ const result = await requestTransfer(amount, toPerp, isMainnet);
49
+ if (!result.success) {
50
+ this.log(` ${red('✘')} Transfer failed: ${result.error}`);
51
+ return;
52
+ }
53
+ // Post to Hyperliquid
54
+ try {
55
+ const response = await postToHyperliquid(result.action, result.signature, result.nonce, isMainnet);
56
+ this.log(` ${green('✔')} Transfer complete`);
57
+ this.log(` ${dim(`$${amount} moved ${direction}`)}`);
58
+ this.log(` ${dim('Run: rift balance to verify')}`);
59
+ this.log('');
60
+ }
61
+ catch (error) {
62
+ this.log(` ${red('✘')} Failed to submit transfer: ${error.message}`);
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,16 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ export default class Verify extends GatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ strategy: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ pair: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ tf: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ from: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ to: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,38 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { GatedCommand } from '../lib/base-command.js';
3
+ import { passthroughToEngine } from '../lib/engine-passthrough.js';
4
+ export default class Verify extends GatedCommand {
5
+ static description = 'Verify a strategy beats buy-and-hold over a date range — sanity check before going live';
6
+ static examples = [
7
+ '$ rift verify trend_follow',
8
+ '$ rift verify trend_follow --pair BTC --tf 4h --from 2024-01-01 --to 2024-12-31',
9
+ ];
10
+ static args = {
11
+ strategy: Args.string({ description: 'Strategy name', required: true }),
12
+ };
13
+ static flags = {
14
+ pair: Flags.string({ description: 'Trading pair', default: 'BTC' }),
15
+ tf: Flags.string({ description: 'Timeframe', default: '' }),
16
+ from: Flags.string({ description: 'Start date YYYY-MM-DD', default: '' }),
17
+ to: Flags.string({ description: 'End date YYYY-MM-DD', default: '' }),
18
+ json: Flags.boolean({ description: 'Emit raw JSON only', default: false }),
19
+ };
20
+ async run() {
21
+ const { args, flags } = await this.parse(Verify);
22
+ const engineArgs = [args.strategy, '--pair', flags.pair];
23
+ if (flags.tf)
24
+ engineArgs.push('--tf', flags.tf);
25
+ if (flags.from)
26
+ engineArgs.push('--from', flags.from);
27
+ if (flags.to)
28
+ engineArgs.push('--to', flags.to);
29
+ await passthroughToEngine({
30
+ command: 'verify',
31
+ args: engineArgs,
32
+ log: (m) => this.log(m),
33
+ error: (m) => this.error(m),
34
+ exit: (c) => this.exit(c),
35
+ jsonOnly: flags.json,
36
+ });
37
+ }
38
+ }
@@ -0,0 +1,20 @@
1
+ import { GatedCommand } from '../lib/base-command.js';
2
+ export default class WalkForward extends GatedCommand {
3
+ static description: string;
4
+ static aliases: string[];
5
+ static examples: string[];
6
+ static args: {
7
+ strategy: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ pair: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ tf: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ wf: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ equity: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
14
+ leverage: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
15
+ };
16
+ run(): Promise<void>;
17
+ private renderResult;
18
+ private row;
19
+ private rowColored;
20
+ }