Close Position Partially (close_position_partial)¶
Sugar method: Closes part of a position by specified volume.
API Information:
- Method:
sugar.close_position_partial(ticket, volume) - Returns:
Trueif partial close successful,Falseotherwise - Layer: HIGH (MT5Sugar)
Method Signature¶
🔽 Input Parameters¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
ticket |
int |
Yes | - | Position ticket number |
volume |
float |
Yes | - | Volume to close (must be less than position volume) |
Return Value¶
| Type | Description |
|---|---|
bool |
True if partial close successful (return_code == 10009), False otherwise |
Raises:
ValueErrorif position not foundValueErrorif volume >= position volume
🏛️ Essentials¶
What it does:
- Closes specified volume of an open position
- Remaining volume stays as original position
- Creates opposite market order for specified volume
- Original position ticket remains with reduced volume
Key behaviors:
- Volume must be less than position volume
- BUY positions: closes with SELL at BID price
- SELL positions: closes with BUY at ASK price
- Remaining position keeps same ticket number
- Uses default 10 points slippage
⚡ Under the Hood¶
MT5Sugar.close_position_partial()
↓ fetches position
MT5Service.get_opened_orders()
↓ validates volume < position.volume
↓ determines opposite order type
↓ fetches current price
MT5Service.get_symbol_tick()
↓ builds OrderSendRequest (opposite direction)
MT5Service.place_order()
↓ gRPC protobuf
TradingHelperService.OrderSend()
↓ MT5 Terminal
Call chain:
- Sugar fetches all positions to find matching ticket
- Raises ValueError if position not found
- Validates volume < position.volume (raises if >=)
- Determines opposite order type (BUY→SELL, SELL→BUY)
- Fetches current tick price (BID for SELL, ASK for BUY)
- Creates OrderSendRequest with opposite operation and partial volume
- Sends order through Service.place_order()
- Returns True if returned_code == 10009
Related files:
- Sugar:
src/pymt5/mt5_sugar.py:1241 - Service:
src/pymt5/mt5_service.py:929 - Account:
package/MetaRpcMT5/helpers/mt5_account.py:1713
When to Use¶
Use close_position_partial() when:
- Taking partial profits at targets
- Scaling out of positions gradually
- Reducing exposure while keeping position open
- Implementing tiered exit strategies
Don't use when:
- Want to close entire position (use
close_position()) - Volume equals or exceeds position volume
- Position already closed
🔗 Examples¶
Example 1: Take Half Profit¶
from pymt5 import MT5Sugar
async def take_half_profit():
sugar = MT5Sugar(
user=591129415,
password="your_password",
grpc_server="mt5.mrpc.pro:443",
default_symbol="EURUSD"
)
await sugar.connect()
# Open 0.1 lot position
ticket = await sugar.buy_market(volume=0.10)
print(f"Position opened: #{ticket}, 0.10 lots")
# Wait for profit (simplified)
profit = await sugar.get_floating_profit()
while profit < 50:
await asyncio.sleep(5)
profit = await sugar.get_floating_profit()
# Close half the position
success = await sugar.close_position_partial(ticket, volume=0.05)
if success:
print("Closed 0.05 lots, 0.05 lots remaining")
else:
print("Partial close failed")
Example 2: Tiered Exit Strategy¶
async def tiered_exit():
sugar = MT5Sugar(
user=591129415,
password="your_password",
grpc_server="mt5.mrpc.pro:443",
default_symbol="EURUSD"
)
await sugar.connect()
# Open 0.3 lot position
ticket = await sugar.buy_market(volume=0.30)
entry_price = await sugar.get_ask()
print(f"Position: #{ticket}, 0.30 lots at {entry_price}")
# Exit in 3 stages
targets = [
(entry_price + 0.0030, 0.10, "Target 1: +30 pips"),
(entry_price + 0.0060, 0.10, "Target 2: +60 pips"),
(entry_price + 0.0100, 0.10, "Target 3: +100 pips")
]
for target_price, close_volume, label in targets:
# Wait for price to reach target
while True:
current = await sugar.get_bid()
if current >= target_price:
success = await sugar.close_position_partial(
ticket,
volume=close_volume
)
if success:
print(f"{label} - Closed {close_volume} lots")
break
await asyncio.sleep(5)
print("All targets hit, position fully closed")
Example 3: Risk Reduction on Profit¶
async def reduce_risk_on_profit():
sugar = MT5Sugar(
user=591129415,
password="your_password",
grpc_server="mt5.mrpc.pro:443",
default_symbol="EURUSD"
)
await sugar.connect()
# Open position
ticket = await sugar.buy_market(volume=0.20)
entry_price = await sugar.get_ask()
# Close 75% when in 50 pips profit, let 25% run
while True:
current_price = await sugar.get_bid()
profit_pips = (current_price - entry_price) / 0.0001
if profit_pips >= 50:
# Close 0.15 lots (75%), keep 0.05 lots (25%)
success = await sugar.close_position_partial(
ticket,
volume=0.15
)
if success:
print("Closed 75%, letting 25% run with trailing stop")
# Move remaining 0.05 lots to breakeven
await sugar.modify_position_sltp(
ticket,
sl=entry_price,
tp=None
)
break
await asyncio.sleep(5)
Common Pitfalls¶
Pitfall 1: Volume too large
# Position has 0.10 lots
ticket = await sugar.buy_market(volume=0.10)
# ERROR: Can't close 0.10 or more
try:
await sugar.close_position_partial(ticket, volume=0.10)
except ValueError as e:
print(e) # "Partial volume must be less than position volume"
Solution: Always close less than position volume
# Close part, not all
await sugar.close_position_partial(ticket, volume=0.05) # OK
await sugar.close_position_partial(ticket, volume=0.09) # OK
Pitfall 2: Not tracking remaining volume
# Open 0.10 lots
ticket = await sugar.buy_market(volume=0.10)
# Close 0.05 lots
await sugar.close_position_partial(ticket, volume=0.05)
# ERROR: Can't close 0.06 (only 0.05 remaining)
await sugar.close_position_partial(ticket, volume=0.06)
Solution: Track or check remaining volume
# Check position volume before closing
positions_data = await sugar._service.get_opened_orders()
for pos in positions_data.position_infos:
if pos.ticket == ticket:
remaining = pos.volume
print(f"Remaining volume: {remaining}")
# Close only what remains
close_vol = min(0.05, remaining)
await sugar.close_position_partial(ticket, close_vol)
Pitfall 3: Multiple partial closes not updating volume
# Open 0.30 lots
ticket = await sugar.buy_market(volume=0.30)
# Close in steps
await sugar.close_position_partial(ticket, 0.10) # 0.20 left
await sugar.close_position_partial(ticket, 0.10) # 0.10 left
await sugar.close_position_partial(ticket, 0.10) # 0.00 left
# ERROR: Position fully closed, ticket invalid for further partials
await sugar.close_position_partial(ticket, 0.05)
Solution: Check if position still exists
# After each partial close, verify position exists
success = await sugar.close_position_partial(ticket, 0.10)
if success:
# Check if position still exists
positions_data = await sugar._service.get_opened_orders()
still_open = any(p.ticket == ticket for p in positions_data.position_infos)
if not still_open:
print("Position fully closed")
Pro Tips¶
Tip 1: Scale out with percentage
# Get current position volume
positions_data = await sugar._service.get_opened_orders()
for pos in positions_data.position_infos:
if pos.ticket == ticket:
# Close 50% of position
close_volume = pos.volume * 0.5
await sugar.close_position_partial(ticket, close_volume)
print(f"Closed 50%: {close_volume} lots")
Tip 2: Combine with SL adjustment
# Close partial and move SL to breakeven
entry_price = 1.0850
# Close 50%
await sugar.close_position_partial(ticket, volume=0.05)
# Move SL to breakeven for remaining
await sugar.modify_position_sltp(ticket, sl=entry_price, tp=None)
Tip 3: Pyramid exit strategy
# Exit in pyramid fashion (larger amounts first)
total_volume = 0.30
exits = [
(0.15, "50%"), # Close half
(0.10, "33%"), # Close third of remaining
(0.05, "17%") # Let small amount run
]
for volume, label in exits:
success = await sugar.close_position_partial(ticket, volume)
if success:
print(f"Closed {label}: {volume} lots")
await asyncio.sleep(60) # Wait between exits
📚 See Also¶
- close_position - Close entire position
- close_all_positions - Close all positions
- modify_position_sltp - Modify SL/TP
- buy_market - Open BUY position