@megatao/sdk 1.1.0
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.
- package/.env.example +37 -0
- package/CHANGELOG.md +19 -0
- package/README.md +199 -0
- package/bin/alf +4 -0
- package/cli/README.md +198 -0
- package/cli/TEST_MANUAL.md +577 -0
- package/cli/commands/account.ts +545 -0
- package/cli/commands/funding.ts +481 -0
- package/cli/commands/liquidation.ts +523 -0
- package/cli/commands/market.ts +590 -0
- package/cli/commands/orders.ts +395 -0
- package/cli/commands/position.ts +1085 -0
- package/cli/commands/shared/positionUtils.ts +239 -0
- package/cli/commands/trading.ts +483 -0
- package/cli/commands/utils.ts +281 -0
- package/cli/commands/vault.ts +522 -0
- package/cli/index.ts +169 -0
- package/cli/interactive.ts +530 -0
- package/cli/utils/client.ts +457 -0
- package/cli/utils/config.ts +226 -0
- package/cli/utils/display.ts +258 -0
- package/cli/utils/index.ts +10 -0
- package/cli/utils/prompts.ts +364 -0
- package/config.example.json +23 -0
- package/dist/AlphaFuturesClient.d.ts +36 -0
- package/dist/AlphaFuturesClient.d.ts.map +1 -0
- package/dist/AlphaFuturesClient.js +116 -0
- package/dist/AlphaFuturesClient.js.map +1 -0
- package/dist/abi/Alpha.json +5987 -0
- package/dist/abi/abis.d.ts +319 -0
- package/dist/abi/abis.d.ts.map +1 -0
- package/dist/abi/abis.js +128 -0
- package/dist/abi/abis.js.map +1 -0
- package/dist/abi/index.d.ts +11 -0
- package/dist/abi/index.d.ts.map +1 -0
- package/dist/abi/index.js +15 -0
- package/dist/abi/index.js.map +1 -0
- package/dist/config/contracts.config.d.ts +70 -0
- package/dist/config/contracts.config.d.ts.map +1 -0
- package/dist/config/contracts.config.js +137 -0
- package/dist/config/contracts.config.js.map +1 -0
- package/dist/config/environments/alpha.config.d.ts +17 -0
- package/dist/config/environments/alpha.config.d.ts.map +1 -0
- package/dist/config/environments/alpha.config.js +140 -0
- package/dist/config/environments/alpha.config.js.map +1 -0
- package/dist/config/environments/beta.config.d.ts +16 -0
- package/dist/config/environments/beta.config.d.ts.map +1 -0
- package/dist/config/environments/beta.config.js +131 -0
- package/dist/config/environments/beta.config.js.map +1 -0
- package/dist/config/environments/dev.config.d.ts +13 -0
- package/dist/config/environments/dev.config.d.ts.map +1 -0
- package/dist/config/environments/dev.config.js +123 -0
- package/dist/config/environments/dev.config.js.map +1 -0
- package/dist/config/environments/index.d.ts +48 -0
- package/dist/config/environments/index.d.ts.map +1 -0
- package/dist/config/environments/index.js +81 -0
- package/dist/config/environments/index.js.map +1 -0
- package/dist/config/environments/localhost.config.d.ts +16 -0
- package/dist/config/environments/localhost.config.d.ts.map +1 -0
- package/dist/config/environments/localhost.config.js +152 -0
- package/dist/config/environments/localhost.config.js.map +1 -0
- package/dist/config/environments/prod.config.d.ts +20 -0
- package/dist/config/environments/prod.config.d.ts.map +1 -0
- package/dist/config/environments/prod.config.js +143 -0
- package/dist/config/environments/prod.config.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +41 -0
- package/dist/config/index.js.map +1 -0
- package/dist/constants/assets.d.ts +76 -0
- package/dist/constants/assets.d.ts.map +1 -0
- package/dist/constants/assets.js +277 -0
- package/dist/constants/assets.js.map +1 -0
- package/dist/constants/contracts.d.ts +41 -0
- package/dist/constants/contracts.d.ts.map +1 -0
- package/dist/constants/contracts.js +57 -0
- package/dist/constants/contracts.js.map +1 -0
- package/dist/constants/index.d.ts +36 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +75 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/networks.d.ts +32 -0
- package/dist/constants/networks.d.ts.map +1 -0
- package/dist/constants/networks.js +174 -0
- package/dist/constants/networks.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +21 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/viem/AlphaViem.d.ts +518 -0
- package/dist/contracts/viem/AlphaViem.d.ts.map +1 -0
- package/dist/contracts/viem/AlphaViem.js +1287 -0
- package/dist/contracts/viem/AlphaViem.js.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts +71 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.js +212 -0
- package/dist/contracts/viem/PriceOracleViem.js.map +1 -0
- package/dist/contracts/viem/index.d.ts +9 -0
- package/dist/contracts/viem/index.d.ts.map +1 -0
- package/dist/contracts/viem/index.js +17 -0
- package/dist/contracts/viem/index.js.map +1 -0
- package/dist/errors/index.d.ts +44 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +83 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/types/alpha.d.ts +299 -0
- package/dist/types/alpha.d.ts.map +1 -0
- package/dist/types/alpha.js +6 -0
- package/dist/types/alpha.js.map +1 -0
- package/dist/types/client.d.ts +24 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +13 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/contracts.d.ts +48 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +6 -0
- package/dist/types/contracts.js.map +1 -0
- package/dist/types/funding.d.ts +27 -0
- package/dist/types/funding.d.ts.map +1 -0
- package/dist/types/funding.js +6 -0
- package/dist/types/funding.js.map +1 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +47 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/liquidation.d.ts +20 -0
- package/dist/types/liquidation.d.ts.map +1 -0
- package/dist/types/liquidation.js +6 -0
- package/dist/types/liquidation.js.map +1 -0
- package/dist/types/margin.d.ts +29 -0
- package/dist/types/margin.d.ts.map +1 -0
- package/dist/types/margin.js +6 -0
- package/dist/types/margin.js.map +1 -0
- package/dist/types/oracle.d.ts +21 -0
- package/dist/types/oracle.d.ts.map +1 -0
- package/dist/types/oracle.js +6 -0
- package/dist/types/oracle.js.map +1 -0
- package/dist/types/positions.d.ts +43 -0
- package/dist/types/positions.d.ts.map +1 -0
- package/dist/types/positions.js +13 -0
- package/dist/types/positions.js.map +1 -0
- package/dist/utils/calculations.d.ts +84 -0
- package/dist/utils/calculations.d.ts.map +1 -0
- package/dist/utils/calculations.js +155 -0
- package/dist/utils/calculations.js.map +1 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +129 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/events.d.ts +40 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +73 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/format.d.ts +40 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +86 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/network.d.ts +52 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +192 -0
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/positionCalculations.d.ts +145 -0
- package/dist/utils/positionCalculations.d.ts.map +1 -0
- package/dist/utils/positionCalculations.js +278 -0
- package/dist/utils/positionCalculations.js.map +1 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +68 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/README.md +40 -0
- package/docs/api/API.md +831 -0
- package/docs/guides/GETTING_STARTED.md +316 -0
- package/docs/guides/TRADING_GUIDE.md +677 -0
- package/docs/integration/INTEGRATION_GUIDE.md +1679 -0
- package/docs/integration/VIEM_INTEGRATION.md +294 -0
- package/docs/reference/CLI_QUICK_REFERENCE.md +197 -0
- package/docs/reference/TROUBLESHOOTING.md +922 -0
- package/package.json +113 -0
- package/src/AlphaFuturesClient.ts +158 -0
- package/src/abi/.gitkeep +1 -0
- package/src/abi/Alpha.json +5987 -0
- package/src/abi/README.md +99 -0
- package/src/abi/abis.ts +131 -0
- package/src/abi/index.ts +13 -0
- package/src/config/contracts.config.ts +186 -0
- package/src/config/environments/alpha.config.ts +139 -0
- package/src/config/environments/beta.config.ts +130 -0
- package/src/config/environments/dev.config.ts +122 -0
- package/src/config/environments/index.ts +87 -0
- package/src/config/environments/localhost.config.ts +153 -0
- package/src/config/environments/prod.config.ts +142 -0
- package/src/config/index.ts +29 -0
- package/src/constants/assets.ts +299 -0
- package/src/constants/contracts.ts +64 -0
- package/src/constants/index.ts +69 -0
- package/src/constants/networks.ts +182 -0
- package/src/contracts/index.ts +5 -0
- package/src/contracts/viem/AlphaViem.ts +1615 -0
- package/src/contracts/viem/PriceOracleViem.ts +272 -0
- package/src/contracts/viem/index.ts +11 -0
- package/src/errors/index.ts +87 -0
- package/src/index.ts +59 -0
- package/src/types/VIEM_TYPES_README.md +70 -0
- package/src/types/alpha.ts +358 -0
- package/src/types/client.ts +27 -0
- package/src/types/contracts.ts +74 -0
- package/src/types/funding.ts +31 -0
- package/src/types/index.ts +108 -0
- package/src/types/liquidation.ts +23 -0
- package/src/types/margin.ts +34 -0
- package/src/types/oracle.ts +24 -0
- package/src/types/positions.ts +48 -0
- package/src/utils/calculations.ts +175 -0
- package/src/utils/errors.ts +147 -0
- package/src/utils/events.ts +98 -0
- package/src/utils/format.ts +84 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/network.ts +212 -0
- package/src/utils/positionCalculations.ts +317 -0
- package/src/utils/validation.ts +76 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trading Commands - Execute trades and manage orders
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { parseEther } from 'viem';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import inquirer from 'inquirer';
|
|
10
|
+
import { AlphaFuturesClient } from '../../src';
|
|
11
|
+
import { formatUSD, formatTAO, formatPercentage } from '../../src/utils';
|
|
12
|
+
import { getClient, handleError, getMarketAddress } from '../utils/client';
|
|
13
|
+
import { confirmAction, validateDirection } from '../utils/prompts';
|
|
14
|
+
import { displayMarketSummary, calculatePnL } from '../utils/display';
|
|
15
|
+
import { openPositionCommon } from './shared/positionUtils';
|
|
16
|
+
|
|
17
|
+
export function tradingCommands(program: Command) {
|
|
18
|
+
const trade = program.command('trade').description('Execute trades');
|
|
19
|
+
|
|
20
|
+
// Market order command (redirects to position open)
|
|
21
|
+
trade
|
|
22
|
+
.command('market')
|
|
23
|
+
.description('Execute a market order (opens a position)')
|
|
24
|
+
.requiredOption('-a, --asset <asset>', 'Asset to trade')
|
|
25
|
+
.requiredOption('-s, --side <side>', 'Trade side: buy/long or sell/short')
|
|
26
|
+
.option(
|
|
27
|
+
'-z, --size <size>',
|
|
28
|
+
'Position size in TAO (auto-calculated from margin Ă leverage if not provided)',
|
|
29
|
+
)
|
|
30
|
+
.option('-m, --margin <margin>', 'Margin amount in TAO')
|
|
31
|
+
.option('-l, --leverage <leverage>', 'Leverage (1-10x)', '3')
|
|
32
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
33
|
+
.option('--sl, --stop-loss <price>', 'Stop loss price')
|
|
34
|
+
.option('--tp, --take-profit <price>', 'Take profit price')
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
try {
|
|
37
|
+
// Auto-calculate size if margin and leverage are provided
|
|
38
|
+
let size = options.size;
|
|
39
|
+
if (!size && options.margin) {
|
|
40
|
+
const marginValue = parseFloat(options.margin);
|
|
41
|
+
const leverageValue = parseFloat(options.leverage);
|
|
42
|
+
size = (marginValue * leverageValue).toString();
|
|
43
|
+
console.log(
|
|
44
|
+
chalk.gray(
|
|
45
|
+
`Auto-calculated position size: ${size} TAO (${marginValue} TAO margin Ă ${leverageValue}x leverage)`,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
} else if (!size && !options.margin) {
|
|
49
|
+
console.error(chalk.red('Error: Either --size or --margin must be provided'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Convert trade options to position options with strict validation
|
|
54
|
+
let direction: 'long' | 'short';
|
|
55
|
+
try {
|
|
56
|
+
direction = validateDirection(options.side);
|
|
57
|
+
} catch (error: any) {
|
|
58
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const positionOptions = {
|
|
63
|
+
asset: options.asset,
|
|
64
|
+
direction,
|
|
65
|
+
size: size,
|
|
66
|
+
leverage: parseFloat(options.leverage),
|
|
67
|
+
margin: options.margin,
|
|
68
|
+
skipConfirmation: options.yes,
|
|
69
|
+
stopLoss: options.stopLoss,
|
|
70
|
+
takeProfit: options.takeProfit,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const success = await openPositionCommon(positionOptions, program.opts());
|
|
74
|
+
|
|
75
|
+
if (success && (options.stopLoss || options.takeProfit)) {
|
|
76
|
+
console.log(chalk.yellow('\nâ ď¸ Stop loss and take profit orders coming soon'));
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(chalk.red('Market order failed'));
|
|
80
|
+
handleError(error, program.opts());
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Limit order command (placeholder for orderbook integration)
|
|
85
|
+
trade
|
|
86
|
+
.command('limit')
|
|
87
|
+
.description('Place a limit order')
|
|
88
|
+
.requiredOption('-a, --asset <asset>', 'Asset to trade')
|
|
89
|
+
.requiredOption('-s, --side <side>', 'Trade side: buy/long or sell/short')
|
|
90
|
+
.requiredOption('-p, --price <price>', 'Limit price')
|
|
91
|
+
.requiredOption('-z, --size <size>', 'Order size in USD')
|
|
92
|
+
.option('-m, --margin <margin>', 'Margin amount in TAO')
|
|
93
|
+
.option('-l, --leverage <leverage>', 'Leverage (1-10x)', '3')
|
|
94
|
+
.option('--post-only', 'Post-only order (maker only)')
|
|
95
|
+
.option('--ioc', 'Immediate or cancel')
|
|
96
|
+
.option('--fok', 'Fill or kill')
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
console.log(chalk.yellow('\nâ ď¸ Limit orders are coming soon!'));
|
|
99
|
+
console.log('The orderbook module is currently being integrated.');
|
|
100
|
+
|
|
101
|
+
// Show what the order would look like
|
|
102
|
+
const asset = options.asset.toUpperCase();
|
|
103
|
+
|
|
104
|
+
// Validate direction
|
|
105
|
+
let direction: 'long' | 'short';
|
|
106
|
+
try {
|
|
107
|
+
direction = validateDirection(options.side);
|
|
108
|
+
} catch (error: any) {
|
|
109
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const price = parseEther(options.price);
|
|
114
|
+
const size = parseEther(options.size);
|
|
115
|
+
const leverage = parseFloat(options.leverage);
|
|
116
|
+
|
|
117
|
+
console.log(chalk.bold('\nđ Limit Order Preview:'));
|
|
118
|
+
console.log(` Asset: ${chalk.cyan(asset)}`);
|
|
119
|
+
const isBuy = direction === 'long';
|
|
120
|
+
console.log(` Side: ${isBuy ? chalk.green('BUY') : chalk.red('SELL')}`);
|
|
121
|
+
console.log(` Price: ${formatUSD(price)}`);
|
|
122
|
+
console.log(` Size: ${formatUSD(size)}`);
|
|
123
|
+
console.log(` Leverage: ${leverage}x`);
|
|
124
|
+
|
|
125
|
+
if (options.postOnly) console.log(` Type: ${chalk.blue('Post-Only')}`);
|
|
126
|
+
if (options.ioc) console.log(` Type: ${chalk.orange('IOC')}`);
|
|
127
|
+
if (options.fok) console.log(` Type: ${chalk.red('FOK')}`);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Quick trade command with interactive prompts
|
|
131
|
+
trade
|
|
132
|
+
.command('quick')
|
|
133
|
+
.description('Quick trade with interactive prompts')
|
|
134
|
+
.action(async () => {
|
|
135
|
+
try {
|
|
136
|
+
const client = await getClient(program.opts());
|
|
137
|
+
const alpha = client.getAlpha();
|
|
138
|
+
const userAddress = await client.getSignerAddress();
|
|
139
|
+
|
|
140
|
+
// Get available balance first
|
|
141
|
+
const available = await alpha.getAvailableMargin(userAddress);
|
|
142
|
+
console.log(chalk.cyan(`\nAvailable Balance: ${formatTAO(available)} TAO`));
|
|
143
|
+
|
|
144
|
+
// Interactive prompts
|
|
145
|
+
const answers = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'input',
|
|
148
|
+
name: 'asset',
|
|
149
|
+
message: 'Asset to trade:',
|
|
150
|
+
default: 'ALPHA',
|
|
151
|
+
transformer: (input) => input.toUpperCase(),
|
|
152
|
+
filter: (input) => input.toUpperCase(),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'list',
|
|
156
|
+
name: 'side',
|
|
157
|
+
message: 'Direction:',
|
|
158
|
+
choices: [
|
|
159
|
+
{ name: chalk.green('Long (Buy) â'), value: 'long' },
|
|
160
|
+
{ name: chalk.red('Short (Sell) â'), value: 'short' },
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
type: 'input',
|
|
165
|
+
name: 'margin',
|
|
166
|
+
message: 'Margin amount (TAO):',
|
|
167
|
+
validate: (input: string) => {
|
|
168
|
+
const trimmed = input.trim();
|
|
169
|
+
const amount = parseFloat(trimmed);
|
|
170
|
+
if (isNaN(amount) || amount <= 0) {
|
|
171
|
+
return 'Please enter a valid amount greater than 0';
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
if (parseEther(trimmed) > available) {
|
|
175
|
+
return `Insufficient balance (available: ${formatTAO(available)} TAO)`;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
return 'Please enter a valid number';
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
},
|
|
182
|
+
filter: (input: string) => input.trim(),
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: 'list',
|
|
186
|
+
name: 'leverage',
|
|
187
|
+
message: 'Leverage:',
|
|
188
|
+
choices: ['1x', '2x', '3x', '5x', '10x'],
|
|
189
|
+
default: '3x',
|
|
190
|
+
filter: (input) => parseFloat(input),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'confirm',
|
|
194
|
+
name: 'confirm',
|
|
195
|
+
message: 'Execute trade?',
|
|
196
|
+
default: true,
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
if (!answers.confirm) {
|
|
201
|
+
console.log(chalk.yellow('Trade cancelled'));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Calculate position size
|
|
206
|
+
const marginAmount = parseEther(answers.margin);
|
|
207
|
+
const positionSize = (marginAmount * BigInt(Math.floor(answers.leverage * 100))) / 100n;
|
|
208
|
+
|
|
209
|
+
// Get current price
|
|
210
|
+
const currentPrice = await client.oracle.getPrice(answers.asset);
|
|
211
|
+
|
|
212
|
+
// Show final summary
|
|
213
|
+
console.log(chalk.bold('\nđ Executing Trade:'));
|
|
214
|
+
console.log(
|
|
215
|
+
` ${answers.side === 'long' ? chalk.green('LONG') : chalk.red('SHORT')} ${answers.asset}`,
|
|
216
|
+
);
|
|
217
|
+
console.log(` Size: ${formatUSD(positionSize)}`);
|
|
218
|
+
console.log(` Entry: ${formatUSD(currentPrice)}`);
|
|
219
|
+
|
|
220
|
+
const spinner = ora('Opening position...').start();
|
|
221
|
+
|
|
222
|
+
const marketAddress = getMarketAddress(asset);
|
|
223
|
+
const leverageBigInt = BigInt(Math.floor(answers.leverage * 100));
|
|
224
|
+
const hash = await alpha.openPosition(
|
|
225
|
+
marketAddress,
|
|
226
|
+
answers.side === 'long',
|
|
227
|
+
positionSize,
|
|
228
|
+
leverageBigInt,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const publicClient = client.getPublicClient();
|
|
232
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
233
|
+
spinner.succeed('Trade executed successfully!');
|
|
234
|
+
|
|
235
|
+
// Parse position opened event
|
|
236
|
+
const positionEvent = alpha.parsePositionOpenedEvent(receipt);
|
|
237
|
+
if (positionEvent) {
|
|
238
|
+
console.log(chalk.green(`\nâ Position ID: ${positionEvent.positionId}`));
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
handleError(error, program.opts());
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Close all positions command
|
|
246
|
+
trade
|
|
247
|
+
.command('close-all')
|
|
248
|
+
.description('Close all open positions')
|
|
249
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
250
|
+
.action(async (options) => {
|
|
251
|
+
const spinner = ora('Loading positions...').start();
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const client = await getClient(program.opts());
|
|
255
|
+
const alpha = client.getAlpha();
|
|
256
|
+
const userAddress = await client.getSignerAddress();
|
|
257
|
+
|
|
258
|
+
// Get all active positions
|
|
259
|
+
const positionIds = await alpha.getUserPositions(userAddress);
|
|
260
|
+
const positions = await Promise.all(positionIds.map((id) => alpha.getPosition(id)));
|
|
261
|
+
|
|
262
|
+
const activePositions = positions.filter((p) => p.size > 0n);
|
|
263
|
+
|
|
264
|
+
if (activePositions.length === 0) {
|
|
265
|
+
spinner.stop();
|
|
266
|
+
console.log(chalk.yellow('\nNo active positions to close'));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
spinner.stop();
|
|
271
|
+
|
|
272
|
+
// Calculate total P&L
|
|
273
|
+
let totalPnL = 0n;
|
|
274
|
+
console.log(chalk.bold(`\nđ Closing ${activePositions.length} positions:`));
|
|
275
|
+
|
|
276
|
+
const currentPrice = await client.oracle.getPrice('ALPHA'); // Placeholder asset
|
|
277
|
+
|
|
278
|
+
for (const pos of activePositions) {
|
|
279
|
+
const pnl = calculatePnL(pos, currentPrice, pos.size);
|
|
280
|
+
|
|
281
|
+
totalPnL += pnl;
|
|
282
|
+
|
|
283
|
+
console.log(
|
|
284
|
+
` Position: ALPHA ${pos.isLong ? 'LONG' : 'SHORT'} - P&L: ${pnl >= 0 ? chalk.green('+' + formatUSD(pnl)) : chalk.red(formatUSD(pnl))}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log(
|
|
289
|
+
chalk.bold(
|
|
290
|
+
`\nTotal P&L: ${totalPnL >= 0 ? chalk.green('+' + formatUSD(totalPnL)) : chalk.red(formatUSD(totalPnL))}`,
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Confirm
|
|
295
|
+
if (!options.yes) {
|
|
296
|
+
const confirmed = await confirmAction('Close all positions?');
|
|
297
|
+
if (!confirmed) {
|
|
298
|
+
console.log(chalk.yellow('Cancelled'));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
spinner.start('Closing positions...');
|
|
304
|
+
|
|
305
|
+
// Close all positions
|
|
306
|
+
const results = await Promise.allSettled(
|
|
307
|
+
activePositions.map((pos, index) => alpha.closePosition(positionIds[index], pos.size)),
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
spinner.stop();
|
|
311
|
+
|
|
312
|
+
// Report results
|
|
313
|
+
const successful = results.filter((r) => r.status === 'fulfilled').length;
|
|
314
|
+
const failed = results.filter((r) => r.status === 'rejected').length;
|
|
315
|
+
|
|
316
|
+
if (successful > 0) {
|
|
317
|
+
console.log(chalk.green(`\nâ Successfully closed ${successful} positions`));
|
|
318
|
+
}
|
|
319
|
+
if (failed > 0) {
|
|
320
|
+
console.log(chalk.red(`â Failed to close ${failed} positions`));
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
spinner.fail('Failed to close positions');
|
|
324
|
+
handleError(error, program.opts());
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Trading calculator
|
|
329
|
+
trade
|
|
330
|
+
.command('calc')
|
|
331
|
+
.description('Trading calculator for position sizing and risk management')
|
|
332
|
+
.action(async () => {
|
|
333
|
+
try {
|
|
334
|
+
const answers = await inquirer.prompt([
|
|
335
|
+
{
|
|
336
|
+
type: 'input',
|
|
337
|
+
name: 'balance',
|
|
338
|
+
message: 'Account balance (TAO):',
|
|
339
|
+
validate: (input: string) => {
|
|
340
|
+
const value = parseFloat(input.trim());
|
|
341
|
+
if (isNaN(value)) {
|
|
342
|
+
return 'Please enter a valid number';
|
|
343
|
+
}
|
|
344
|
+
if (value <= 0) {
|
|
345
|
+
return 'Balance must be greater than 0';
|
|
346
|
+
}
|
|
347
|
+
return true;
|
|
348
|
+
},
|
|
349
|
+
filter: (input: string) => input.trim(),
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
type: 'input',
|
|
353
|
+
name: 'risk',
|
|
354
|
+
message: 'Risk per trade (%):',
|
|
355
|
+
default: '2',
|
|
356
|
+
validate: (input: string) => {
|
|
357
|
+
const value = parseFloat(input.trim());
|
|
358
|
+
if (isNaN(value)) {
|
|
359
|
+
return 'Please enter a valid number';
|
|
360
|
+
}
|
|
361
|
+
if (value <= 0 || value > 100) {
|
|
362
|
+
return 'Risk percentage must be between 0 and 100';
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
},
|
|
366
|
+
filter: (input: string) => input.trim(),
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
type: 'input',
|
|
370
|
+
name: 'entry',
|
|
371
|
+
message: 'Entry price ($):',
|
|
372
|
+
validate: (input: string) => {
|
|
373
|
+
const value = parseFloat(input.trim());
|
|
374
|
+
if (isNaN(value)) {
|
|
375
|
+
return 'Please enter a valid number';
|
|
376
|
+
}
|
|
377
|
+
if (value <= 0) {
|
|
378
|
+
return 'Entry price must be greater than 0';
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
381
|
+
},
|
|
382
|
+
filter: (input: string) => input.trim(),
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: 'input',
|
|
386
|
+
name: 'stop',
|
|
387
|
+
message: 'Stop loss price ($):',
|
|
388
|
+
validate: (input: string) => {
|
|
389
|
+
const value = parseFloat(input.trim());
|
|
390
|
+
if (isNaN(value)) {
|
|
391
|
+
return 'Please enter a valid number';
|
|
392
|
+
}
|
|
393
|
+
if (value <= 0) {
|
|
394
|
+
return 'Stop loss price must be greater than 0';
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
},
|
|
398
|
+
filter: (input: string) => input.trim(),
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
type: 'list',
|
|
402
|
+
name: 'leverage',
|
|
403
|
+
message: 'Leverage:',
|
|
404
|
+
choices: [
|
|
405
|
+
{ name: '1x (No leverage)', value: '1' },
|
|
406
|
+
{ name: '2x', value: '2' },
|
|
407
|
+
{ name: '3x (Recommended)', value: '3' },
|
|
408
|
+
{ name: '5x', value: '5' },
|
|
409
|
+
{ name: '10x (High risk)', value: '10' },
|
|
410
|
+
],
|
|
411
|
+
default: '3',
|
|
412
|
+
},
|
|
413
|
+
]);
|
|
414
|
+
|
|
415
|
+
// Calculate position sizing
|
|
416
|
+
const balance = parseFloat(answers.balance);
|
|
417
|
+
const riskPercent = parseFloat(answers.risk) / 100;
|
|
418
|
+
const entry = parseFloat(answers.entry);
|
|
419
|
+
const stop = parseFloat(answers.stop);
|
|
420
|
+
const leverage = parseFloat(answers.leverage);
|
|
421
|
+
|
|
422
|
+
// Validate that entry and stop make sense
|
|
423
|
+
if (entry === stop) {
|
|
424
|
+
console.error(chalk.red('â Entry price and stop loss cannot be the same'));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const isLong = stop < entry;
|
|
429
|
+
const riskPerUnit = Math.abs(entry - stop);
|
|
430
|
+
const riskAmount = balance * riskPercent;
|
|
431
|
+
const maxLoss = riskAmount;
|
|
432
|
+
|
|
433
|
+
// Validate risk per unit is reasonable
|
|
434
|
+
const riskPercentFromEntry = (riskPerUnit / entry) * 100;
|
|
435
|
+
if (riskPercentFromEntry > 50) {
|
|
436
|
+
console.log(
|
|
437
|
+
chalk.yellow(
|
|
438
|
+
`â ď¸ Warning: Large price difference (${riskPercentFromEntry.toFixed(1)}% from entry price)`,
|
|
439
|
+
),
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Position size calculation
|
|
444
|
+
const marginRequired = maxLoss / (riskPerUnit / entry);
|
|
445
|
+
const positionSize = marginRequired * leverage;
|
|
446
|
+
|
|
447
|
+
// Validate calculations
|
|
448
|
+
if (marginRequired <= 0 || positionSize <= 0) {
|
|
449
|
+
console.error(chalk.red('â Invalid calculation result. Please check your inputs.'));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
console.log(chalk.bold('\nđ Position Calculation:'));
|
|
454
|
+
console.log(` Direction: ${isLong ? chalk.green('LONG') : chalk.red('SHORT')}`);
|
|
455
|
+
console.log(` Risk Amount: ${riskAmount.toFixed(2)} TAO (${answers.risk}%)`);
|
|
456
|
+
console.log(` Margin Required: ${marginRequired.toFixed(2)} TAO`);
|
|
457
|
+
console.log(` Position Size: $${positionSize.toFixed(2)}`);
|
|
458
|
+
console.log(` Risk per Unit: $${riskPerUnit.toFixed(2)}`);
|
|
459
|
+
|
|
460
|
+
// Calculate potential profit with different R multiples
|
|
461
|
+
console.log(chalk.bold('\nđ° Risk/Reward Scenarios:'));
|
|
462
|
+
|
|
463
|
+
const scenarios = [1, 2, 3, 5];
|
|
464
|
+
scenarios.forEach((r) => {
|
|
465
|
+
const target = isLong ? entry + riskPerUnit * r : entry - riskPerUnit * r;
|
|
466
|
+
const profit = riskAmount * r;
|
|
467
|
+
console.log(` ${r}R Target: $${target.toFixed(2)} â Profit: ${profit.toFixed(2)} TAO`);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Liquidation info
|
|
471
|
+
const liquidationDistance = 0.2; // 20% for simplicity
|
|
472
|
+
const liquidationPrice = isLong
|
|
473
|
+
? entry * (1 - liquidationDistance)
|
|
474
|
+
: entry * (1 + liquidationDistance);
|
|
475
|
+
|
|
476
|
+
console.log(chalk.bold('\nâ ď¸ Risk Management:'));
|
|
477
|
+
console.log(` Liquidation Price: ${chalk.red('$' + liquidationPrice.toFixed(2))}`);
|
|
478
|
+
console.log(` Max Leverage Safe: ${(1 / (riskPerUnit / entry)).toFixed(1)}x`);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
handleError(error, program.opts());
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|