From 64175f0a0f1aaddffd4fcfd35c57be10bb2edac8 Mon Sep 17 00:00:00 2001 From: Aaron Janeiro Stone Date: Fri, 17 Jan 2025 13:19:42 -0500 Subject: [PATCH] refactor: cleanup --- alpaca/common/requests.py | 4 +- alpaca/trading/models.py | 4 +- alpaca/trading/requests.py | 15 ++- tests/trading/test_trading_models.py | 95 ++++++++++++------- .../trading_client/test_order_routes.py | 93 +++++++++++------- 5 files changed, 139 insertions(+), 72 deletions(-) diff --git a/alpaca/common/requests.py b/alpaca/common/requests.py index 160a5b1f..bec09274 100644 --- a/alpaca/common/requests.py +++ b/alpaca/common/requests.py @@ -76,5 +76,7 @@ def map_values(val: Any) -> Any: # {trusted_contact: {}, contact: {}, identity: None, etc} # so we do a simple list comprehension to filter out None and {} return { - key: map_values(val) for key, val in d.items() if val is not None and val != {} and len(str(val)) > 0 + key: map_values(val) + for key, val in d.items() + if val is not None and val != {} and len(str(val)) > 0 } diff --git a/alpaca/trading/models.py b/alpaca/trading/models.py index c87bb7c7..0f360597 100644 --- a/alpaca/trading/models.py +++ b/alpaca/trading/models.py @@ -275,7 +275,9 @@ def root_validator(cls, data: dict) -> dict: if "_is_sub_mleg" not in data or not data["_is_sub_mleg"]: if "legs" not in data or data["legs"] == "": raise ValueError("legs is required for mleg orders") - if data["legs"] is not None: # it is possible when querying individual legs that this is None + if ( + data["legs"] is not None + ): # it is possible when querying individual legs that this is None if len(data["legs"]) < 1: raise ValueError("legs must have at least one order") for leg in data["legs"]: diff --git a/alpaca/trading/requests.py b/alpaca/trading/requests.py index 1ed4b059..f1f8701e 100644 --- a/alpaca/trading/requests.py +++ b/alpaca/trading/requests.py @@ -165,6 +165,7 @@ class StopLossRequest(NonEmptyRequest): stop_price: float limit_price: Optional[float] = None + class OptionLegRequest(NonEmptyRequest): """ Used for providing details for a leg of a multi-leg order. @@ -323,18 +324,24 @@ def root_validator(cls, values: dict) -> dict: if "legs" not in values or values["legs"] is None: raise ValueError("legs is required for the mleg order class.") l_len = len(values["legs"]) - if l_len> 4: + if l_len > 4: raise ValueError("At most 4 legs are allowed for the mleg order class.") if l_len < 2: - raise ValueError("At least 2 legs are required for the mleg order class.") + raise ValueError( + "At least 2 legs are required for the mleg order class." + ) n_unique = len(set([l.symbol for l in values["legs"]])) if n_unique != l_len: raise ValueError("All legs must have unique symbols.") else: if "symbol" not in values or values["symbol"] is None: - raise ValueError("symbol is required for all order classes other than mleg.") + raise ValueError( + "symbol is required for all order classes other than mleg." + ) if "side" not in values or values["side"] is None: - raise ValueError("side is required for all order classes other than mleg.") + raise ValueError( + "side is required for all order classes other than mleg." + ) return values diff --git a/tests/trading/test_trading_models.py b/tests/trading/test_trading_models.py index 147f06d6..d8002c30 100644 --- a/tests/trading/test_trading_models.py +++ b/tests/trading/test_trading_models.py @@ -80,33 +80,38 @@ def test_trailing_stop_order_type(): def test_mleg_options() -> None: - symbols = ["AAPL250117P00200000", - "AAPL250117P00250000", - "AAPL250117P00300000", - "AAPL250117P00350000", - "AAPL250117P00400000"] + symbols = [ + "AAPL250117P00200000", + "AAPL250117P00250000", + "AAPL250117P00300000", + "AAPL250117P00350000", + "AAPL250117P00400000", + ] def kwargs_as_string(**kwargs): return ", ".join([f"{k}={v}" for k, v in kwargs.items()]) def order_request_factory(is_market: bool): if is_market: + def factory(warn_validated: bool = True, **kwargs): - o = MarketOrderRequest( - **kwargs - ) + o = MarketOrderRequest(**kwargs) if warn_validated: - warnings.warn(f"MarketOrderRequest({kwargs_as_string(**kwargs)}) passed validation!") + warnings.warn( + f"MarketOrderRequest({kwargs_as_string(**kwargs)}) passed validation!" + ) return o + else: + def factory(warn_validated: bool = True, **kwargs): - o = LimitOrderRequest( - limit_price=1, - **kwargs - ) + o = LimitOrderRequest(limit_price=1, **kwargs) if warn_validated: - warnings.warn(f"LimitOrderRequest({kwargs_as_string(**kwargs)}) passed validation!") + warnings.warn( + f"LimitOrderRequest({kwargs_as_string(**kwargs)}) passed validation!" + ) return o + return factory for is_mkt in [True, False]: @@ -114,12 +119,16 @@ def factory(warn_validated: bool = True, **kwargs): # Good requests for sym_index in range(2, 5): - o_req(warn_validated=False, - qty=1, - time_in_force=TimeInForce.DAY, - order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) for symbol in - symbols[:sym_index]]) + o_req( + warn_validated=False, + qty=1, + time_in_force=TimeInForce.DAY, + order_class=OrderClass.MLEG, + legs=[ + OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) + for symbol in symbols[:sym_index] + ], + ) # Bad requests with pytest.raises(ValueError) as e: @@ -127,8 +136,15 @@ def factory(warn_validated: bool = True, **kwargs): o_req( time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY), - OptionLegRequest(symbol=symbols[1], ratio_qty=1, side=OrderSide.SELL)]) + legs=[ + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY + ), + OptionLegRequest( + symbol=symbols[1], ratio_qty=1, side=OrderSide.SELL + ), + ], + ) assert "At least one of qty or notional must be provided" in str(e.value) with pytest.raises(ValueError) as e: @@ -137,7 +153,10 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY)]) + legs=[ + OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY) + ], + ) assert "At least 2 legs are required for the mleg order class" in str(e.value) with pytest.raises(ValueError) as e: @@ -146,16 +165,16 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) - for symbol in symbols]) + legs=[ + OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) + for symbol in symbols + ], + ) assert "At most 4 legs are allowed for the mleg order class." in str(e.value) with pytest.raises(ValueError) as e: # Missing legs - o_req( - qty=1, - time_in_force=TimeInForce.DAY, - order_class=OrderClass.MLEG) + o_req(qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG) assert "legs is required for the mleg order class." in str(e.value) with pytest.raises(ValueError) as e: @@ -164,16 +183,24 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY), - OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.SELL)]) + legs=[ + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY + ), + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.SELL + ), + ], + ) assert "All legs must have unique symbols." in str(e.value) - with pytest.raises(ValueError) as e: # Legs in non-MLEG order o_req( qty=1, time_in_force=TimeInForce.DAY, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY)]) - + legs=[ + OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY) + ], + ) diff --git a/tests/trading/trading_client/test_order_routes.py b/tests/trading/trading_client/test_order_routes.py index 9b626785..74223fc3 100644 --- a/tests/trading/trading_client/test_order_routes.py +++ b/tests/trading/trading_client/test_order_routes.py @@ -627,33 +627,38 @@ def test_order_position_intent(reqmock, trading_client: TradingClient): def test_mleg_request_validation() -> None: - symbols = ["AAPL250117P00200000", - "AAPL250117P00250000", - "AAPL250117P00300000", - "AAPL250117P00350000", - "AAPL250117P00400000"] + symbols = [ + "AAPL250117P00200000", + "AAPL250117P00250000", + "AAPL250117P00300000", + "AAPL250117P00350000", + "AAPL250117P00400000", + ] def kwargs_as_string(**kwargs): return ", ".join([f"{k}={v}" for k, v in kwargs.items()]) def order_request_factory(is_market: bool): if is_market: + def factory(warn_validated: bool = True, **kwargs): - o = MarketOrderRequest( - **kwargs - ) + o = MarketOrderRequest(**kwargs) if warn_validated: - warnings.warn(f"MarketOrderRequest({kwargs_as_string(**kwargs)}) passed validation!") + warnings.warn( + f"MarketOrderRequest({kwargs_as_string(**kwargs)}) passed validation!" + ) return o + else: + def factory(warn_validated: bool = True, **kwargs): - o = LimitOrderRequest( - limit_price=1, - **kwargs - ) + o = LimitOrderRequest(limit_price=1, **kwargs) if warn_validated: - warnings.warn(f"LimitOrderRequest({kwargs_as_string(**kwargs)}) passed validation!") + warnings.warn( + f"LimitOrderRequest({kwargs_as_string(**kwargs)}) passed validation!" + ) return o + return factory for is_mkt in [True, False]: @@ -661,12 +666,16 @@ def factory(warn_validated: bool = True, **kwargs): # Good requests for sym_index in range(2, 5): - o_req(warn_validated=False, - qty=1, - time_in_force=TimeInForce.DAY, - order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) for symbol in - symbols[:sym_index]]) + o_req( + warn_validated=False, + qty=1, + time_in_force=TimeInForce.DAY, + order_class=OrderClass.MLEG, + legs=[ + OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) + for symbol in symbols[:sym_index] + ], + ) # Bad requests with pytest.raises(ValueError): @@ -674,8 +683,15 @@ def factory(warn_validated: bool = True, **kwargs): o_req( time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY), - OptionLegRequest(symbol=symbols[1], ratio_qty=1, side=OrderSide.SELL)]) + legs=[ + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY + ), + OptionLegRequest( + symbol=symbols[1], ratio_qty=1, side=OrderSide.SELL + ), + ], + ) with pytest.raises(ValueError): # Too few legs @@ -683,7 +699,10 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY)]) + legs=[ + OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY) + ], + ) with pytest.raises(ValueError): # Too many legs @@ -691,15 +710,15 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) - for symbol in symbols]) + legs=[ + OptionLegRequest(symbol=symbol, ratio_qty=1, side=OrderSide.BUY) + for symbol in symbols + ], + ) with pytest.raises(ValueError): # Missing legs - o_req( - qty=1, - time_in_force=TimeInForce.DAY, - order_class=OrderClass.MLEG) + o_req(qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG) with pytest.raises(ValueError): # Repeated symbols across legs @@ -707,12 +726,22 @@ def factory(warn_validated: bool = True, **kwargs): qty=1, time_in_force=TimeInForce.DAY, order_class=OrderClass.MLEG, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY), - OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.SELL)]) + legs=[ + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY + ), + OptionLegRequest( + symbol=symbols[0], ratio_qty=1, side=OrderSide.SELL + ), + ], + ) with pytest.raises(ValueError): # Legs in non-MLEG order o_req( qty=1, time_in_force=TimeInForce.DAY, - legs=[OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY)]) + legs=[ + OptionLegRequest(symbol=symbols[0], ratio_qty=1, side=OrderSide.BUY) + ], + )