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>
403 lines
11 KiB
TypeScript
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}`,
|
|
},
|
|
],
|
|
};
|
|
}
|