How the Leg Enum Supports Multi-Asset Strategies in optionstratlib
The Leg enum unifies options, spot assets, futures, and perpetual swaps under a single LegAble interface, enabling seamless construction of multi-asset strategies through uniform P&L calculations and risk metrics.
The Leg enum serves as the architectural foundation for complex portfolio modeling in the optionstratlib crate. By abstracting four distinct instrument types into a unified type system, it eliminates the need for separate handling logic when building multi-asset strategies that combine traditional derivatives with spot and crypto instruments.
The Four Leg Variants
Defined in src/model/leg/leg_enum.rs, the Leg enum encapsulates each instrument type within a dedicated variant. This design allows a single vector of legs to represent heterogeneous positions while maintaining type safety through Rust's enum dispatch.
Option Positions
The Option(Box<Position>) variant wraps traditional option contracts (calls and puts). According to the source in src/model/leg/leg_enum.rs [lines 61-64], this variant stores the option contract details, premium, and fees within a boxed Position struct to enable efficient heap allocation for complex option strategies.
Spot Positions
The Spot(SpotPosition) variant represents direct ownership of the underlying asset. As implemented in src/model/leg/spot.rs [lines 63-84], SpotPosition tracks the asset symbol, quantity, cost basis, side (long/short), and associated fees. This enables strategies like protective puts or cash-and-carry arbitrage that require physical holdings.
Futures Positions
The Future(FuturePosition) variant handles standard exchange-traded futures contracts. The implementation in src/model/leg/future.rs [lines 71-79] includes fields for symbol, quantity, entry price, expiration date, contract size, and margin requirements. This supports calendar spreads and basis trading strategies within the same framework.
Perpetual Swaps
The Perpetual(PerpetualPosition) variant accommodates crypto perpetual swaps. Defined in src/model/leg/perpetual.rs [lines 71-79], this struct tracks the symbol, quantity, entry price, funding rate, margin, and expiration handling specific to perpetual instruments. This enables crypto-native strategies that combine perpetuals with options or spot holdings.
The LegAble Trait Interface
All four variants implement the LegAble trait, which serves as the common contract for strategy calculations. This trait abstraction, defined alongside the Leg enum in src/model/leg/leg_enum.rs, exposes uniform methods regardless of the underlying instrument type:
get_symbol()– Returns the ticker symbol of the underlying asset.get_quantity()– Retrieves the number of units or contracts held.get_side()– Indicates long or short orientation.pnl_at_price()– Calculates profit and loss at a given underlying price.total_cost()– Aggregates entry costs including fees.fees()– Returns transaction costs.- Greeks calculation methods (
delta,gamma,theta,vega,rho) for risk metrics.
Because every leg implements these methods, strategies can iterate over a Vec<Leg> (retrieved via get_legs()) without pattern matching on specific instrument types. The enum's internal dispatch handles the differentiation, while strategies work with the uniform LegAble interface.
Building Multi-Asset Strategies
The Leg enum enables complex multi-asset strategies through convenience constructors and From implementations. Strategies like CoveredCall, ProtectivePut, and custom crypto-covered-calls collect heterogeneous positions into a unified Vec<Leg> for uniform processing.
Cash-and-Carry Arbitrage
This strategy combines a spot position with a short futures contract to capture the basis differential. The following example demonstrates constructing both legs using the Leg enum:
use optionstratlib::model::leg::{Leg, SpotPosition, FuturePosition};
use optionstratlib::model::types::Side;
use positive::Positive;
use chrono::Utc;
// Spot leg – long 100 shares of AAPL
let spot = SpotPosition::long(
"AAPL".to_string(),
Positive::HUNDRED,
Positive::from(150),
);
let spot_leg = Leg::spot(spot);
// Futures leg – short 2 ES contracts, expiring in 30 days
let future = FuturePosition::short(
"ES".to_string(),
Positive::TWO,
Positive::from(4500),
optionstratlib::model::ExpirationDate::Days(Positive::from(30)),
Positive::from(50), // contract size
Positive::from(15000), // margin per contract
);
let future_leg = Leg::future(future);
// Combine into a strategy-like container
let legs = vec![spot_leg, future_leg];
// Uniformly access data
for leg in &legs {
println!("{} {} @ {}",
leg.leg_type_name(),
leg.get_quantity(),
leg.get_symbol(),
);
}
This creates a two-leg, multi-asset strategy where the spot position offsets the futures exposure, all accessed through the same Leg API.
Crypto Covered Call with Perpetual
For crypto-native portfolios, strategies often combine perpetual swaps with options. The Leg enum accommodates this by mixing the Perpetual variant with traditional Option and Spot variants:
use optionstratlib::model::leg::{Leg, SpotPosition, PerpetualPosition};
use optionstratlib::model::types::Side;
use positive::Positive;
use chrono::Utc;
// Existing covered-call legs (spot + option) – omitted for brevity
let spot_leg = Leg::spot(/* SpotPosition::long(...) */);
let option_leg = Leg::option(/* Position::new(...) */);
// Perpetual leg – long BTC-USDT perpetual swap
let perp = PerpetualPosition::long(
"BTC-USDT-PERP".to_string(),
Positive::ONE,
Positive::from(50000),
optionstratlib::model::ExpirationDate::Perpetual, // perpetual never expires
Positive::from(1), // contract multiplier
Positive::from(1000), // initial margin
);
let perp_leg = Leg::perpetual(perp);
// Full strategy now holds three different asset types
let all_legs = vec![spot_leg, option_leg, perp_leg];
for leg in all_legs {
println!("Leg: {} – Symbol: {}", leg.leg_type_name(), leg.get_symbol());
}
The Leg enum cleanly accommodates a perpetual swap alongside traditional option-spot components, enabling complex crypto derivatives strategies.
Generic P&L Aggregation
The unified LegAble interface enables generic calculations across heterogeneous positions. Because every variant implements pnl_at_price(), strategies can aggregate P&L without type-specific logic:
use optionstratlib::model::leg::Leg;
use positive::Positive;
use rust_decimal::Decimal;
fn aggregate_pnl(legs: &[Leg], price: Positive) -> Decimal {
legs.iter()
.map(|leg| leg.pnl_at_price(price))
.sum()
}
// Example usage
let pnl = aggregate_pnl(&all_legs, Positive::from(52000));
println!("Total P&L at price 52,000 = {}", pnl);
Because every Leg implements pnl_at_price, the function works for any mix of instrument types, from spot holdings to perpetual swaps.
Summary
The Leg enum provides the type system foundation for complex portfolio construction in optionstratlib:
- Four unified variants (
Option,Spot,Future,Perpetual) encapsulate distinct instrument types while exposing a common interface. - The
LegAbletrait enables uniform access to symbols, quantities, sides, P&L calculations, and Greeks across all asset classes. - Heterogeneous vectors of
Legitems allow strategies to combine options with spot, futures, and perpetual positions without type-specific dispatch logic. - Source locations including
src/model/leg/leg_enum.rs,src/model/leg/spot.rs,src/model/leg/future.rs, andsrc/model/leg/perpetual.rsimplement this unified abstraction.
Frequently Asked Questions
How does the Leg enum handle different margin requirements across asset types?
The Leg enum delegates margin tracking to the underlying position structs. For futures, FuturePosition stores margin requirements in src/model/leg/future.rs [lines 71-79], while PerpetualPosition handles crypto-specific margin and funding rates in src/model/leg/perpetual.rs [lines 71-79]. When accessed through the LegAble interface, margin calculations remain uniform while preserving instrument-specific requirements.
Can I mix long and short positions in the same multi-asset strategy?
Yes. Each Leg variant carries its own Side orientation (long or short) within the underlying position struct. The get_side() method from the LegAble trait allows strategies to query directionality uniformly, enabling complex multi-asset strategies like cash-and-carry arbitrage (long spot, short futures) or reverse conversions without type-specific logic.
What performance benefits does the LegAble trait provide for strategy calculations?
The LegAble trait enables vectorized iteration over heterogeneous positions without dynamic dispatch overhead. Because Leg is an enum with inline storage, calling delta(), gamma(), or theta() on each leg in a Vec<Leg> uses static dispatch through the trait implementation. This allows strategies defined in files like src/strategies/covered_call.rs to aggregate risk metrics across options, spot, and futures positions with a single iteration loop.
How do I construct a Leg instance from a custom position struct?
The Leg enum provides convenience constructors (Leg::spot(), Leg::future(), Leg::perpetual(), Leg::option()) that wrap concrete position types. Additionally, From implementations allow implicit conversion. For example, passing a SpotPosition to a function expecting a Leg automatically invokes the conversion, or you can explicitly call Leg::spot(spot_position) as implemented in src/model/leg/leg_enum.rs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →