trading-platform/apps/mcp-mt4-connector/src/tools/trading.ts
rckrdmrd a7cca885f0 feat: Major platform documentation and architecture updates
Changes include:
- Updated architecture documentation
- Enhanced module definitions (OQI-001 to OQI-008)
- ML integration documentation updates
- Trading strategies documentation
- Orchestration and inventory updates
- Docker configuration updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 05:33:35 -06:00

403 lines
11 KiB
TypeScript

/**
* MT4 Trading Tools
*
* - mt4_execute_trade: Execute BUY/SELL market orders
* - mt4_modify_position: Modify SL/TP of existing positions
*/
import { z } from 'zod';
import { getMT4Client, TradeResult } from '../services/mt4-client';
// ==========================================
// mt4_execute_trade
// ==========================================
/**
* mt4_execute_trade - Execute a market order (BUY or SELL)
*
* @description Opens a new trading position with optional SL/TP levels.
* Supports market orders only (pending orders not implemented).
*
* @param symbol - Trading symbol (e.g., "XAUUSD", "EURUSD")
* @param action - Trade direction: "buy" or "sell"
* @param lots - Volume in lots (e.g., 0.01, 0.1, 1.0)
* @param stopLoss - Optional stop loss price
* @param takeProfit - Optional take profit price
* @param slippage - Optional max slippage in points (default: 3)
* @param magic - Optional magic number for EA identification
* @param comment - Optional order comment
* @returns Trade result with ticket number
*
* @example
* // Simple buy order
* const result = await mt4_execute_trade({
* symbol: "XAUUSD",
* action: "buy",
* lots: 0.1
* });
*
* // Buy with SL/TP
* const result = await mt4_execute_trade({
* symbol: "XAUUSD",
* action: "buy",
* lots: 0.1,
* stopLoss: 2640.00,
* takeProfit: 2680.00,
* comment: "AI Signal"
* });
*/
export const mt4ExecuteTradeSchema = {
name: 'mt4_execute_trade',
description: 'Execute a market order (BUY or SELL) on MT4 with optional stop loss and take profit',
inputSchema: {
type: 'object' as const,
properties: {
symbol: {
type: 'string',
description: 'Trading symbol (e.g., XAUUSD, EURUSD, GBPUSD)',
},
action: {
type: 'string',
enum: ['buy', 'sell'],
description: 'Trade direction: buy or sell',
},
lots: {
type: 'number',
description: 'Volume in lots (e.g., 0.01, 0.1, 1.0)',
},
stopLoss: {
type: 'number',
description: 'Optional: Stop loss price level',
},
takeProfit: {
type: 'number',
description: 'Optional: Take profit price level',
},
slippage: {
type: 'number',
description: 'Optional: Maximum slippage in points (default: 3)',
},
magic: {
type: 'number',
description: 'Optional: Magic number for EA identification (default: 12345)',
},
comment: {
type: 'string',
description: 'Optional: Order comment (max 31 chars)',
},
},
required: ['symbol', 'action', 'lots'] as string[],
},
};
export const Mt4ExecuteTradeInputSchema = z.object({
symbol: z.string().min(1).max(20),
action: z.enum(['buy', 'sell']),
lots: z.number().positive().max(100),
stopLoss: z.number().positive().optional(),
takeProfit: z.number().positive().optional(),
slippage: z.number().int().min(0).max(100).optional(),
magic: z.number().int().optional(),
comment: z.string().max(31).optional(),
});
export type Mt4ExecuteTradeInput = z.infer<typeof Mt4ExecuteTradeInputSchema>;
export interface Mt4ExecuteTradeResult {
success: boolean;
data?: TradeResult & {
symbol: string;
action: string;
lots: number;
};
error?: string;
}
export async function mt4_execute_trade(
params: Mt4ExecuteTradeInput
): Promise<Mt4ExecuteTradeResult> {
try {
const client = getMT4Client();
// Check connection
const isConnected = await client.isConnected();
if (!isConnected) {
return {
success: false,
error: 'MT4 terminal is not connected',
};
}
// Validate SL/TP logic (basic check)
if (params.stopLoss !== undefined && params.takeProfit !== undefined) {
if (params.action === 'buy') {
// For buy: SL should be below current price, TP above
if (params.stopLoss >= params.takeProfit) {
return {
success: false,
error: 'For BUY orders, stop loss must be below take profit',
};
}
} else {
// For sell: SL should be above current price, TP below
if (params.stopLoss <= params.takeProfit) {
return {
success: false,
error: 'For SELL orders, stop loss must be above take profit',
};
}
}
}
// Execute trade
const result = await client.executeTrade({
symbol: params.symbol.toUpperCase(),
action: params.action,
lots: params.lots,
stopLoss: params.stopLoss,
takeProfit: params.takeProfit,
slippage: params.slippage,
magic: params.magic,
comment: params.comment,
});
if (result.success) {
return {
success: true,
data: {
...result,
symbol: params.symbol.toUpperCase(),
action: params.action,
lots: params.lots,
},
};
}
return {
success: false,
error: result.message || 'Trade execution failed',
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
export async function handleMt4ExecuteTrade(
params: unknown
): Promise<{ content: Array<{ type: string; text: string }> }> {
const validatedParams = Mt4ExecuteTradeInputSchema.parse(params);
const result = await mt4_execute_trade(validatedParams);
if (result.success && result.data) {
const formattedOutput = `
Trade Executed Successfully
===========================
Ticket: #${result.data.ticket}
Symbol: ${result.data.symbol}
Direction: ${result.data.action.toUpperCase()}
Volume: ${result.data.lots} lots
${validatedParams.stopLoss ? `Stop Loss: ${validatedParams.stopLoss}` : 'Stop Loss: Not set'}
${validatedParams.takeProfit ? `Take Profit: ${validatedParams.takeProfit}` : 'Take Profit: Not set'}
Message: ${result.data.message || 'Order placed successfully'}
`.trim();
return {
content: [
{
type: 'text',
text: formattedOutput,
},
],
};
}
return {
content: [
{
type: 'text',
text: `Error executing trade: ${result.error}`,
},
],
};
}
// ==========================================
// mt4_modify_position
// ==========================================
/**
* mt4_modify_position - Modify SL/TP of an existing position
*
* @description Updates the stop loss and/or take profit levels of an open position.
*
* @param ticket - Position ticket number to modify
* @param stopLoss - New stop loss price (null to remove)
* @param takeProfit - New take profit price (null to remove)
* @returns Modification result
*
* @example
* // Set both SL and TP
* const result = await mt4_modify_position({
* ticket: 123456,
* stopLoss: 2640.00,
* takeProfit: 2680.00
* });
*
* // Update only TP
* const result = await mt4_modify_position({
* ticket: 123456,
* takeProfit: 2700.00
* });
*/
export const mt4ModifyPositionSchema = {
name: 'mt4_modify_position',
description: 'Modify stop loss and/or take profit of an existing position',
inputSchema: {
type: 'object' as const,
properties: {
ticket: {
type: 'number',
description: 'Position ticket number to modify',
},
stopLoss: {
type: 'number',
description: 'New stop loss price level (optional)',
},
takeProfit: {
type: 'number',
description: 'New take profit price level (optional)',
},
},
required: ['ticket'] as string[],
},
};
export const Mt4ModifyPositionInputSchema = z.object({
ticket: z.number().int().positive(),
stopLoss: z.number().positive().optional(),
takeProfit: z.number().positive().optional(),
});
export type Mt4ModifyPositionInput = z.infer<typeof Mt4ModifyPositionInputSchema>;
export interface Mt4ModifyPositionResult {
success: boolean;
data?: TradeResult;
error?: string;
}
export async function mt4_modify_position(
params: Mt4ModifyPositionInput
): Promise<Mt4ModifyPositionResult> {
try {
const client = getMT4Client();
// Check connection
const isConnected = await client.isConnected();
if (!isConnected) {
return {
success: false,
error: 'MT4 terminal is not connected',
};
}
// Validate at least one parameter is provided
if (params.stopLoss === undefined && params.takeProfit === undefined) {
return {
success: false,
error: 'At least one of stopLoss or takeProfit must be provided',
};
}
// Verify position exists
const position = await client.getPosition(params.ticket);
if (!position) {
return {
success: false,
error: `Position with ticket ${params.ticket} not found`,
};
}
// Validate SL/TP based on position type
const sl = params.stopLoss;
const tp = params.takeProfit;
if (sl !== undefined && tp !== undefined) {
if (position.type === 'buy') {
if (sl >= tp) {
return {
success: false,
error: 'For BUY positions, stop loss must be below take profit',
};
}
} else {
if (sl <= tp) {
return {
success: false,
error: 'For SELL positions, stop loss must be above take profit',
};
}
}
}
// Modify position
const result = await client.modifyPosition({
ticket: params.ticket,
stopLoss: params.stopLoss,
takeProfit: params.takeProfit,
});
return {
success: result.success,
data: result,
error: result.success ? undefined : result.message,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
export async function handleMt4ModifyPosition(
params: unknown
): Promise<{ content: Array<{ type: string; text: string }> }> {
const validatedParams = Mt4ModifyPositionInputSchema.parse(params);
const result = await mt4_modify_position(validatedParams);
if (result.success && result.data) {
const formattedOutput = `
Position Modified Successfully
==============================
Ticket: #${validatedParams.ticket}
${validatedParams.stopLoss !== undefined ? `New Stop Loss: ${validatedParams.stopLoss}` : 'Stop Loss: Unchanged'}
${validatedParams.takeProfit !== undefined ? `New Take Profit: ${validatedParams.takeProfit}` : 'Take Profit: Unchanged'}
Message: ${result.data.message || 'Position modified successfully'}
`.trim();
return {
content: [
{
type: 'text',
text: formattedOutput,
},
],
};
}
return {
content: [
{
type: 'text',
text: `Error modifying position: ${result.error}`,
},
],
};
}