Math & Risk¶
🤖 async auto_breakeven(ticket, *, trigger_pips, plus_pips=0.0)¶
What it does: Automatically moves SL to breakeven once price advances by trigger_pips;
sets SL to entry ± plus_pips depending on direction.
Used in:
- Semi‑automated strategies/scalping to lock in breakeven without manual intervention.
Related to: order_modify.md, opened_orders.md
Example
await sugar.auto_breakeven(ticket=123456, trigger_pips=15, plus_pips=1.0)
⚖️ breakeven_price(entry_price, commission=0.0, swap=0.0)¶
What it does: Computes breakeven price considering commission/swap.
Used in:
- Determining the level to pull SL to when targeting "flat" (zero P/L).
Related to: pure math helper
Example
be = sugar.breakeven_price(entry_price=1.10000, commission=0.5, swap=0.0)
💵 async calc_cash_risk(symbol, lots, stop_pips)¶
What it does: Calculates cash risk for a position if SL is placed stop_pips away.
Used in:
- Risk control / money management before order placement.
- Validating position sizes don't exceed risk limits.
- Portfolio risk aggregation.
- Risk reporting and monitoring.
Parameters:
symbol- Trading symbollots- Position size in lotsstop_pips- Stop loss distance in pips
Returns: Cash risk amount in account currency (float)
Related to: tick_value_with_size.md
Example 1: Verify risk before opening
# Check if risk is acceptable
risk_cash = await sugar.calc_cash_risk("EURUSD", lots=0.2, stop_pips=25)
print(f"This trade risks ${risk_cash:.2f}")
if risk_cash > 100:
logger.warning("Risk too high - reducing position size")
else:
await sugar.buy_market("EURUSD", lots=0.2, sl_pips=25)
Example 2: Calculate total portfolio risk
# Get all open positions
orders = await sugar._svc.opened_orders()
total_risk = 0
for order in orders:
# Calculate risk for each position
risk = await sugar.calc_cash_risk(
symbol=order.symbol,
lots=order.lots,
stop_pips=abs(order.stop_loss - order.open_price) / pip_size
)
total_risk += risk
print(f"Total portfolio risk: ${total_risk:.2f}")
Example 3: Risk as percentage of balance
balance = 10000 # Account balance
risk_cash = await sugar.calc_cash_risk("EURUSD", lots=0.1, stop_pips=20)
risk_pct = (risk_cash / balance) * 100
print(f"Risk: ${risk_cash:.2f} ({risk_pct:.2f}% of balance)")
🧮 async calc_lot_by_risk(symbol, risk_percent, stop_pips, *, balance=None)¶
What it does: Derives lot size from desired % risk of balance and SL distance in pips. This is THE MOST IMPORTANT method for proper position sizing.
Used in:
- Auto‑sizing position before sending an order.
- Implementing consistent risk management across all trades.
- Adjusting position size based on stop loss distance.
- Portfolio risk management.
Parameters:
symbol- Trading symbolrisk_percent- Percentage of balance to risk (e.g., 1.0 = 1%, 2.5 = 2.5%)stop_pips- Stop loss distance in pipsbalance- (Optional) Account balance. If None, fetches current balance automatically.
Returns: Lot size (float) that risks exactly the specified percentage
Important notes:
- Automatically fetches account balance if not provided
- Accounts for symbol-specific tick values
- Returns normalized lot size (valid for broker)
- Higher risk_percent = larger position
- Wider stop_pips = smaller position (for same risk)
Related to: tick_value_with_size.md
Example 1: Risk 1% on each trade
# Risk 1% of balance with 20 pip stop
lots = await sugar.calc_lot_by_risk("EURUSD", risk_percent=1.0, stop_pips=20)
await sugar.buy_market("EURUSD", lots=lots, sl_pips=20)
Example 2: Adaptive position sizing
# Tighter stops = larger position
# Wider stops = smaller position
# Volatile market - wide stop, automatically smaller position
lots_volatile = await sugar.calc_lot_by_risk("EURUSD", risk_percent=1.0, stop_pips=50)
# Calm market - tight stop, automatically larger position
lots_calm = await sugar.calc_lot_by_risk("EURUSD", risk_percent=1.0, stop_pips=10)
print(f"50 pip stop = {lots_volatile} lots") # e.g., 0.04
print(f"10 pip stop = {lots_calm} lots") # e.g., 0.20
Example 3: Different risk levels for different strategies
# Conservative: 0.5% risk
conservative_lots = await sugar.calc_lot_by_risk("EURUSD", risk_percent=0.5, stop_pips=20)
# Standard: 1% risk
standard_lots = await sugar.calc_lot_by_risk("EURUSD", risk_percent=1.0, stop_pips=20)
# Aggressive: 2% risk
aggressive_lots = await sugar.calc_lot_by_risk("EURUSD", risk_percent=2.0, stop_pips=20)
# Use based on signal confidence
if signal_confidence > 0.8:
lots = aggressive_lots
elif signal_confidence > 0.5:
lots = standard_lots
else:
lots = conservative_lots
Example 4: Portfolio risk management
# Never risk more than 5% total across all positions
MAX_TOTAL_RISK = 5.0
current_risk = 2.5 # Already 2.5% at risk in other positions
remaining_risk = MAX_TOTAL_RISK - current_risk # 2.5%
if remaining_risk > 0:
lots = await sugar.calc_lot_by_risk("EURUSD", risk_percent=remaining_risk, stop_pips=20)
await sugar.buy_market("EURUSD", lots=lots, sl_pips=20)
else:
logger.warning("Max portfolio risk reached - no new positions")
🧷 async close_by_breakeven(ticket, plus_pips=0.0)¶
What it does: Closes a position if price returns to entry (adjusted by plus_pips).
Used in:
- "Don’t give back" logic: from profit either continue or exit flat.
Related to: order_close_delete.md, opened_orders_tickets.md
Example
await sugar.close_by_breakeven(ticket=123456, plus_pips=0.5)
🧯 async normalize_lot(symbol, lots)¶
What it does: Normalizes lot to the instrument's step/min/max constraints.
Used in:
- Before
order_send/order_modifyto avoid broker rejections. - Ensuring lot sizes comply with broker requirements.
- Rounding calculated lot sizes to valid values.
Parameters:
symbol- Trading symbollots- Lot size to normalize
Returns: Normalized lot size (float) that meets broker requirements
Important notes:
- Rounds to nearest valid lot step (usually 0.01)
- Clamps to min/max lot sizes
- Essential to avoid "invalid volume" errors
Related to: symbol_params_many.md
Example 1: Normalize calculated lots
lots_ok = await sugar.normalize_lot("XAUUSD", lots=0.033) # → 0.03
Example 2: Normalize before order placement
# Calculate ideal lot size
ideal_lots = 0.1234
# Normalize to broker requirements
actual_lots = await sugar.normalize_lot("EURUSD", ideal_lots)
await sugar.buy_market("EURUSD", lots=actual_lots)
🎯 async normalize_price(symbol, price)¶
What it does: Normalizes price to the instrument's precision (digits/point).
Used in:
- Before setting SL/TP/limit/stop orders.
- Ensuring prices comply with broker requirements.
- Rounding calculated prices to valid tick sizes.
Parameters:
symbol- Trading symbolprice- Price to normalize
Returns: Normalized price (float) rounded to valid tick size
Important notes:
- Rounds to nearest valid tick
- Essential to avoid order rejections
- Different symbols have different precision (EURUSD: 5 digits, USDJPY: 3 digits)
Related to: symbol_params_many.md
Example 1: Normalize calculated price
p = await sugar.normalize_price("GBPUSD", 1.279991) # → 1.27999
Example 2: Normalize SL/TP before order
# Calculate ideal SL price
ideal_sl = 1.095678
# Normalize to broker precision
actual_sl = await sugar.normalize_price("EURUSD", ideal_sl)
await sugar.buy_market("EURUSD", lots=0.1)
await sugar.modify_sl_tp_by_price(ticket=ticket, sl_price=actual_sl)
📏 async pips_to_price(symbol, pips)¶
What it does: Converts a distance in pips to a price delta for the symbol.
Used in:
- Computing SL/TP/entry levels from pip values.
- Converting pip-based calculations to actual price levels.
- Setting orders at specific pip distances from current price.
Parameters:
symbol- Trading symbolpips- Number of pips to convert
Returns: Price delta (float) equivalent to the pip distance
Important notes:
- Accounts for symbol-specific pip sizes
- For 5-digit brokers (EURUSD): 10 pips = 0.00100
- For 3-digit brokers (USDJPY): 10 pips = 0.100
Related to: symbol_params_many.md, tick_value_with_size.md
Example 1: Basic conversion
delta = await sugar.pips_to_price("EURUSD", 15) # ~0.0015 on 5-digit
Example 2: Calculate SL/TP prices
# Get current price
quote = await sugar.last_quote("EURUSD")
entry_price = quote["ask"]
# Calculate SL and TP prices
sl_distance = await sugar.pips_to_price("EURUSD", 20)
tp_distance = await sugar.pips_to_price("EURUSD", 60)
sl_price = entry_price - sl_distance # For buy order
tp_price = entry_price + tp_distance # For buy order
print(f"Entry: {entry_price}, SL: {sl_price}, TP: {tp_price}")
Example 3: Place order at specific pip distance
# Place buy stop 30 pips above current price
quote = await sugar.last_quote("EURUSD")
current_ask = quote["ask"]
pip_distance = await sugar.pips_to_price("EURUSD", 30)
entry_price = current_ask + pip_distance
await sugar.buy_stop("EURUSD", lots=0.1, price=entry_price, sl_pips=20)
🔁 async price_to_pips(symbol, price_delta)¶
What it does: Converts a price delta back into pips for the symbol.
Used in:
- Expressing spread/profit/risk in pips.
- Calculating profit/loss in pips.
- Converting technical levels to pip distances.
Parameters:
symbol- Trading symbolprice_delta- Price difference to convert
Returns: Number of pips (float)
Related to: symbol_params_many.md
Example 1: Convert price to pips
pips = await sugar.price_to_pips("EURUSD", 0.0007) # ~7 pips
Example 2: Calculate profit in pips
# Get position details
entry_price = 1.09500
current_price = 1.09750
price_move = current_price - entry_price
# Convert to pips
profit_pips = await sugar.price_to_pips("EURUSD", price_move)
print(f"Profit: {profit_pips:.1f} pips") # ~25 pips
Example 3: Measure distance to key levels
# Technical analysis
quote = await sugar.last_quote("EURUSD")
current_price = quote["bid"]
resistance = 1.1000
distance_to_resistance = resistance - current_price
pips_to_resistance = await sugar.price_to_pips("EURUSD", distance_to_resistance)
print(f"{pips_to_resistance:.1f} pips to resistance")
🎫 async tick_value(symbol, lots=1.0)¶
What it does: Cash value of one tick move for the symbol at the given lot.
Used in:
- Risk management, expected P/L per tick.
Related to: tick_value_with_size.md
Example
tv = await sugar.tick_value("USDJPY", lots=0.5)