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>
316 lines
8.0 KiB
TypeScript
316 lines
8.0 KiB
TypeScript
/**
|
|
* MT4 Position Tools
|
|
*
|
|
* - mt4_get_positions: List all open positions
|
|
* - mt4_close_position: Close a specific position
|
|
*/
|
|
|
|
import { z } from 'zod';
|
|
import { getMT4Client, MT4Position, TradeResult } from '../services/mt4-client';
|
|
|
|
// ==========================================
|
|
// mt4_get_positions
|
|
// ==========================================
|
|
|
|
/**
|
|
* mt4_get_positions - List all open positions
|
|
*
|
|
* @description Retrieves all currently open positions from MT4 terminal.
|
|
* Can optionally filter by symbol.
|
|
*
|
|
* @param symbol - Optional symbol to filter positions (e.g., "XAUUSD")
|
|
* @returns Array of open positions with details
|
|
*
|
|
* @example
|
|
* const result = await mt4_get_positions({});
|
|
* // Returns all positions
|
|
*
|
|
* const result = await mt4_get_positions({ symbol: "XAUUSD" });
|
|
* // Returns only XAUUSD positions
|
|
*/
|
|
|
|
export const mt4GetPositionsSchema = {
|
|
name: 'mt4_get_positions',
|
|
description: 'List all open trading positions from MT4. Optionally filter by symbol.',
|
|
inputSchema: {
|
|
type: 'object' as const,
|
|
properties: {
|
|
symbol: {
|
|
type: 'string',
|
|
description: 'Optional: Filter positions by symbol (e.g., XAUUSD, EURUSD)',
|
|
},
|
|
},
|
|
required: [] as string[],
|
|
},
|
|
};
|
|
|
|
export const Mt4GetPositionsInputSchema = z.object({
|
|
symbol: z.string().optional(),
|
|
});
|
|
|
|
export type Mt4GetPositionsInput = z.infer<typeof Mt4GetPositionsInputSchema>;
|
|
|
|
export interface Mt4GetPositionsResult {
|
|
success: boolean;
|
|
data?: {
|
|
positions: MT4Position[];
|
|
totalProfit: number;
|
|
count: number;
|
|
};
|
|
error?: string;
|
|
}
|
|
|
|
export async function mt4_get_positions(
|
|
params: Mt4GetPositionsInput
|
|
): Promise<Mt4GetPositionsResult> {
|
|
try {
|
|
const client = getMT4Client();
|
|
|
|
// Check connection
|
|
const isConnected = await client.isConnected();
|
|
if (!isConnected) {
|
|
return {
|
|
success: false,
|
|
error: 'MT4 terminal is not connected',
|
|
};
|
|
}
|
|
|
|
// Get all positions
|
|
let positions = await client.getPositions();
|
|
|
|
// Filter by symbol if specified
|
|
if (params.symbol) {
|
|
positions = positions.filter(
|
|
p => p.symbol.toUpperCase() === params.symbol!.toUpperCase()
|
|
);
|
|
}
|
|
|
|
// Calculate total profit
|
|
const totalProfit = positions.reduce((sum, p) => sum + p.profit, 0);
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
positions,
|
|
totalProfit,
|
|
count: positions.length,
|
|
},
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function handleMt4GetPositions(
|
|
params: unknown
|
|
): Promise<{ content: Array<{ type: string; text: string }> }> {
|
|
const validatedParams = Mt4GetPositionsInputSchema.parse(params);
|
|
const result = await mt4_get_positions(validatedParams);
|
|
|
|
if (result.success && result.data) {
|
|
if (result.data.count === 0) {
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: params && (params as Mt4GetPositionsInput).symbol
|
|
? `No open positions found for ${(params as Mt4GetPositionsInput).symbol}`
|
|
: 'No open positions found',
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
const positionLines = result.data.positions.map(p => {
|
|
const direction = p.type.toUpperCase();
|
|
const profitSign = p.profit >= 0 ? '+' : '';
|
|
const slInfo = p.stopLoss !== null ? `SL: ${p.stopLoss}` : 'SL: None';
|
|
const tpInfo = p.takeProfit !== null ? `TP: ${p.takeProfit}` : 'TP: None';
|
|
|
|
return `
|
|
#${p.ticket} | ${p.symbol} | ${direction} ${p.lots} lots
|
|
Open: ${p.openPrice} | Current: ${p.currentPrice}
|
|
${slInfo} | ${tpInfo}
|
|
P/L: ${profitSign}${p.profit.toFixed(2)} | Swap: ${p.swap.toFixed(2)}
|
|
Opened: ${p.openTime}
|
|
Magic: ${p.magic} | Comment: ${p.comment || 'None'}`;
|
|
});
|
|
|
|
const formattedOutput = `
|
|
Open Positions (${result.data.count})
|
|
${'='.repeat(30)}
|
|
${positionLines.join('\n---\n')}
|
|
|
|
Total P/L: ${result.data.totalProfit >= 0 ? '+' : ''}${result.data.totalProfit.toFixed(2)}
|
|
`.trim();
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: formattedOutput,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: `Error: ${result.error}`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
// ==========================================
|
|
// mt4_close_position
|
|
// ==========================================
|
|
|
|
/**
|
|
* mt4_close_position - Close a trading position
|
|
*
|
|
* @description Closes an open position by ticket number.
|
|
* Can optionally close partial volume.
|
|
*
|
|
* @param ticket - Position ticket number to close
|
|
* @param lots - Optional: Partial volume to close (default: close all)
|
|
* @param slippage - Optional: Maximum slippage in points (default: 3)
|
|
* @returns Trade result with success status
|
|
*
|
|
* @example
|
|
* // Close entire position
|
|
* const result = await mt4_close_position({ ticket: 123456 });
|
|
*
|
|
* // Close partial position
|
|
* const result = await mt4_close_position({ ticket: 123456, lots: 0.5 });
|
|
*/
|
|
|
|
export const mt4ClosePositionSchema = {
|
|
name: 'mt4_close_position',
|
|
description: 'Close an open trading position by ticket number. Can close partially.',
|
|
inputSchema: {
|
|
type: 'object' as const,
|
|
properties: {
|
|
ticket: {
|
|
type: 'number',
|
|
description: 'Position ticket number to close',
|
|
},
|
|
lots: {
|
|
type: 'number',
|
|
description: 'Optional: Partial volume to close. If not specified, closes entire position.',
|
|
},
|
|
slippage: {
|
|
type: 'number',
|
|
description: 'Optional: Maximum slippage in points (default: 3)',
|
|
},
|
|
},
|
|
required: ['ticket'] as string[],
|
|
},
|
|
};
|
|
|
|
export const Mt4ClosePositionInputSchema = z.object({
|
|
ticket: z.number().int().positive(),
|
|
lots: z.number().positive().optional(),
|
|
slippage: z.number().int().min(0).max(100).optional(),
|
|
});
|
|
|
|
export type Mt4ClosePositionInput = z.infer<typeof Mt4ClosePositionInputSchema>;
|
|
|
|
export interface Mt4ClosePositionResult {
|
|
success: boolean;
|
|
data?: TradeResult;
|
|
error?: string;
|
|
}
|
|
|
|
export async function mt4_close_position(
|
|
params: Mt4ClosePositionInput
|
|
): Promise<Mt4ClosePositionResult> {
|
|
try {
|
|
const client = getMT4Client();
|
|
|
|
// Check connection
|
|
const isConnected = await client.isConnected();
|
|
if (!isConnected) {
|
|
return {
|
|
success: false,
|
|
error: 'MT4 terminal is not connected',
|
|
};
|
|
}
|
|
|
|
// Verify position exists
|
|
const position = await client.getPosition(params.ticket);
|
|
if (!position) {
|
|
return {
|
|
success: false,
|
|
error: `Position with ticket ${params.ticket} not found`,
|
|
};
|
|
}
|
|
|
|
// Validate lots if specified
|
|
if (params.lots !== undefined && params.lots > position.lots) {
|
|
return {
|
|
success: false,
|
|
error: `Requested lots (${params.lots}) exceeds position size (${position.lots})`,
|
|
};
|
|
}
|
|
|
|
// Close position
|
|
const result = await client.closePosition({
|
|
ticket: params.ticket,
|
|
lots: params.lots,
|
|
slippage: params.slippage,
|
|
});
|
|
|
|
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 handleMt4ClosePosition(
|
|
params: unknown
|
|
): Promise<{ content: Array<{ type: string; text: string }> }> {
|
|
const validatedParams = Mt4ClosePositionInputSchema.parse(params);
|
|
const result = await mt4_close_position(validatedParams);
|
|
|
|
if (result.success && result.data) {
|
|
const formattedOutput = `
|
|
Position Closed Successfully
|
|
============================
|
|
Ticket: ${validatedParams.ticket}
|
|
${validatedParams.lots ? `Closed Volume: ${validatedParams.lots} lots` : 'Closed: Entire position'}
|
|
Message: ${result.data.message || 'Position closed'}
|
|
`.trim();
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: formattedOutput,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: `Error closing position: ${result.error}`,
|
|
},
|
|
],
|
|
};
|
|
}
|