diff --git a/docs/order-templates.rst b/docs/order-templates.rst index 7280ff3..4abf954 100644 --- a/docs/order-templates.rst +++ b/docs/order-templates.rst @@ -74,6 +74,10 @@ Sell orders .. autofunction:: tda.orders.equities.equity_sell_market .. autofunction:: tda.orders.equities.equity_sell_limit +.. autofunction:: tda.orders.equities.equity_sell_stop +.. autofunction:: tda.orders.equities.equity_sell_stop_limit +.. autofunction:: tda.orders.equities.equity_sell_trailing_stop +.. autofunction:: tda.orders.equities.equity_sell_trailing_stop_limit +++++++++++++++++ Sell short orders diff --git a/tda/orders/equities.py b/tda/orders/equities.py index 248100d..0c30a58 100644 --- a/tda/orders/equities.py +++ b/tda/orders/equities.py @@ -1,7 +1,19 @@ from enum import Enum -from tda.orders.common import Duration, Session +from tda.orders.common import Duration, EquityInstruction +from tda.utils import EnumEnforcer +from tda.orders.common import (OrderStrategyType, OrderType, Session, StopType, + StopPriceLinkType, StopPriceLinkBasis) +from tda.orders.generic import OrderBuilder +enforcer = EnumEnforcer(False) + + +########################################################################## + +STOP_TYPES = ['MARK', 'LAST', 'BID', 'ASK', 'STANDARD'] +STOP_PRICE_LINK_BASIS = ['MANUAL', 'BASE', 'TRIGGER', 'LAST', 'BID', 'ASK', 'ASK_BID', 'MARK', 'AVERAGE'] +STOP_PRICE_LINK_TYPE = ['VALUE', 'PERCENT', 'TICK'] ########################################################################## # Buy orders @@ -79,6 +91,114 @@ def equity_sell_limit(symbol, quantity, price): .set_order_strategy_type(OrderStrategyType.SINGLE) .add_equity_leg(EquityInstruction.SELL, symbol, quantity)) + +def equity_sell_stop(symbol, quantity, stop_price, stop_type='MARK'): + """ + Returns a pre-filled :class:`~tda.orders.generic.OrderBuilder` for an equity + sell stop order + """ + + stop_type = enforcer.convert_enum(stop_type, StopType) + assert stop_type in STOP_TYPES, f'Stop Type must be one of {STOP_TYPES}' + + return (OrderBuilder() + .set_order_type(OrderType.STOP) + .set_quantity(int(quantity)) + .set_session(Session.NORMAL) + .set_stop_price(round(float(stop_price), 2)) + .set_stop_type(stop_type) + .set_duration(Duration.DAY) + .set_order_strategy_type(OrderStrategyType.SINGLE) + .add_equity_leg(EquityInstruction.SELL, symbol, quantity)) + + +def equity_sell_stop_limit(symbol, quantity, limit_price, stop_price, + stop_type='MARK'): + """ + Returns a pre-filled :class:`~tda.orders.generic.OrderBuilder` for an equity + sell stop limit order + """ + + stop_type = enforcer.convert_enum(stop_type, StopType) + assert stop_type in STOP_TYPES, f'Stop Type must be one of {STOP_TYPES}' + + return (OrderBuilder() + .set_order_type(OrderType.STOP_LIMIT) + .set_quantity(int(quantity)) + .set_session(Session.NORMAL) + .set_price(round(float(limit_price), 2)) + .set_stop_price(round(float(stop_price), 2)) + .set_stop_type(stop_type) + .set_duration(Duration.DAY) + .set_order_strategy_type(OrderStrategyType.SINGLE) + .add_equity_leg(EquityInstruction.SELL, symbol, quantity)) + + +def equity_sell_trailing_stop(symbol, quantity, trail_offset, trail_offset_type='PERCENT', + stop_type='MARK', stop_price_link_basis='MARK'): + """ + Returns a pre-filled :class:`~tda.orders.generic.OrderBuilder` for an equity + sell trailing stop order + """ + trail_offset_type = enforcer.convert_enum(trail_offset_type, StopPriceLinkType) + stop_type = enforcer.convert_enum(stop_type, StopType) + stop_price_link_basis = enforcer.convert_enum(stop_price_link_basis, StopType) + + assert trail_offset_type in STOP_PRICE_LINK_TYPE, \ + f'offset type must be one of {STOP_PRICE_LINK_TYPE}' + assert stop_type in STOP_TYPES, f'offset type must be one of {STOP_TYPES}' + assert stop_price_link_basis in STOP_PRICE_LINK_BASIS, \ + f'offset type must be one of {STOP_PRICE_LINK_BASIS}' + + if trail_offset_type == 'PERCENT' and (float(trail_offset) < 1 or (float(trail_offset)) > 99): + raise ValueError('When using percent, trailing offset must be >=1 and <=99') + + return (OrderBuilder() + .set_order_type(OrderType.TRAILING_STOP) + .set_quantity(int(quantity)) + .set_session(Session.NORMAL) + .set_stop_type(stop_type) + .set_duration(Duration.DAY) + .set_stop_price_offset(round(float(trail_offset), 2)) + .set_stop_price_link_basis(stop_price_link_basis) + .set_stop_price_link_type(trail_offset_type) + .set_order_strategy_type(OrderStrategyType.SINGLE) + .add_equity_leg(EquityInstruction.SELL, symbol, quantity)) + + +def equity_sell_trailing_stop_limit(symbol, quantity, trail_offset, limit_price, + trail_offset_type='PERCENT', stop_type='MARK', + stop_price_link_basis='MARK'): + """ + Returns a pre-filled :class:`~tda.orders.generic.OrderBuilder` for an equity + sell trailing stop limit order + """ + trail_offset_type = enforcer.convert_enum(trail_offset_type, StopPriceLinkType) + stop_type = enforcer.convert_enum(stop_type, StopType) + stop_price_link_basis = enforcer.convert_enum(stop_price_link_basis, StopType) + + assert trail_offset_type in STOP_PRICE_LINK_TYPE, \ + f'offset type must be one of {STOP_PRICE_LINK_TYPE}' + assert stop_type in STOP_TYPES, f'offset type must be one of {STOP_TYPES}' + assert stop_price_link_basis in STOP_PRICE_LINK_BASIS,\ + f'offset type must be one of {STOP_PRICE_LINK_BASIS}' + + if trail_offset_type == 'PERCENT' and (float(trail_offset) < 1 or float(trail_offset) > 99): + raise ValueError('When using percent, trailing offset must be >=1 and <=99') + + return (OrderBuilder() + .set_order_type(OrderType.TRAILING_STOP) + .set_quantity(int(quantity)) + .set_price(round(float(limit_price), 2)) + .set_session(Session.NORMAL) + .set_stop_type(stop_type) + .set_duration(Duration.DAY) + .set_stop_price_offset(round(float(trail_offset), 2)) + .set_stop_price_link_basis(stop_price_link_basis) + .set_stop_price_link_type(trail_offset_type) + .set_order_strategy_type(OrderStrategyType.SINGLE) + .add_equity_leg(EquityInstruction.SELL, symbol, quantity)) + ########################################################################## # Short sell orders