async def create_closing_order(session: TastyAPISession, acct: TradingAccount, order: OrderChangeAccountObject, close_order_type: OrderType, limit_price_delta: float = None, stop_order_trigger: float = None): LOGGER.warning(f"Creating Closing Order: {AUTOCLOSE_ORDER_TYPE.name}") #Market Orders don't have price effect, assumes we're Buying first flipped_ope = (OrderPriceEffect.CREDIT if (not (getattr(order, 'price_effect_enum', None)) or OrderPriceEffect.DEBIT == order.price_effect_enum.DEBIT) else OrderPriceEffect.DEBIT) details = OrderDetails( type=close_order_type, price=((order.price + limit_price_delta) if close_order_type.is_limit() else None), stop_trigger=(stop_order_trigger if close_order_type.is_stop() else None), price_effect=flipped_ope, is_open_order=False) new_order = Order(details) for leg in order.legs: opt = CompleteOption(instrument_type=leg.instrument_type, symbol_complete=leg.symbol, quantity=leg.quantity) new_order.add_leg(opt) res = await acct.execute_order(new_order, session, dry_run=False) LOGGER.info(f"Closing Order opened?: {bool(res)}")
async def EnterTrade(ticker, price: Decimal, expiry, strike: Decimal, opt_type: OptionType, quantity = 1): sub_values = {"Quote": ["/ES"]} if Settings.EnterWithMarketOrder: details = OrderDetails(type=OrderType.MARKET, price=None, price_effect=OrderPriceEffect.DEBIT) else: details = OrderDetails(type=OrderType.LIMIT, price=price, price_effect=OrderPriceEffect.DEBIT) new_order = Order(details) opt = Option(ticker=ticker, quantity=quantity, expiry=expiry, strike=strike, option_type=opt_type, underlying_type=UnderlyingType.EQUITY) new_order.add_leg(opt) return await tasty_acct.execute_order(new_order, tasty_client, dry_run=False)
def get_closing_order_object(self, price: Decimal, stop_trigger: Decimal = None, order_type: OrderType = OrderType.LIMIT): closing_order_price_effect = self.get_closing_order_price_effect() details = OrderDetails(type=order_type, price=price, price_effect=closing_order_price_effect, stop_trigger=stop_trigger) new_order = Order(details) opt = self.get_option_obj() new_order.add_leg(opt) return new_order
async def main_loop(session: TastyAPISession, streamer: DataStreamer): # sub_values = { # "Greeks": [ # ".VIX180718C21", # ".YUM180518C95" # ] # } sub_values = { "Quote": ["/ES"] } accounts = await TradingAccount.get_remote_accounts(session) acct = accounts[0] LOGGER.info('Accounts available: %s', accounts) orders = await Order.get_remote_orders(session, acct) LOGGER.info('Number of active orders: %s', len(orders)) # Execute an order details = OrderDetails( type=OrderType.LIMIT, price=400, price_effect=OrderPriceEffect.CREDIT) new_order = Order(details) opt = Option( ticker='AKS', quantity=1, expiry=date(2018, 10, 19), strike=3.0, option_type=OptionType.CALL, underlying_type=UnderlyingType.EQUITY ) new_order.add_leg(opt) res = await acct.execute_order(new_order, session, dry_run=True) LOGGER.info('Order executed successfully: %s', res) # Get an options chain undl = underlying.Underlying('AKS') chain = await option_chain.get_option_chain(session, undl) LOGGER.info('Chain strikes: %s', chain.get_all_strikes()) await streamer.add_data_sub(sub_values) async for item in streamer.listen(): LOGGER.info('Received item: %s' % item.data)
async def execute_order(self, order: Order, session, dry_run=True): """ Execute an order. If doing a dry run, the order isn't placed but simulated (server-side). Args: order (Order): The order object to execute. session (TastyAPISession): The tastyworks session onto which to execute the order. dry_run (bool): Whether to do a test (dry) run. Returns: bool: Whether the order was successful. """ if not order.check_is_order_executable(): raise Exception('Order is not executable, most likely due to missing data') if not session.is_active(): raise Exception('The supplied session is not active and valid') url = '{}/accounts/{}/orders'.format( session.API_url, self.account_number ) if dry_run: url = f'{url}/dry-run' body = _get_execute_order_json(order) async with aiohttp.request('POST', url, headers=session.get_request_headers(), json=body) as resp: if resp.status == 201: return True elif resp.status == 400: raise Exception('Order execution failed, message: {}'.format(await resp.text())) else: raise Exception('Unknown remote error, status code: {}, message: {}'.format(resp.status, await resp.text()))
async def execute_order(self, order: Order, session, dry_run=True): """ Execute an order. If doing a dry run, the order isn't placed but simulated (server-side). Args: order (Order): The order object to execute. dry_run (bool): Whether to do a test (dry) run. Returns: bool: Whether the order was successful. """ if not order.check_is_order_executable(): raise Exception( 'Order is not executable, most likely due to missing data') if not session.is_active(): raise Exception('The supplied session is not active and valid') url = '{}/accounts/{}/orders'.format(session.API_url, self.account_number) if dry_run: url = f'{url}/dry-run' body = { 'source': order.details.source, 'order-type': order.details.type.value, 'price': str(order.details.price), 'price-effect': order.details.price_effect.value, 'time-in-force': order.details.time_in_force, 'legs': self._get_legs_request_data(order) } async with aiohttp.request('POST', url, headers=session.get_request_headers(), json=body) as resp: if resp.status == 201: return True elif resp.status == 400: raise Exception('Order execution failed, message: {}'.format( await resp.text())) else: raise Exception( 'Unknown remote error, status code: {}, message: {}'. format(resp.status, await resp.text()))
async def call(underlying: str, quantity: int, strike: Optional[int] = None, all_expirations: Optional[bool] = False, delta: Optional[int] = None): if strike is not None and delta is not None: raise TastyworksCLIError( 'Must specify either delta or strike, but not both.') elif not strike and not delta: raise TastyworksCLIError( 'Please specify either delta or strike for the option.') elif abs(delta) > 100: raise TastyworksCLIError( 'Delta value is too high, -100 <= delta <= 100') sesh = await RenewableTastyAPISession.create() undl = Underlying(underlying) expiration = await choose_expiration(sesh, undl, all_expirations) streamer = await DataStreamer.create(sesh) if not strike: chain = await get_option_chain(sesh, undl, expiration) dxfeeds = [ option.symbol_dxf for option in chain.options if option.option_type == OptionType.CALL ] greeks = await streamer.stream('Greeks', dxfeeds) lowest = abs(greeks[0]['delta'] * 100.0 - delta) index = 0 for i in range(1, len(greeks)): diff = abs(greeks[i]['delta'] * 100.0 - delta) if diff < lowest: index = i lowest = diff for option in chain.options: if option.symbol_dxf == greeks[index]['eventSymbol']: strike = option.strike break option = Option(ticker=underlying, quantity=abs(quantity), expiry=expiration, strike=strike, option_type=OptionType.CALL, underlying_type=UnderlyingType.EQUITY) quote = await streamer.stream('Quote', [option.symbol_dxf]) bid = quote[0]['bidPrice'] ask = quote[0]['askPrice'] mid = (bid + ask) / 2 await streamer.close() console = Console() table = Table(show_header=True, header_style='bold', title_style='bold', title=f'Quote for {underlying} {strike}C {expiration}') table.add_column('Bid', style='green', width=8, justify='center') table.add_column('Mid', width=8, justify='center') table.add_column('Ask', style='red', width=8, justify='center') table.add_row(f'{bid:.2f}', f'{mid:.2f}', f'{ask:.2f}') console.print(table) price = input( 'Please enter a limit price for the entire order (default mid): ') if not price: price = round(mid * abs(quantity), 2) price = Decimal(price) details = OrderDetails(type=OrderType.LIMIT, price=price, price_effect=OrderPriceEffect.CREDIT if quantity < 0 else OrderPriceEffect.DEBIT) order = Order(details) order.add_leg(option) acct = await get_account(sesh) details = await acct.get_balance(sesh) nl = Decimal(details['net-liquidating-value']) data = await acct.execute_order(order, sesh, dry_run=True) bp = Decimal(data['buying-power-effect']['change-in-buying-power']) percent = bp / nl * Decimal(100) # bp_effect = data['buying-power-effect']['change-in-buying-power-effect'] fees = Decimal(data['fee-calculation']['total-fees']) table = Table(show_header=True, header_style='bold', title_style='bold', title='Order Review') table.add_column('Quantity', width=8, justify='center') table.add_column('Symbol', width=8, justify='center') table.add_column('Strike', width=8, justify='center') table.add_column('Type', width=8, justify='center') table.add_column('Expiration', width=10, justify='center') table.add_column('Price', width=8, justify='center') table.add_column('BP', width=8, justify='center') table.add_column('% of NL', width=8, justify='center') table.add_column('Fees', width=8, justify='center') table.add_row(f'{quantity}', underlying, f'{strike:.2f}', 'CALL', f'{expiration}', f'${price:.2f}', f'${bp:.2f}', f'{percent:.2f}%', f'${fees}') console.print(table) if data['warnings']: console.print('[bold orange]Warnings:[/bold orange]') for warning in data['warnings']: console.print(f'[i gray]{warning}[/i gray]') if get_confirmation('Send order? Y/n '): await acct.execute_order(order, sesh, dry_run=False)