def test_read_price_metrics_with_exception(self): with patch.object(intrinio_data, 'get_daily_stock_close_prices', return_value={ "2020-06-09": 54.74 }), \ patch.object(intrinio_data, 'get_sma_indicator', return_value={ "2020-06-09": 43.80299999999999 }), \ patch.object(intrinio_data, 'get_macd_indicator', return_value={ "2020-06-09": { "macd_histogram": 9111766583116911, "macd_line": 2.085415403516656, "signal_line": 1.1742387452049647 } }): ticker_file_path = "%s/djia30.json" % constants.TICKER_DATA_DIR macd_strategy = MACDCrossoverStrategy(self.ticker_list, date(2020, 6, 8), 0.0016, 12, 26, 9) with self.assertRaises(ValidationError): macd_strategy._read_price_metrics('AAPL')
def test_analyze_security_3(self): ''' Significant divergence. both MACD and Signal negative ''' macd_strategy = MACDCrossoverStrategy(self.ticker_list, date(2020, 6, 10), 0.0016, 12, 26, 9) current_price = 100 macd_line = -10 signal_line = -9 self.assertFalse( macd_strategy._analyze_security(current_price, macd_line, signal_line))
def test_analyze_security_2(self): ''' No significant divergence. Both MACD and histogram positive ''' macd_strategy = MACDCrossoverStrategy(self.ticker_list, date(2020, 6, 10), 0.0016, 12, 26, 9) current_price = 100 macd_line = 10 signal_line = 9.9 self.assertTrue( macd_strategy._analyze_security(current_price, macd_line, signal_line))
def test_analyze_security_7(self): ''' No Significant divergence. MACD negative, Signal positive ''' macd_strategy = MACDCrossoverStrategy(self.ticker_list, date(2020, 6, 10), 0.0016, 12, 26, 9) current_price = 100 macd_line = -0.01 signal_line = 0.01 self.assertTrue( macd_strategy._analyze_security(current_price, macd_line, signal_line))
def test_read_price_metrics(self): with patch.object(intrinio_data, 'get_daily_stock_close_prices', return_value=self.price_dict), \ patch.object(intrinio_data, 'get_sma_indicator', return_value=self.sma_dict), \ patch.object(intrinio_data, 'get_macd_indicator', return_value=self.macd_dict): macd_strategy = MACDCrossoverStrategy(self.ticker_list, date(2020, 6, 8), 0.0016, 12, 26, 9) (current_price, macd_line, signal_line) = macd_strategy._read_price_metrics('AAPL') self.assertEqual(current_price, 54.74) self.assertEqual(macd_line, 2.085415403516656) self.assertEqual(signal_line, 1.1742387452049647)
def main(): ''' Main testing script ''' try: ticker_list = TickerList.from_local_file("%s/djia30.json" % (constants.APP_DATA_DIR)) config = Configuration.try_from_s3(constants.STRATEGY_CONFIG_FILE_NAME, 'sa') #macd_strategy = MACDCrossoverStrategy.from_configuration(config, 'sa') macd_strategy = MACDCrossoverStrategy(ticker_list, date(2020, 6, 16), 0.0016, 12, 16, 9) macd_strategy.generate_recommendation() macd_strategy.display_results() ''' #pd_strategy = PriceDispersionStrategy.from_configuration(config, 'sa') pd_strategy = PriceDispersionStrategy( ticker_list, '2020-06', date(2020, 5, 16), 3) pd_strategy.generate_recommendation() pd_strategy.display_results() ''' except Exception as e: log.error("Could run script, because, %s" % (str(e)))
def test_recommendation_set_dates(self): price_date = date(2020, 6, 8) with patch.object(intrinio_data, 'get_daily_stock_close_prices', return_value=self.price_dict), \ patch.object(intrinio_data, 'get_sma_indicator', return_value=self.sma_dict), \ patch.object(intrinio_data, 'get_macd_indicator', return_value=self.macd_dict): strategy = MACDCrossoverStrategy(self.ticker_list, price_date, 0.0016, 12, 26, 9) strategy.generate_recommendation() recommendation_set = strategy.recommendation_set self.assertEqual(recommendation_set.model['valid_from'], str(date(2020, 6, 8))) self.assertEqual(recommendation_set.model['valid_to'], str(date(2020, 6, 8))) self.assertEqual(recommendation_set.model['price_date'], str(date(2020, 6, 8)))
def test_from_configuration_valid(self): ticker_list = TickerList.from_local_file("%s/djia30.json" % (constants.APP_DATA_DIR)) price_date = date(2020, 6, 3) with patch.object(util, 'get_business_date', return_value=price_date), \ patch.object(TickerList, 'from_s3', return_value=ticker_list): strategy = MACDCrossoverStrategy.from_configuration( Configuration.from_local_config( constants.STRATEGY_CONFIG_FILE_NAME), 'sa') self.assertEqual(strategy.analysis_date, price_date)
def main(): """ Main Function for this script """ description = """ Executes a backtest for the MACD_CROSSOVER strategy given a ticker list, start date, end date and threshold, e.g. -0.02, used to determine the maximum allowed loss of a trade before a stop loss takes effect. """ parser = argparse.ArgumentParser(description=description) parser.add_argument("-ticker_list", help="Ticker List File", type=str, required=True) date_parser = lambda s: datetime.strptime(s, '%Y/%m/%d') parser.add_argument("-start_date", help="Backtest start date (YYY/MM/DD)", type=date_parser, required=True) parser.add_argument("-end_date", help="Backtest end date (YYYY/MM/DD)", type=date_parser, required=True) parser.add_argument("-stop_loss_theshold", help="Stop Loss Threshold factor, e.g. -0.02 (-2%%)", type=float, required=True) args = parser.parse_args() ticker_file_name = args.ticker_list start_date = args.start_date end_date = args.end_date stop_loss_theshold = args.stop_loss_theshold log.info("Parameters:") log.info("Ticker File: %s" % ticker_file_name) log.info("Start Date: %s" % start_date) log.info("End Date: %s" % end_date) log.info("Stop Loss Threshold: %.2f" % stop_loss_theshold) log.info("") log.info("MACD Configuration:") log.info("Divergence Tolerance Factor: %f" % DIVERGENCE_FACTOR_THRESHOLD) log.info("Slow Period: %d" % SLOW_PERIOD) log.info("Fast Period: %d" % FAST_PERIOD) log.info("Signal Period: %d" % SIGNAL_PERIOD) log.info("") ticker_list = TickerList.from_local_file( "%s/%s" % (constants.TICKER_DATA_DIR, ticker_file_name)) trade_dict = { 'ticker': [], 'buy_date': [], 'buy_price': [], 'sell_date': [], 'sell_price': [], 'trade_pnl_factor': [], 'false_signal': [] } try: date_list = get_business_date_list(start_date, end_date) init_portfolio_dict(ticker_list) for i in range(0, len(date_list) - 1): recommendation_date = date_list[i] trade_date = date_list[i + 1] strategy = MACDCrossoverStrategy(ticker_list, recommendation_date, DIVERGENCE_FACTOR_THRESHOLD, FAST_PERIOD, SLOW_PERIOD, SIGNAL_PERIOD) strategy.generate_recommendation() print("processing: %s" % recommendation_date, end="\r") unwound_positions = trade(trade_date, strategy.recommendation_set) for position in unwound_positions: (ticker, buy_date, sell_date, buy_price) = position sell_price = get_close_price(ticker, sell_date) pnl = ((sell_price / buy_price) - 1) false_signal = 0 if (pnl < stop_loss_theshold): sell_price = 'STOP_LOSS' false_signal = 1 pnl = stop_loss_theshold if (pnl < 0): false_signal = 1 trade_dict['ticker'].append(ticker) trade_dict['buy_date'].append(buy_date) trade_dict['buy_price'].append(buy_price) trade_dict['sell_date'].append(str(sell_date)) trade_dict['sell_price'].append(sell_price) trade_dict['trade_pnl_factor'].append(pnl) trade_dict['false_signal'].append(false_signal) trades_by_ticker = [] for ticker in ticker_list.model['ticker_symbols']: trades_by_ticker.append(calculate_returns(ticker, trade_dict)) trade_dataframe = pd.concat(trades_by_ticker) display_results(trade_dataframe) except Exception as e: log.error("Could run script, because, %s" % (str(e))) raise e
def main(): """ Main function for this script """ try: app_ns = parse_params() log.info("Parameters:") log.info("Application Namespace: %s" % app_ns) business_date = util.get_business_date( constants.BUSINESS_DATE_DAYS_LOOKBACK, constants.BUSINESS_DATE_CUTOVER_TIME) log.info("Business Date is: %s" % business_date) # test all connectivity upfront, so if there any issues # the problem becomes more apparent connector_test.test_aws_connectivity() connector_test.test_intrinio_connectivity() log.info('Loading Strategy Configuration "%s" from S3' % constants.STRATEGY_CONFIG_FILE_NAME) configuration = Configuration.try_from_s3( constants.STRATEGY_CONFIG_FILE_NAME, app_ns) log.info("Initalizing Trading Strategies") strategies = [ PriceDispersionStrategy.from_configuration(configuration, app_ns), MACDCrossoverStrategy.from_configuration(configuration, app_ns) ] notification_list = [] for strategy in strategies: recommendation_set = None try: log.info("Executing %s strategy" % strategy.STRATEGY_NAME) recommendation_set = SecurityRecommendationSet.from_s3( app_ns, strategy.S3_RECOMMENDATION_SET_OBJECT_NAME) except AWSError as awe: if not awe.resource_not_found(): raise awe log.info("No recommendation set was found in S3.") if recommendation_set == None \ or not recommendation_set.is_current(business_date): strategy.generate_recommendation() strategy.display_results() recommendation_set = strategy.recommendation_set recommendation_set.save_to_s3( app_ns, strategy.S3_RECOMMENDATION_SET_OBJECT_NAME) notification_list.append(recommendation_set) else: log.info( "Recommendation set is still valid. There is nothing to do") recommendation_svc.notify_new_recommendation( notification_list, app_ns) except Exception as e: stack_trace = traceback.format_exc() log.error("Could run script, because: %s" % (str(e))) log.error(stack_trace)
def test_from_configuration_invalid(self): with patch('support.constants.CONFIG_FILE_PATH', "./test/config-unittest-bad/"): bad_config = Configuration.from_local_config("bad-test-config.ini") with self.assertRaises(ValidationError): MACDCrossoverStrategy.from_configuration(bad_config, 'sa')