def unit_quote_retrive(self, company: str) -> pd.DataFrame: logger.info(f"Retriving Detail Quote data for {company}") try: return DataRetrive.single_company_quote(f"{company}.NS") except (KeyError, IndexError, ValueError): logger.warning(f"Cannot retrive data for {company}") return "Invalid"
def _parallel_ema_indicator_n3( self, company: str, ema_canditate: Tuple[int, int] = (5, 13, 26), cutoff_date: Union[str, datetime.datetime] = 'today', verbosity: int = 1): logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_complete( company_name=f"{company}.NS") if company_df['Close'].isnull().sum() != 0: logger.warning(f"{company} have some missing value, fixing it") company_df.dropna(inplace=True) try: EMA_A = exponential_moving_avarage(data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[0], verbosity=verbosity) EMA_B = exponential_moving_avarage(data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[1], verbosity=verbosity) EMA_C = exponential_moving_avarage(data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[2], verbosity=verbosity) percentage_diff_cb = percentage_diff_analysis(EMA_C, EMA_B) percentage_diff_ca = percentage_diff_analysis(EMA_C, EMA_A) percentage_diff_ba = percentage_diff_analysis(EMA_B, EMA_A) if (percentage_diff_cb < 1) and (percentage_diff_ca < 1) and (percentage_diff_ba < 1): action = 'buy' else: action = 'sell' except (KeyError, IndexError, ValueError): logger.warning(f"{company} has less record than minimum required") EMA_A, EMA_B, EMA_C, action = pd.NA, pd.NA, pd.NA, pd.NA return { 'company': company, 'ema_date': now_strting if cutoff_date == 'today' else cutoff_date.strftime('%d-%m-%Y'), f'ema{str(ema_canditate[0])}': EMA_A, f'ema{str(ema_canditate[1])}': EMA_B, f'ema{str(ema_canditate[2])}': EMA_C, # 'percentage_diffCB': percentage_diff_cb, # 'percentage_diffCA': percentage_diff_ca, # 'percentage_diffBA': percentage_diff_ba, 'action': action }
def unit_momentum(self, company: str, start, end, verbosity: int = 1): logger.info(f"Retriving data for {company}") try: company_df = DataRetrive.single_company_specific( company_name=f"{company}.NS", start_date=start, end_date=end ) company_df.reset_index(inplace=True) ar_yearly = annualized_rate_of_return( end_date=company_df.iloc[-1].Close, start_date=company_df.iloc[0].Close, duration=1, ) # (company_df.iloc[-30,0] - company_df.iloc[0,0]).days/365) ar_monthly = annualized_rate_of_return( end_date=company_df.iloc[-1].Close, start_date=get_appropriate_date_momentum( company_df, company, verbosity=verbosity )[1], duration=(company_df.iloc[-1, 0] - company_df.iloc[-30, 0]).days / 30, ) monthly_start_date = get_appropriate_date_momentum( company_df, company, verbosity=0 )[0].strftime("%d-%m-%Y") except (IndexError, KeyError, ValueError): if verbosity > 0: logger.debug(f"Data is not available for: {company}") company_df = pd.DataFrame( {"Date": [datetime.datetime(1000, 1, 1)] * 30, "Close": [pd.NA] * 30} ) ar_yearly, ar_monthly, monthly_start_date = pd.NA, pd.NA, pd.NA return { "company": company, "yearly_start_date": company_df.iloc[0].Date.strftime("%d-%m-%Y"), "yearly_start_date_close": company_df.iloc[0].Close, "yearly_end_date": company_df.iloc[-1].Date.strftime("%d-%m-%Y"), "yearly_end_date_close": company_df.iloc[-1].Close, "return_yearly": ar_yearly, "monthly_start_date": monthly_start_date, "monthly_start_date_close": company_df.iloc[-30].Close, "monthly_end_date": company_df.iloc[-1].Date.strftime("%d-%m-%Y"), "monthly_end_date_close": company_df.iloc[-1].Close, "return_monthly": ar_monthly, }
def unit_vol_indicator_n_days(self, company: str = None, duration: int = 90): end = datetime.datetime.now() start = end - dateutil.relativedelta.relativedelta(days=duration) logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_specific( company_name=f"{company}.NS", start_date=start, end_date=end ) buy_stock = company_df.iloc[-1].Volume > company_df["Volume"].mean() print(f"Problem with {company}, moving on") return { "company": company, "current date": company_df.index[-1].strftime("%d-%m-%Y"), "start date": company_df.index[0].strftime("%d-%m-%Y"), "current volume": company_df.iloc[-1].Volume, "mean volume": company_df["Volume"].mean(), "close price": company_df.iloc[-1].Close, "action": buy_stock, }
def _parallel_vol_indicator_n_days(self, company: str = None, duration: int = 90): end = datetime.datetime.now() start = end - dateutil.relativedelta.relativedelta(days=duration) logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_specific( company_name=f"{company}.NS", start_date=start, end_date=end) buy_stock = company_df.iloc[-1].Volume > company_df['Volume'].mean() print(f"Problem with {company}, moving on") return { 'company': company, 'current date': company_df.index[-1].strftime('%d-%m-%Y'), 'start date': company_df.index[0].strftime('%d-%m-%Y'), 'current volume': company_df.iloc[-1].Volume, 'mean volume': company_df['Volume'].mean(), 'close price': company_df.iloc[-1].Close, 'action': buy_stock }
def _parallel_ema_indicator(self, company: str = None, ema_canditate: Tuple[int, int] = (50, 200), cutoff_date: Union[ str, datetime.datetime] = 'today', verbosity: int = 1) -> Dict: logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_complete( company_name=f"{company}.NS") if company_df['Close'].isnull().sum() != 0: logger.warning(f"{company} have some missing value, fixing it") company_df.dropna(inplace=True) try: EMA_A = exponential_moving_avarage(data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[0], verbosity=verbosity) EMA_B = exponential_moving_avarage(data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[1], verbosity=verbosity) if EMA_A > EMA_B: action = 'buy' else: action = 'sell' except (KeyError, IndexError, ValueError): logger.warning(f"{company} has less record than minimum rexquired") EMA_A, EMA_B, action = pd.NA, pd.NA, pd.NA return { 'company': company, 'ema_date': now_strting if cutoff_date == 'today' else cutoff_date.strftime('%d-%m-%Y'), f'ema{str(ema_canditate[0])}': EMA_A, f'ema{str(ema_canditate[1])}': EMA_B, 'action': action }
def unit_ema_indicator( self, company: str = None, ema_canditate: Tuple[int, int] = (50, 200), cutoff_date: Union[str, datetime.datetime] = "today", verbosity: int = 1, ) -> Dict: logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_complete(company_name=f"{company}.NS") if company_df["Close"].isnull().sum() != 0: logger.warning(f"{company} have some missing value, fixing it") company_df.dropna(inplace=True) try: EMA_A = exponential_moving_average( data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[0], verbosity=verbosity, ) EMA_B = exponential_moving_average( data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[1], verbosity=verbosity, ) if EMA_A > EMA_B: action = "buy" else: action = "sell" except (KeyError, IndexError, ValueError): logger.warning(f"{company} has less record than minimum rexquired") EMA_A, EMA_B, action = pd.NA, pd.NA, pd.NA return { "company": company, "ema_date": now_strting if cutoff_date == "today" else cutoff_date.strftime("%d-%m-%Y"), f"ema{str(ema_canditate[0])}": EMA_A, f"ema{str(ema_canditate[1])}": EMA_B, "action": action, }
def _parallel_quote_retrive(self, company: str) -> pd.DataFrame: logger.info(f"Retriving Detail Quote data for {company}") return DataRetrive.single_company_quote(f'{company}.NS')
def ema_crossover_indicator_detail(self, ema_canditate: Tuple[int, int, int] = (5, 13, 26), save: bool = True, export_path: str = '.', verbosity: int = 1) -> pd.DataFrame: """Exponential moving average for crossover triple period technique Parameters ---------- ema_canditate : Tuple[int, int, int], optional Three Period (or days) to calculate EMA, by default (5,13,26) save : bool, optional Save to hard disk, by default True export_path : str, optional Path to save, to be used only if 'save' is true, by default '.' verbosity : int, optional Level of detail logging,1=< Deatil, 0=Less detail , by default 1 Returns ------- pd.DataFrame """ logger.info("Performing EMA Indicator Task") ema_short = self._ema_indicator_n3(ema_canditate=ema_canditate, verbosity=verbosity) logger.info("Extarcting detail company quote data") batch_company_quote = pd.DataFrame() with multiprocessing.Pool(multiprocessing.cpu_count() - 1) as pool: company_quote = pool.map(self._parallel_quote_retrive, ema_short['company']) for single_company_quote in company_quote: batch_company_quote = batch_company_quote.append( single_company_quote) batch_company_quote = batch_company_quote.reset_index().rename( columns={'index': 'company'}) batch_company_quote = batch_company_quote[[ 'company', 'longName', 'price', 'regularMarketVolume', 'marketCap', 'bookValue', 'priceToBook', 'averageDailyVolume3Month', 'averageDailyVolume10Day', 'fiftyTwoWeekLowChange', 'fiftyTwoWeekLowChangePercent', 'fiftyTwoWeekRange', 'fiftyTwoWeekHighChange', 'fiftyTwoWeekHighChangePercent', 'fiftyTwoWeekLow', 'fiftyTwoWeekHigh' ]] batch_company_quote['company'] = batch_company_quote[ 'company'].str.replace('.NS', '') ema_quote = ema_short.merge(batch_company_quote, on='company', validate='1:1') if verbosity > 0: logger.debug(f"Here are sample 5 company\n{ema_quote.head()}") if save is not False: ema_quote.to_csv( f"{export_path}/ema_indicator_detail{str(ema_canditate[0])}-{str(ema_canditate[1])}_{len(self.data['company'])}company_{now_strting}.csv", index=False) if verbosity > 0: logger.debug( f"Exported at {export_path}/ema_indicator_detail{str(ema_canditate[0])}-{str(ema_canditate[1])}_{len(self.data['company'])}company_{now_strting}.csv" ) else: return ema_quote
def relative_momentum_with_ema( self, end_date: str = "today", top_company_count: int = 20, ema_canditate: Tuple[int, int] = (50, 200), save: bool = True, export_path: str = ".", verbosity: int = 1, ) -> Optional[pd.DataFrame]: """The strategy is used to identity stocks with 'good performance' based on desired 'return' duration and 'exponential moving avg'. Args: end_date (str, optional): End date of of stock record to retrive. Must be in format: dd/mm/yyyy. Defaults to 'today'. top_company_count (int, optional): No of top company to retrieve based on Annualized return. Defaults to 20. ema_canditate (Tuple[int, int], optional): Period (or days) to calculate EMA. Defaults to (50, 200). save (bool, optional): Wether to export to disk. Defaults to True. export_path (str, optional): Path to export csv.To be used only if 'save' is True. Defaults to '.'. verbosity (int, optional): Level of detail logging,1=< Deatil, 0=Less detail. Defaults to 1. Returns: Record based on monthly and yearly calculation and EMA calculation Example: ```python from stock_analysis import MomentumStrategy sa = MomentumStrategy('./data/company_list.yaml') mes = sa.relative_momentum_with_ema('01/06/2020', 30) ``` """ logger.info("Performing Momentum Strategy task") momentum_df = self.relative_momentum( end_date=end_date, top_company_count=top_company_count, save=False, verbosity=verbosity, ) momentum_df.reset_index(drop=True, inplace=True) ind = Indicator(company_name=momentum_df.loc[:, "company"]) logger.info( f"Performing EMA task on top {top_company_count} company till {end_date}" ) if end_date == "today": cutoff_date = end_date save_date = datetime.datetime.now().strftime("%d-%m-%Y") else: save_date = end_date.replace("/", "-") cutoff_date = datetime.datetime.strptime(end_date, "%d/%m/%Y") assert isinstance(cutoff_date, datetime.datetime), "Incorrect date type" ema_df = ind.ema_indicator( ema_canditate=ema_canditate, cutoff_date=cutoff_date, save=False, verbosity=verbosity, ) momentum_ema_df = momentum_df.merge(ema_df, on="company", validate="1:1") if save is True: new_folder(export_path) momentum_ema_df.reset_index(drop=True, inplace=True) momentum_ema_df.to_csv( f"{export_path}/momentum_ema{ema_canditate[0]}-{ema_canditate[1]}_{save_date}_top_{top_company_count}.csv", index=False, ) logger.debug( f"Saved at {export_path}/momentum_ema{ema_canditate[0]}-{ema_canditate[1]}_{save_date}_top_{top_company_count}.csv" ) if verbosity > 0: logger.debug(f"Sample output:\n{momentum_ema_df.head()}") else: return momentum_ema_df
def momentum_with_ema_strategy(self, end_date: str = 'today', top_company_count: int = 20, ema_canditate: Tuple[int, int] = (50, 200), save: bool = True, export_path: str = '.', verbosity: int = 1) -> pd.DataFrame: """The strategy is used to identity stocks with 'good performance' based on desired 'return' duration and 'exponential moving avg'. Parameters ---------- end_date : str, optional End date of of stock record to retrive. Must be in format: dd/mm/yyyy, by default 'today' for current date top_company_count : int, optional No of top company to retrieve based on Annualized return, by default 20 ema_canditate : Tuple[int, int], optional Period (or days) to calculate EMA, by default (50,200) save : int, optional Wether to export to disk, by default True export_path : str, optional Path to export csv.To be used only if 'save' is True,by default'.' verbosity : int, optional Level of detail logging,1=< Deatil, 0=Less detail , by default 1 Returns ------- pd.DataFrame Record based on monthly and yearly calculation and EMA calculation """ logger.info("Performing Momentum Strategy task") momentum_df = self.momentum_strategy( end_date=end_date, top_company_count=top_company_count, save=False, verbosity=verbosity) momentum_df.reset_index(drop=True, inplace=True) ind = Indicator(company_name=momentum_df.loc[:, 'company']) logger.info( f"Performing EMA task on top {top_company_count} company till {end_date}" ) if end_date == 'today': cutoff_date = end_date save_date = datetime.datetime.now().strftime('%d-%m-%Y') else: save_date = end_date.replace('/', '-') cutoff_date = datetime.datetime.strptime(end_date, '%d/%m/%Y') assert isinstance(cutoff_date, datetime.datetime), 'Incorrect date type' ema_df = ind.ema_indicator(ema_canditate=ema_canditate, cutoff_date=cutoff_date, save=False, verbosity=verbosity) momentum_ema_df = momentum_df.merge(ema_df, on='company', validate='1:1') if save is True: momentum_ema_df.reset_index(drop=True, inplace=True) momentum_ema_df.to_csv( f"{export_path}/momentum_ema{ema_canditate[0]}-{ema_canditate[1]}_{save_date}_top_{top_company_count}.csv", index=False) logger.debug( f"Saved at {export_path}/momentum_ema{ema_canditate[0]}-{ema_canditate[1]}_{save_date}_top_{top_company_count}.csv" ) if verbosity > 0: logger.debug(f"Sample output:\n{momentum_ema_df.head()}") else: return momentum_ema_df
def test_company_name(): logger.info("Loading") with open(f"{data_path}/sample_company.yaml", "r") as f: data = yaml.load(f, Loader=yaml.FullLoader) assert isinstance(data["company"], list)
def unit_dma_absolute( self, company: str = None, end_date: Union[str, datetime.datetime] = "today", period: int = 200, cutoff: int = 5, ) -> Dict: logger.info(f"Retriving data for {company}") if end_date == "today": cutoff_date = datetime.datetime.today() else: cutoff_date = datetime.datetime.strptime(end_date, "%d/%m/%Y").date() start_date = cutoff_date - dateutil.relativedelta.relativedelta(months=18) try: company_df = DataRetrive.single_company_specific( company_name=f"{company}.NS", start_date=start_date, end_date=cutoff_date, ) if company_df["Close"].isnull().sum() != 0: logger.warning(f"{company} have some missing value, fixing it") company_df.dropna(inplace=True) except (KeyError, ValueError, IndexError): company_df = pd.DataFrame( { "Open": pd.NA, "High": pd.NA, "Low": pd.NA, "Close": pd.NA, "Adj Close": pd.NA, "Volume": pd.NA, }, index=["Date"], ) action = "Invalid" try: sma = simple_moving_average(company_df["Close"][-(period - 1) :], period) turnover_value = turnover(company_df["Volume"][-period:], sma) / 10000000 buy = sma + (sma * (cutoff / 100)) sell = sma - (sma * (cutoff / 100)) if (buy < company_df["Close"][-1]) and (turnover_value > 1): action = "buy" elif (sell > company_df["Close"][-1]) and (turnover_value > 1): action = "sell" elif (sell < company_df["Close"][-1] < buy) and (turnover_value < 1): action = "no action" long_name = self.unit_quote_retrive(company)["longName"][0] current_price = company_df["Close"][-1] except (KeyError, IndexError, ValueError, TypeError, ZeroDivisionError): logger.warning(f"{company} has less record than minimum rexquired") long_name, sma, current_price, action, turnover_value, buy, sell = ( f"{company} (Invalid name)", "Invalid", "Invalid", "Invalid", "Invalid", "Invalid", "Invalid", ) company = f"Problem with {company}" return { "company name": long_name, "nse symbol": company, "sma_date": now_strting if cutoff_date == "today" else cutoff_date.strftime("%d-%m-%Y"), "current price": current_price, "sma": sma, "ideal buy": buy, "ideal sell": sell, "turnover in cr.": turnover_value, "action": action, }
def unit_ema_indicator_n3( self, company: str, ema_canditate: Tuple[int, int] = (5, 13, 26), cutoff_date: Union[str, datetime.datetime] = "today", verbosity: int = 1, ) -> Dict: logger.info(f"Retriving data for {company}") company_df = DataRetrive.single_company_complete(company_name=f"{company}.NS") if company_df["Close"].isnull().sum() != 0: logger.warning(f"{company} have some missing value, fixing it") company_df.dropna(inplace=True) try: EMA_A = exponential_moving_average( data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[0], verbosity=verbosity, ) EMA_B = exponential_moving_average( data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[1], verbosity=verbosity, ) EMA_C = exponential_moving_average( data_df=company_df, cutoff_date=cutoff_date, period=ema_canditate[2], verbosity=verbosity, ) percentage_diff_cb = percentage_diff(EMA_C, EMA_B, return_absolute=True) percentage_diff_ca = percentage_diff(EMA_C, EMA_A, return_absolute=True) percentage_diff_ba = percentage_diff(EMA_B, EMA_A, return_absolute=True) if ( (percentage_diff_cb < 1) and (percentage_diff_ca < 1) and (percentage_diff_ba < 1) ): action = "buy" else: action = "sell" except (KeyError, IndexError, ValueError): logger.warning(f"{company} has less record than minimum required") EMA_A, EMA_B, EMA_C, action = pd.NA, pd.NA, pd.NA, pd.NA return { "company": company, "ema_date": now_strting if cutoff_date == "today" else cutoff_date.strftime("%d-%m-%Y"), f"ema{str(ema_canditate[0])}": EMA_A, f"ema{str(ema_canditate[1])}": EMA_B, f"ema{str(ema_canditate[2])}": EMA_C, # 'percentage_diffCB': percentage_diff_cb, # 'percentage_diffCA': percentage_diff_ca, # 'percentage_diffBA': percentage_diff_ba, "action": action, }
import warnings from stock_analysis.unit_strategy import UnitStrategy from stock_analysis.indicator import Indicator from stock_analysis.utils.logger import logger logger = logger() warnings.filterwarnings('ignore') logger.info("Please give your input to Start Stock Analysis") task = int( input("Choice task to Perform \n1.Unit Strategy \n2.Indicator \nChoice: ")) # Tasks for Unit Strategy if task == 1: yaml_path = input("Enter Company name yaml file location: ") sa = UnitStrategy(path=yaml_path) sub_task = int( input("Choose sub task to perform: \n1.Momentum Strategy\n2.Momentum with EMA \nChoice: ")) # Sub-task for Momentum Strategy if sub_task == 1: sub_task_para = int(input("Choose input parameter nature \n1.Default --> i)Date = today, \ ii)Top company count = 20 iii)Export path = Same folder iv)Verbosity (level of detail logging): Detail\ \n2.Manual \nChoice: ")) if sub_task_para == 1: sa.momentum_strategy() elif sub_task_para == 2: logger.info( "In Manual mode whereever you see 'default' then presing enter key will take default value") sub_task_para_date = ( input("Input desired date (default: today)(eg: 01/06/2020): ") or 'today')
def ema_crossover_detail_indicator( self, ema_canditate: Tuple[int, int, int] = (5, 13, 26), save: bool = True, export_path: str = ".", verbosity: int = 1, ) -> Optional[pd.DataFrame]: """Exponential moving average for crossover triple period technique Args: ema_canditate (Tuple[int, int, int], optional): Three Period (or days) to calculate EMA. Defaults to (5, 13, 26). save (bool, optional): Save to hard disk. Defaults to True. export_path (str, optional): Path to save, to be used only if 'save' is true. Defaults to ".". verbosity (int, optional): Level of detail logging,1=< Deatil, 0=Less detail. Defaults to 1. Returns: Results is based on crossover ema and detailed metrics Example: ```python from stock_analysis.indicator import Indicator ind = Indicator('./data/company_list.yaml') ema = ind.ema_crossover_detail_indicator((5,10,020), '01/06/2020') ``` """ logger.info("Performing EMA Indicator Task") ema_short = self._ema_indicator_n3(ema_canditate=ema_canditate, verbosity=verbosity) logger.info("Extarcting detail company quote data") batch_company_quote = pd.DataFrame() with parallel_backend(n_jobs=-1, backend="multiprocessing"): company_quote = Parallel()( delayed(self.unit_quote_retrive)(company) for company in ema_short["company"]) for single_company_quote in company_quote: if isinstance(single_company_quote, pd.DataFrame): batch_company_quote = batch_company_quote.append( single_company_quote) batch_company_quote = batch_company_quote.reset_index().rename( columns={"index": "company"}) batch_company_quote = batch_company_quote[[ "company", "longName", "price", "regularMarketVolume", "marketCap", "bookValue", "priceToBook", "averageDailyVolume3Month", "averageDailyVolume10Day", "fiftyTwoWeekLowChange", "fiftyTwoWeekLowChangePercent", "fiftyTwoWeekRange", "fiftyTwoWeekHighChange", "fiftyTwoWeekHighChangePercent", "fiftyTwoWeekLow", "fiftyTwoWeekHigh", ]] batch_company_quote["company"] = batch_company_quote[ "company"].str.replace(".NS", "") ema_quote = ema_short.merge(batch_company_quote, on="company", validate="1:1") if verbosity > 0: logger.debug(f"Here are sample 5 company\n{ema_quote.head()}") if save is not False: ema_quote.to_csv( f"{export_path}/ema_crossover_detail_indicator{str(ema_canditate[0])}-{str(ema_canditate[1])}-{str(ema_canditate[2])}_{len(self.data['company'])}company_{now_strting}.csv", index=False, ) if verbosity > 0: logger.debug( f"Exported at {export_path}/ema_crossover_detail_indicator{str(ema_canditate[0])}-{str(ema_canditate[1])}-{str(ema_canditate[2])}_{len(self.data['company'])}company_{now_strting}.csv" ) else: return ema_quote
def test_yaml(): logger.info("Loading") with open(f"{data_path}/sample_company.yaml", "r") as f: data = yaml.load(f, Loader=yaml.FullLoader) assert isinstance(data, dict), "Incorrect data"