Position Scaler Orchestrator¶
Adds volume to an existing position at predefined price levels — either pyramiding into a winner or averaging into a loser — while respecting a hard total-volume cap.
Demonstration only — not production ready
This orchestrator shows the strategy idea and the API calls behind it. A real deployment needs proper position sizing, slippage control, logging, monitoring, and extensive testing. Run it on a demo account only.
→ Source: examples/4_orchestrators/12_position_scaler.py
What it does¶
Scales a single base position in steps. Each time price reaches the next configured level, it adds a slice of volume and records that the level has fired (so it never doubles up on the same level). Total exposure is always clamped to max_total_volume.
How it works¶
- Picks a base position (opens test positions if none is passed) and streams its P&L via
stream_position_profits(). - On each update, measures profit/loss in pips against the configured
scale_levels_pips. - When a level is reached, adds
scale_volume_percentof the base volume — as long as the new total stays undermax_total_volumeand margin allows. - Each level triggers once. A live progress bar and periodic status show total volume and scale count.
Two modes¶
- Pyramiding (trend-following): adds to profitable positions at
+30/+60/+90pips. Lower risk, needs a real trend. - Averaging (mean-reversion): adds to losing positions at
−30/−60/−90pips. Higher risk — use strict limits.
Parameters¶
| Parameter | Type | Default | Meaning |
|---|---|---|---|
service |
MT5Service |
— | Streaming, quotes, account summary. |
sugar |
MT5Sugar |
— | buy_market/sell_market, margin & symbol helpers, close_position. |
scale_type |
ScaleType |
PYRAMIDING |
PYRAMIDING or AVERAGING. |
scale_levels_pips |
list[float] |
[30, 60, 90] |
Pip levels at which to add volume. |
scale_volume_percent |
float |
50.0 |
Percent of base volume added per level. |
max_total_volume |
float |
0.50 |
Hard cap on combined volume (lots). |
update_interval_ms |
int |
1000 |
Position stream interval (ms). |
dry_run |
bool |
False |
Simulate adds without executing. |
await start(symbol, base_ticket=None, duration_minutes=3.0, cancellation_event=None) runs the loop. If base_ticket is omitted the demo opens its own test positions.
Example scenario (pyramiding)¶
BUY EURUSD at 1.2000, base volume 0.10, levels +30/+60/+90, add 50% (0.05) per level, cap 0.30:
- price → 1.2030 (+30p) → add 0.05 → total 0.15
- price → 1.2060 (+60p) → add 0.05 → total 0.20
- price → 1.2090 (+90p) → add 0.05 → total 0.25
Run it¶
Minimal usage¶
import asyncio
from pymt5.mt5_service import MT5Service
from pymt5.mt5_sugar import MT5Sugar
from demo_helpers import load_settings, create_and_connect_mt5
# ScaleType lives in the orchestrator module (12_position_scaler.py)
async def main():
config = load_settings()
account = await create_and_connect_mt5(config)
service = MT5Service(account)
sugar = MT5Sugar(service, default_symbol=config["test_symbol"])
orch = PositionScalerOrchestrator(
service, sugar,
scale_type=ScaleType.PYRAMIDING,
scale_levels_pips=[30.0, 60.0, 90.0],
scale_volume_percent=50.0,
max_total_volume=0.30,
)
await orch.start(symbol=config["test_symbol"], duration_minutes=3.0)
await account.disconnect()
asyncio.run(main())
API methods used¶
- MT5Service:
stream_position_profits()(core),get_opened_orders(),get_symbol_tick(),get_account_summary(). - MT5Sugar:
buy_market()/sell_market(),calculate_required_margin(),get_account_info(),get_symbol_info(),close_position().
Notes¶
- Always respects
max_total_volume, and checks free margin before adding. - Averaging adds to losers — it is the higher-risk mode; keep
max_total_volumetight. - The demo opens 2 small test positions, uses the first as base and the second as backup, and closes only its own test positions on exit.