From a3069bb923a72fcff31db7bad7171167525cf966 Mon Sep 17 00:00:00 2001 From: Derek Melchin <38889814+DerekMelchin@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:03:06 -0600 Subject: [PATCH] Update manual-indicator.php --- .../option-indicators/manual-indicator.php | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/Resources/option-indicators/manual-indicator.php b/Resources/option-indicators/manual-indicator.php index 6e5f48fae8..cb44581444 100644 --- a/Resources/option-indicators/manual-indicator.php +++ b/Resources/option-indicators/manual-indicator.php @@ -124,38 +124,41 @@ def _update_contracts_and_greeks(self) -> None: # Get all the tradable Option contracts. - chain = self.option_chain() - # You can do further filtering here - - # Iterate all expiries - for expiry in set(x.id.date for x in chain): - contracts_for_expiry = [x for x in chain if x.id.date == expiry] + chain = self.option_chain(self._underlying).data_frame + if chain.empty: + return - # Iterate all strike prices among the contracts of the same expiry. - for strike in set(x.id.strike_price for x in contracts_for_expiry): - contracts_for_strike = [x for x in contracts_for_expiry if x.id.strike_price == strike] - - # Get the call and put, respectively. - calls = [x for x in contracts_for_strike if x.id.option_right == OptionRight.CALL] - puts = [x for x in contracts_for_strike if x.id.option_right == OptionRight.PUT] - # Skip if the call doesn't exist, the put doesn't exist, or they are already in the universe. - if not calls or not puts or calls[0].symbol in self.securities: - continue - - # Subscribe to both contracts. - call = calls[0].symbol - put = puts[0].symbol - self.(call) - self.(put) + # Filter the contracts down. For example, ATM contracts with the closest expiry. + expiry = chain.expiry.min() + chain = chain[chain.expiry == expiry] + chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) + abs_strike_delta = chain['abs_strike_delta'].min() + chain = chain[chain['abs_strike_delta'] == abs_strike_delta] + + # Group the contracts into call/put pairs. + contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] + paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index + symbols = [ + idx[-1] for idx in chain[ + chain['expiry'].isin(paired_contracts.levels[0]) & + chain['strike'].isin(paired_contracts.levels[1]) + ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index + ] + pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] + + for call, put in pairs: + # Subscribe to both contracts. + self.(call) + self.(put) - # Create and save the automatic indicators. - for contract_a, contract_b in [(call, put), (put, call)]: - self._indicators.append( - ( - contract_a, self.risk_free_interest_rate_model, - self._dividend_yield_provider, contract_b, self._option_pricing_model - ) - ) + # Create and save the automatic indicators. + for contract_a, contract_b in [(call, put), (put, call)]: + self._indicators.append( + ( + contract_a, self.risk_free_interest_rate_model, + self._dividend_yield_provider, contract_b, self._option_pricing_model + ) + ) def on_data(self, slice: Slice) -> None: # Iterate through the indicators.