Close Percent (close.percent
) 🎯%¶
Closes a percentage of a position’s volume by ticket. Great for scaling‑out with rules like “take 50% at +1R, trail the rest”.
Method Signatures¶
// Read open positions (resolve ticket → symbol & current volume)
public Task<OpenedOrdersData> OpenedOrdersAsync(
BMT5_ENUM_OPENED_ORDER_SORT_TYPE sortMode = BMT5_ENUM_OPENED_ORDER_SORT_TYPE.Bmt5OpenedOrderSortByOpenTimeAsc,
DateTime? deadline = null,
CancellationToken cancellationToken = default);
// Query lot constraints
public Task<(double min, double step, double max)> GetVolumeConstraintsAsync(
string symbol,
DateTime? deadline = null,
CancellationToken cancellationToken = default);
// Best‑effort: ensure the symbol is visible before trading
public Task EnsureSymbolVisibleAsync(
string symbol,
TimeSpan? maxWait = null,
TimeSpan? pollInterval = null,
DateTime? deadline = null,
CancellationToken cancellationToken = default);
// Partial close (variant A — explicit deviation)
public Task ClosePositionPartialAsync(
ulong ticket,
double volume,
int deviation,
CancellationToken cancellationToken);
// Partial close (variant B — by symbol; deviation via defaults)
public Task CloseOrderByTicketAsync(
ulong ticket,
string symbol,
double volume,
DateTime? deadline = null,
CancellationToken cancellationToken = default);
Input Parameters ⬇️¶
Parameter | Type | Required | Description |
---|---|---|---|
--profile , -p |
string | yes | Profile from profiles.json . |
--ticket , -t |
ulong | yes | Position ticket to partially close. |
--pct |
double | no | Percentage to close (0 < pct ≤ 100). Default: 50 . |
--deviation |
int | no | Max slippage (points). Default: 10 . |
--timeout-ms |
int | no | RPC timeout in ms (default: 30000 ). |
--dry-run |
flag | no | Print intended action without sending a request. |
Note: This command is text-only; JSON output is not supported by the current handler.
Output ⬆️¶
Examples:
[DRY-RUN] CLOSE.PERCENT ticket=123456 pct=50 volume=0.12 deviation=10
✔ close.percent done: ticket=123456 closed=0.12 (pct=50)
Errors:
Position #123456 not found. (exit code 2)
Percent must be in (0;100]. (exit code 2)
Computed close volume below MinLot after step. (exit code 2)
RPC error: <broker message> (exit code 1)
Exit codes
0
— success2
— validation/not found/guard failures (including too‑small rounded volume)1
— fatal error (printed via ErrorPrinter)
How to Use¶
# Close 50% (default)
dotnet run -- close.percent -p demo -t 123456
# Close 25% with wider slippage
dotnet run -- close.percent -p demo -t 123456 --pct 25 --deviation 20
# Dry‑run (no request sent)
dotnet run -- close.percent -p demo -t 123456 --pct 33.3 --dry-run
PowerShell shortcut (from ps/shortcasts.ps1
)¶
. .\ps\shortcasts.ps1
use-pf demo
cpp -t 123456 -pct 50
# → mt5 close.percent -p demo -t 123456 --pct 50 --deviation 10 --timeout-ms 90000
Notes & Safety¶
- The computed close volume is rounded to lot step and clamped to
[MinLot; CurrentVolume]
. - If the rounded result is below MinLot, the broker may reject the request — consider
close.partial
to choose a valid lot explicitly. pct = 100
closes the entire position; behavior is equivalent to full close.--deviation
matters on fast markets; widen if you see rejections.- Use symbol limits to verify
min/step/max
for your symbol.
Code Reference 🧩¶
var cpTicketOpt = new Option<ulong>(new[] { "--ticket", "-t" }, "Position ticket") { IsRequired = true };
var cpPctOpt = new Option<double>(new[] { "--pct" }, () => 50.0, "Percent to close (0 < pct ≤ 100)");
var cpDevOpt = devOpt;
var closePercent = new Command("close.percent", "Close a percentage of a position by ticket");
closePercent.AddAlias("cpp");
closePercent.AddOption(profileOpt);
closePercent.AddOption(cpTicketOpt);
closePercent.AddOption(cpPctOpt);
closePercent.AddOption(cpDevOpt);
closePercent.AddOption(timeoutOpt);
closePercent.AddOption(dryRunOpt);
closePercent.SetHandler(async (InvocationContext ctx) =>
{
var profile = ctx.ParseResult.GetValueForOption(profileOpt)!;
var ticket = ctx.ParseResult.GetValueForOption(cpTicketOpt);
var pct = ctx.ParseResult.GetValueForOption(cpPctOpt);
var deviation = ctx.ParseResult.GetValueForOption(cpDevOpt);
var timeoutMs = ctx.ParseResult.GetValueForOption(timeoutOpt);
var dryRun = ctx.ParseResult.GetValueForOption(dryRunOpt);
Validators.EnsureProfile(profile);
Validators.EnsureTicket(ticket);
if (pct <= 0 || pct > 100) throw new ArgumentOutOfRangeException(nameof(pct), "Percent must be in (0;100].");
using (UseOpTimeout(timeoutMs))
using (_logger.BeginScope("Cmd:CLOSE.PERCENT Profile:{Profile}", profile))
using (_logger.BeginScope("Ticket:{Ticket} Pct:{Pct} Dev:{Dev}", ticket, pct, deviation))
{
await ConnectAsync();
// 1) Resolve symbol & current volume
var opened = await _mt5Account.OpenedOrdersAsync();
var pos = opened.PositionInfos.FirstOrDefault(p => (ulong)p.Ticket == ticket || unchecked((ulong)p.Ticket) == ticket);
if (pos is null) { Console.WriteLine($"Position #{ticket} not found."); Environment.ExitCode = 2; return; }
var symbol = pos.Symbol;
var currentVol = pos.Volume;
// 2) Compute requested close volume
var req = currentVol * (pct / 100.0);
// 3) Normalize to lot constraints
var (min, step, max) = await _mt5Account.GetVolumeConstraintsAsync(symbol);
double norm = Math.Max(min, Math.Min(currentVol, Math.Floor(req / step + 1e-9) * step));
if (norm < min || norm <= 0) { Console.WriteLine("Computed close volume below MinLot after step."); Environment.ExitCode = 2; return; }
// 4) Best‑effort ensure visibility
try { await _mt5Account.EnsureSymbolVisibleAsync(symbol, TimeSpan.FromSeconds(3)); } catch { }
// 5) Dry‑run or execute
if (dryRun)
{
Console.WriteLine($"[DRY-RUN] CLOSE.PERCENT ticket={ticket} pct={pct} volume={norm} deviation={deviation}");
return;
}
await _mt5Account.ClosePositionPartialAsync(ticket, norm, deviation, CancellationToken.None);
Console.WriteLine($"\u2714 close.percent done: ticket={ticket} closed={norm} (pct={pct})");
}
});
See also¶
close.half
— close 50% via aliasclose.partial
— close an exact lot amountsymbol limits
— min/step/max lot constraints