pub struct Position {
pub symbol: Symbol,
pub quantity: Decimal,
pub avg_cost: Decimal,
pub realized_pnl: Decimal,
pub open_bar: usize,
}Expand description
A single-symbol position tracking quantity, average cost, and realized P&L.
Fields§
§symbol: SymbolThe instrument.
quantity: DecimalCurrent net quantity (positive = long, negative = short, zero = flat).
avg_cost: DecimalVolume-weighted average cost of the current position.
realized_pnl: DecimalCumulative realized P&L for this position (net of commissions).
open_bar: usizeBar index at which the current position leg was opened. Set via Position::set_open_bar.
Implementations§
Source§impl Position
impl Position
Sourcepub fn set_open_bar(&mut self, bar: usize)
pub fn set_open_bar(&mut self, bar: usize)
Records the bar index at which the current position leg was opened.
Call this whenever transitioning from flat to a new position.
Sourcepub fn position_age_bars(&self, current_bar: usize) -> usize
pub fn position_age_bars(&self, current_bar: usize) -> usize
Returns how many bars the current position has been open.
age = current_bar - self.open_bar (saturating at 0).
Sourcepub fn max_favorable_excursion(&self, prices: &[Price]) -> Option<Decimal>
pub fn max_favorable_excursion(&self, prices: &[Price]) -> Option<Decimal>
Maximum favorable excursion (MFE): the best unrealized P&L seen across prices.
For a long position, this is max(price - avg_cost) * quantity.
For a short position, this is max(avg_cost - price) * |quantity|.
Returns None when the position is flat, avg_cost is zero, or prices is empty.
Sourcepub fn kelly_fraction(
win_rate: Decimal,
avg_win: Decimal,
avg_loss: Decimal,
) -> Option<Decimal>
pub fn kelly_fraction( win_rate: Decimal, avg_win: Decimal, avg_loss: Decimal, ) -> Option<Decimal>
Kelly fraction: optimal bet size as a fraction of capital.
Kelly = win_rate - (1 - win_rate) / (avg_win / avg_loss)
Returns None when avg_loss or avg_win is zero.
The result is clamped to [0, 1] — never bet more than 100% or go short via Kelly.
Sourcepub fn apply_fill(&mut self, fill: &Fill) -> Result<Decimal, FinError>
pub fn apply_fill(&mut self, fill: &Fill) -> Result<Decimal, FinError>
Applies a fill, updating quantity, avg_cost, and realized_pnl.
§Returns
The realized P&L contributed by this fill (0 if position is increasing).
§Errors
Returns FinError::ArithmeticOverflow on checked arithmetic failure.
Sourcepub fn unrealized_pnl(&self, current_price: Price) -> Decimal
pub fn unrealized_pnl(&self, current_price: Price) -> Decimal
Returns unrealized P&L at current_price.
Sourcepub fn checked_unrealized_pnl(
&self,
current_price: Price,
) -> Result<Decimal, FinError>
pub fn checked_unrealized_pnl( &self, current_price: Price, ) -> Result<Decimal, FinError>
Returns unrealized P&L at current_price, returning Err on arithmetic overflow.
Sourcepub fn unrealized_pnl_pct(&self, current_price: Price) -> Option<Decimal>
pub fn unrealized_pnl_pct(&self, current_price: Price) -> Option<Decimal>
Returns unrealized P&L as a percentage of cost basis at current_price.
pct = unrealized_pnl / (|quantity| × avg_cost) × 100.
Returns None if the position is flat or avg_cost is zero.
Sourcepub fn total_cost_basis(&self) -> Decimal
pub fn total_cost_basis(&self) -> Decimal
Returns the total cost basis: |quantity| * avg_cost.
Represents the total capital committed to this position. Returns zero for flat positions.
Sourcepub fn market_value(&self, current_price: Price) -> Decimal
pub fn market_value(&self, current_price: Price) -> Decimal
Returns the market value of this position at current_price.
Sourcepub fn direction(&self) -> PositionDirection
pub fn direction(&self) -> PositionDirection
Returns the direction of the position.
Sourcepub fn total_pnl(&self, current_price: Price) -> Decimal
pub fn total_pnl(&self, current_price: Price) -> Decimal
Returns total P&L: realized_pnl + unrealized_pnl(current_price).
Sourcepub fn quantity_abs(&self) -> Decimal
pub fn quantity_abs(&self) -> Decimal
Returns the absolute magnitude of the current quantity.
Sourcepub fn cost_basis(&self) -> Decimal
pub fn cost_basis(&self) -> Decimal
Returns the cost basis of the current position: avg_cost * |quantity|.
Represents total capital deployed, excluding any realized P&L.
Returns 0 when the position is flat.
Sourcepub fn is_profitable(&self, current_price: Price) -> bool
pub fn is_profitable(&self, current_price: Price) -> bool
Returns true if unrealized PnL at current_price is strictly positive.
Sourcepub fn avg_entry_price(&self) -> Option<Price>
pub fn avg_entry_price(&self) -> Option<Price>
Returns the average entry price as a Price, or None if the position is flat.
This is avg_cost expressed as a validated Price. Returns None when
avg_cost == 0 (no open position).
Sourcepub fn exposure_pct(
&self,
current_price: Price,
total_portfolio_value: Decimal,
) -> Option<Decimal>
pub fn exposure_pct( &self, current_price: Price, total_portfolio_value: Decimal, ) -> Option<Decimal>
Returns the position’s current market value as a percentage of total_portfolio_value.
exposure_pct = |quantity × current_price| / total_portfolio_value × 100
Returns None when total_portfolio_value is zero, the position is flat, or
current_price is zero.
Sourcepub fn stop_loss_price(&self, stop_pct: Decimal) -> Option<Price>
pub fn stop_loss_price(&self, stop_pct: Decimal) -> Option<Price>
Returns the stop-loss price at stop_pct percent below (long) or above (short) entry.
- Long:
stop = avg_cost × (1 - stop_pct / 100) - Short:
stop = avg_cost × (1 + stop_pct / 100)
Returns None when the position is flat or avg_cost is zero.
§Example
// A 2% stop loss on a long position at avg_cost=100 → stop at 98
position.stop_loss_price(dec!(2)).unwrap() == Price::new(dec!(98)).unwrap()Sourcepub fn take_profit_price(&self, tp_pct: Decimal) -> Option<Price>
pub fn take_profit_price(&self, tp_pct: Decimal) -> Option<Price>
Returns the take-profit price for the current position at tp_pct percent gain.
Returns None when the position is flat or avg_cost is zero.
For a long position, the take-profit price is avg_cost * (1 + tp_pct / 100).
For a short position, the take-profit price is avg_cost * (1 - tp_pct / 100).
Sourcepub fn margin_requirement(&self, margin_pct: Decimal) -> Option<Decimal>
pub fn margin_requirement(&self, margin_pct: Decimal) -> Option<Decimal>
Returns the margin requirement for the current position: |net_quantity| × avg_cost × margin_pct / 100.
Returns None if the position is flat or avg_cost is zero.