def plot_view_stock(df: pd.DataFrame, symbol: str, interval: str): """ Plot the loaded stock dataframe Parameters ---------- df: Dataframe Dataframe of prices and volumnes symbol: str Symbol of ticker interval: str Stock data resolution for plotting purposes """ df.sort_index(ascending=True, inplace=True) bar_colors = ["r" if x[1].Open < x[1].Close else "g" for x in df.iterrows()] try: fig, ax = plt.subplots( 2, 1, gridspec_kw={"height_ratios": [3, 1]}, figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI, ) except Exception as e: print(e) print( "Encountered an error trying to open a chart window. Check your X server configuration." ) return # In order to make nice Volume plot, make the bar width = interval if interval == "1440min": bar_width = timedelta(days=1) title_string = "Daily" else: bar_width = timedelta(minutes=int(interval.split("m")[0])) title_string = f"{int(interval.split('m')[0])} min" ax[0].yaxis.tick_right() if "Adj Close" in df.columns: ax[0].plot(df.index, df["Adj Close"], c=cfgPlot.VIEW_COLOR) else: ax[0].plot(df.index, df["Close"], c=cfgPlot.VIEW_COLOR) ax[0].set_xlim(df.index[0], df.index[-1]) ax[0].set_xticks([]) ax[0].yaxis.set_label_position("right") ax[0].set_ylabel("Share Price ($)") ax[0].grid(axis="y", color="gainsboro", linestyle="-", linewidth=0.5) ax[0].spines["top"].set_visible(False) ax[0].spines["left"].set_visible(False) ax[1].bar( df.index, df.Volume / 1_000_000, color=bar_colors, alpha=0.8, width=bar_width ) ax[1].set_xlim(df.index[0], df.index[-1]) ax[1].yaxis.tick_right() ax[1].yaxis.set_label_position("right") ax[1].set_ylabel("Volume (1M)") ax[1].grid(axis="y", color="gainsboro", linestyle="-", linewidth=0.5) ax[1].spines["top"].set_visible(False) ax[1].spines["left"].set_visible(False) ax[1].set_xlabel("Time") fig.suptitle( symbol + " " + title_string, size=20, x=0.15, y=0.95, fontfamily="serif", fontstyle="italic", ) if gtff.USE_ION: plt.ion() fig.tight_layout(pad=2) plt.setp(ax[1].get_xticklabels(), rotation=20, horizontalalignment="right") plt.show() print("")
def display_last_contracts( past_transaction_days: int = 2, num: int = 20, sum_contracts: bool = False, export: str = "", ): """Last government contracts [Source: quiverquant.com] Parameters ---------- past_transaction_days: int Number of days to look back num: int Number of contracts to show sum_contracts: bool Flag to show total amount of contracts given out. export: str Format to export data """ df_contracts = quiverquant_model.get_government_trading("contracts") if df_contracts.empty: print("No government contracts found\n") return df_contracts.sort_values("Date", ascending=False) df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]) df_contracts.drop_duplicates(inplace=True) df = df_contracts.copy() df_contracts = df_contracts[ df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days]) ] df_contracts = df_contracts[["Date", "Ticker", "Amount", "Description", "Agency"]][ :num ] if gtff.USE_TABULATE_DF: df_contracts["Description"] = df_contracts["Description"].apply( lambda x: "\n".join(textwrap.wrap(x, 50)) ) print( tabulate( df_contracts, headers=df_contracts.columns, tablefmt="fancy_grid", showindex=False, floatfmt=".2f", ) ) else: print(df_contracts.to_string(index=False)) if sum_contracts: fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df["Date"] = pd.to_datetime(df["Date"]).dt.date df.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title("Total amount of government contracts given") if gtff.USE_ION: plt.ion() fig.tight_layout() plt.show() print("") export_data(export, os.path.dirname(os.path.abspath(__file__)), "lastcontracts", df)
def display_hist_contracts(ticker: str, raw: bool = False, export: str = ""): """Show historical quarterly government contracts [Source: quiverquant.com] Parameters ---------- ticker: str Ticker to get congress trading data from raw: bool Flag to display raw data export: str Format to export data """ df_contracts = quiverquant_model.get_government_trading( "quarter-contracts", ticker=ticker ) if df_contracts.empty: print("No quarterly government contracts found\n") return amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values quarter_ticks = [ f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr) ] if raw: if gtff.USE_TABULATE_DF: print( tabulate( df_contracts, headers=df_contracts.columns, tablefmt="fancy_grid", showindex=False, ) ) else: print(df_contracts.to_string()) else: fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15) ax.set_xlim([-0.5, len(amounts) - 0.5]) ax.set_xticks(np.arange(0, len(amounts))) ax.set_xticklabels(quarter_ticks) ax.grid() ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}") ax.set_xlabel("Date") ax.set_ylabel("Amount ($1k)") fig.tight_layout() if gtff.USE_ION: plt.ion() plt.show() export_data(export, os.path.dirname(os.path.abspath(__file__)), "histcont") print("")
def plot_aroon( s_ticker: str, s_interval: str, df_stock: pd.DataFrame, length: int, scalar: int, export: str, ): """Plot Aroon indicator Parameters ---------- s_ticker : str Ticker s_interval: str Interval of price data df_stock : pd.DataFrame.length Dataframe of prices length:int Length of window scalar : int Scalar variable """ df_ta = trend_indicators_model.aroon(df_stock, length, scalar) fig, ax = plt.subplots(3, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax0 = ax[0] # Daily if s_interval == "1440min": ax0.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) # Intraday else: ax0.plot(df_stock.index, df_stock["Close"].values, "k", lw=2) ax0.set_title(f"Aroon on {s_ticker}") ax0.set_xlim(df_stock.index[0], df_stock.index[-1]) ax0.set_ylabel("Share Price ($)") ax0.grid(b=True, which="major", color="#666666", linestyle="-") ax1 = ax[1] ax1.plot(df_ta.index, df_ta.iloc[:, 0].values, "r", lw=2) ax1.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=2) ax1.set_xlim(df_stock.index[0], df_stock.index[-1]) ax1.axhline(50, linewidth=1, color="k", ls="--") ax1.legend( [f"Aroon DOWN ({df_ta.columns[0]})", f"Aroon UP ({df_ta.columns[1]})"], loc="upper left", ) ax1.grid(b=True, which="major", color="#666666", linestyle="-") ax1.set_ylim([0, 100]) ax2 = ax[2] ax2.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=2) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.set_xlabel("Time") ax2.legend([f"Aroon OSC ({df_ta.columns[2]})"], loc="upper left") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_ylim([-100, 100]) if gtff.USE_ION: plt.ion() fig.tight_layout(pad=1) plt.show() plt.gcf().autofmt_xdate() print("") export_data( export, os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), "aroon", df_ta, )
def topsells_command( gov_type="", past_transactions_months: int = 5, num: int = 10, raw: bool = False, ): """Displays most sold stocks by the congress/senate/house [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug( "gov-topsells %s %s %s %s", gov_type, past_transactions_months, num, raw, ) possible_args = ["congress", "senate", "house"] if gov_type == "": gov_type = "congress" elif gov_type not in possible_args: raise Exception( "Enter a valid government argument, options are: congress, senate and house" ) # Retrieve Data df_gov = quiverquant_model.get_government_trading(gov_type) # Output Data if df_gov.empty: raise Exception(f"No {gov_type} trading data found\n") df_gov = df_gov.sort_values("TransactionDate", ascending=False) start_date = datetime.now() - timedelta(days=past_transactions_months * 30) df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna() df_gov["Range"] = df_gov["Range"].apply(lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x) df_gov["min"] = df_gov["Range"].apply(lambda x: x.split("-")[0].strip( "$").replace(",", "").strip().replace(">$", "").strip()) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1].replace(",", "").strip().strip("$").replace( ">$", "").strip() if "-" in x else x.strip("$").replace( ",", "").replace(">$", "").strip()) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]), axis=1, ) description = None df_gov = df_gov.sort_values("TransactionDate", ascending=True) if raw: df = pd.DataFrame( df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values( ascending=True).abs().head(n=num)) description = "```" + df.to_string() + "```" fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head( n=num).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title( f"{num} most sold stocks over last {past_transactions_months} months" f" (upper bound) for {gov_type}") plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False) fig.tight_layout() plt.savefig("gov_topsells.png") imagefile = "gov_topsells.png" imagefile = image_border(imagefile) return { "title": f"Stocks: [quiverquant.com] Top sells for {gov_type.upper()}", "imagefile": imagefile, "description": description, }
def aroon(l_args, s_ticker, s_interval, df_stock): parser = argparse.ArgumentParser( add_help=False, prog="aroon", description=""" The word aroon is Sanskrit for "dawn's early light." The Aroon indicator attempts to show when a new trend is dawning. The indicator consists of two lines (Up and Down) that measure how long it has been since the highest high/lowest low has occurred within an n period range. \n \n When the Aroon Up is staying between 70 and 100 then it indicates an upward trend. When the Aroon Down is staying between 70 and 100 then it indicates an downward trend. A strong upward trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above the Aroon Up, it indicates a weakening of the upward trend (and vice versa). """, ) parser.add_argument( "-l", "--length", action="store", dest="n_length", type=check_positive, default=25, help="length", ) parser.add_argument( "-s", "--scalar", action="store", dest="n_scalar", type=check_positive, default=100, help="scalar", ) parser.add_argument( "-o", "--offset", action="store", dest="n_offset", type=check_positive, default=0, help="offset", ) try: ns_parser = parse_known_args_and_warn(parser, l_args) if not ns_parser: return df_ta = ta.aroon( high=df_stock["2. high"], low=df_stock["3. low"], length=ns_parser.n_length, scalar=ns_parser.n_scalar, offset=ns_parser.n_offset, ).dropna() plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.subplot(311) # Daily if s_interval == "1440min": plt.plot(df_stock.index, df_stock["5. adjusted close"].values, "k", lw=2) # Intraday else: plt.plot(df_stock.index, df_stock["4. close"].values, "k", lw=2) plt.title(f"Aroon on {s_ticker}") plt.xlim(df_stock.index[0], df_stock.index[-1]) plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.subplot(312) plt.plot(df_ta.index, df_ta.iloc[:, 0].values, "r", lw=2) plt.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=2) plt.xlim(df_stock.index[0], df_stock.index[-1]) plt.axhline(50, linewidth=1, color="k", ls="--") plt.legend( [f"Aroon DOWN ({df_ta.columns[0]})", f"Aroon UP ({df_ta.columns[1]})"] ) plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.ylim([0, 100]) plt.subplot(313) plt.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=2) plt.xlabel("Time") plt.legend([f"Aroon OSC ({df_ta.columns[2]})"]) plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.ylim([-100, 100]) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e) print("")
def regression(other_args: List[str], s_ticker: str, df_stock: pd.DataFrame, polynomial: int): """ Train a regression model Parameters ---------- other_args: List[str] Argparse arguments s_ticker: str Stock ticker df_stock: pd.DataFrame Dataframe of stock prices polynomial: int Order of polynomial """ parser = argparse.ArgumentParser( add_help=False, prog="regression", description=""" Regression attempts to model the relationship between two variables by fitting a linear/quadratic/cubic/other equation to observed data. One variable is considered to be an explanatory variable, and the other is considered to be a dependent variable. """, ) parser.add_argument( "-i", "--input", action="store", dest="n_inputs", type=check_positive, default=40, help="number of days to use for prediction.", ) parser.add_argument( "-d", "--days", action="store", dest="n_days", type=check_positive, default=5, help="prediction days.", ) parser.add_argument( "-j", "--jumps", action="store", dest="n_jumps", type=check_positive, default=1, help="number of jumps in training data.", ) parser.add_argument( "-e", "--end", action="store", type=valid_date, dest="s_end_date", default=None, help="The end date (format YYYY-MM-DD) to select - Backtesting", ) if polynomial == USER_INPUT: parser.add_argument( "-p", "--polynomial", action="store", dest="n_polynomial", type=check_positive, required=True, help="polynomial associated with regression.", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return # BACKTESTING if ns_parser.s_end_date: if ns_parser.s_end_date < df_stock.index[0]: print( "Backtesting not allowed, since End Date is older than Start Date of historical data\n" ) return if ns_parser.s_end_date < get_next_stock_market_days( last_stock_day=df_stock.index[0], n_next_days=ns_parser.n_inputs + ns_parser.n_days, )[-1]: print( "Backtesting not allowed, since End Date is too close to Start Date to train model\n" ) return future_index = get_next_stock_market_days( last_stock_day=ns_parser.s_end_date, n_next_days=ns_parser.n_days) if future_index[-1] > datetime.datetime.now(): print( "Backtesting not allowed, since End Date + Prediction days is in the future\n" ) return df_future = df_stock[future_index[0]:future_index[-1]] df_stock = df_stock[:ns_parser.s_end_date] # Split training data stock_x, stock_y = splitTrain.split_train( df_stock["5. adjusted close"].values, ns_parser.n_inputs, ns_parser.n_days, ns_parser.n_jumps, ) if not stock_x: print("Given the model parameters more training data is needed.\n") return # Machine Learning model if polynomial == LINEAR: model = linear_model.LinearRegression(n_jobs=-1) else: if polynomial == USER_INPUT: polynomial = ns_parser.n_polynomial model = pipeline.make_pipeline( preprocessing.PolynomialFeatures(polynomial), linear_model.Ridge()) model.fit(stock_x, stock_y) l_predictions = model.predict( df_stock["5. adjusted close"].values[-ns_parser.n_inputs:].reshape( 1, -1))[0] # Prediction data l_pred_days = get_next_stock_market_days( last_stock_day=df_stock["5. adjusted close"].index[-1], n_next_days=ns_parser.n_days, ) df_pred = pd.Series(l_predictions, index=l_pred_days, name="Price") # Plotting plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.plot(df_stock.index, df_stock["5. adjusted close"], lw=2) # BACKTESTING if ns_parser.s_end_date: plt.title( f"BACKTESTING: Regression (polynomial {polynomial}) on {s_ticker} - {ns_parser.n_days} days prediction" ) else: plt.title( f"Regression (polynomial {polynomial}) on {s_ticker} - {ns_parser.n_days} days prediction" ) plt.xlim(df_stock.index[0], get_next_stock_market_days(df_pred.index[-1], 1)[-1]) plt.xlabel("Time") plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.plot( [df_stock.index[-1], df_pred.index[0]], [df_stock["5. adjusted close"].values[-1], df_pred.values[0]], lw=1, c="tab:green", linestyle="--", ) plt.plot(df_pred.index, df_pred, lw=2, c="tab:green") plt.axvspan(df_stock.index[-1], df_pred.index[-1], facecolor="tab:orange", alpha=0.2) _, _, ymin, ymax = plt.axis() plt.vlines(df_stock.index[-1], ymin, ymax, linewidth=1, linestyle="--", color="k") # BACKTESTING if ns_parser.s_end_date: plt.plot( df_future.index, df_future["5. adjusted close"], lw=2, c="tab:blue", ls="--", ) plt.plot( [df_stock.index[-1], df_future.index[0]], [ df_stock["5. adjusted close"].values[-1], df_future["5. adjusted close"].values[0], ], lw=1, c="tab:blue", linestyle="--", ) if gtff.USE_ION: plt.ion() plt.show() # BACKTESTING if ns_parser.s_end_date: plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.subplot(211) plt.plot( df_future.index, df_future["5. adjusted close"], lw=2, c="tab:blue", ls="--", ) plt.plot(df_pred.index, df_pred, lw=2, c="green") plt.scatter(df_future.index, df_future["5. adjusted close"], c="tab:blue", lw=3) plt.plot( [df_stock.index[-1], df_future.index[0]], [ df_stock["5. adjusted close"].values[-1], df_future["5. adjusted close"].values[0], ], lw=2, c="tab:blue", ls="--", ) plt.scatter(df_pred.index, df_pred, c="green", lw=3) plt.plot( [df_stock.index[-1], df_pred.index[0]], [df_stock["5. adjusted close"].values[-1], df_pred.values[0]], lw=2, c="green", ls="--", ) plt.title("BACKTESTING: Real data price versus Prediction") plt.xlim(df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1)) plt.xticks( [ df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1) ], visible=True, ) plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.legend(["Real data", "Prediction data"]) plt.xticks([]) plt.subplot(212) plt.axhline(y=0, color="k", linestyle="--", linewidth=2) plt.plot( df_future.index, 100 * (df_pred.values - df_future["5. adjusted close"].values) / df_future["5. adjusted close"].values, lw=2, c="red", ) plt.scatter( df_future.index, 100 * (df_pred.values - df_future["5. adjusted close"].values) / df_future["5. adjusted close"].values, c="red", lw=5, ) plt.title( "BACKTESTING: Error between Real data and Prediction [%]") plt.plot( [df_stock.index[-1], df_future.index[0]], [ 0, 100 * (df_pred.values[0] - df_future["5. adjusted close"].values[0]) / df_future["5. adjusted close"].values[0], ], lw=2, ls="--", c="red", ) plt.xlim(df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1)) plt.xticks( [ df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1) ], visible=True, ) plt.xlabel("Time") plt.ylabel("Prediction Error (%)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.legend(["Real data", "Prediction data"]) if gtff.USE_ION: plt.ion() plt.show() # Refactor prediction dataframe for backtesting print df_pred.name = "Prediction" df_pred = df_pred.to_frame() df_pred["Real"] = df_future["5. adjusted close"] if gtff.USE_COLOR: patch_pandas_text_adjustment() print("Time Real [$] x Prediction [$]") print( df_pred.apply(price_prediction_backtesting_color, axis=1).to_string()) else: print(df_pred[["Real", "Prediction"]].round(2).to_string()) print("") print_prediction_kpis(df_pred["Real"].values, df_pred["Prediction"].values) else: # Print prediction data print_pretty_prediction(df_pred, df_stock["5. adjusted close"].values[-1]) print("") except SystemExit: print("") except Exception as e: print(e) print("")
def decompose(other_args: List[str], ticker: str, stock: pd.DataFrame): """Decompose time series as: - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="decompose", description=""" Decompose time series as: - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality """, ) parser.add_argument( "-m", "--multiplicative", action="store_true", default=False, dest="multiplicative", help="decompose using multiplicative model instead of additive", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return stock = stock["Adj Close"] seasonal_periods = 5 # Hodrick-Prescott filter # See Ravn and Uhlig: http://home.uchicago.edu/~huhlig/papers/uhlig.ravn.res.2002.pdf lamb = 107360000000 fig = plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI, constrained_layout=True) spec = gridspec.GridSpec(ncols=4, nrows=5, figure=fig) fig.add_subplot(spec[0, :]) plt.plot(stock) plt.title(ticker + " (Time-Series)") if ns_parser.multiplicative: resultMul = seasonal_decompose(stock, model="multiplicative", period=seasonal_periods) cycleMul, trendMul = sm.tsa.filters.hpfilter( resultMul.trend[resultMul.trend.notna().values], lamb=lamb) # Multiplicative model fig.add_subplot(spec[1, :4]) plt.plot(resultMul.trend, lw=2, c="purple") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Multiplicative Cyclic-Trend") fig.add_subplot(spec[2, 0:2]) plt.plot(trendMul, lw=2, c="tab:blue") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Multiplicative Trend component") fig.add_subplot(spec[2, 2:]) plt.plot(cycleMul, lw=2, c="green") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Multiplicative Cycle component") fig.add_subplot(spec[3, :]) plt.plot(resultMul.seasonal, lw=2, c="orange") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Multiplicative Seasonal effect") fig.add_subplot(spec[4, :]) plt.plot(resultMul.resid, lw=2, c="red") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Multiplicative Residuals") else: resultAdd = seasonal_decompose(stock, model="additive", period=seasonal_periods) cycleAdd, trendAdd = sm.tsa.filters.hpfilter( resultAdd.trend[resultAdd.trend.notna().values], lamb=lamb) # Additive model fig.add_subplot(spec[1, :4]) plt.plot(resultAdd.trend, lw=2, c="purple") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Additive Cyclic-Trend") fig.add_subplot(spec[2, 0:2]) plt.plot(trendAdd, lw=2, c="tab:blue") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Additive Trend component") fig.add_subplot(spec[2, 2:]) plt.plot(cycleAdd, lw=2, c="green") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Additive Cycle component") fig.add_subplot(spec[3, :]) plt.plot(resultAdd.seasonal, lw=2, c="orange") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Additive Seasonal effect") fig.add_subplot(spec[4, :]) plt.plot(resultAdd.resid, lw=2, c="red") plt.xlim([stock.index[0], stock.index[-1]]) plt.title("Additive Residuals") if gtff.USE_ION: plt.ion() plt.show() print("") # From # https://otexts.com/fpp2/seasonal-strength.html print("Time-Series Level is " + str(round(stock.mean(), 2))) if ns_parser.multiplicative: FtMul = max(0, 1 - np.var( resultMul.resid)) / np.var(resultMul.trend + resultMul.resid) print("Strength of Trend: %.4f" % FtMul) FsMul = max( 0, 1 - np.var(resultMul.resid) / np.var(resultMul.seasonal + resultMul.resid), ) print("Strength of Seasonality: %.4f" % FsMul) else: FtAdd = max( 0, 1 - np.var(resultAdd.resid) / np.var(resultAdd.trend + resultAdd.resid), ) print("Strength of Trend: %.4f" % FtAdd) FsAdd = max( 0, 1 - np.var(resultAdd.resid) / np.var(resultAdd.seasonal + resultAdd.resid), ) print("Strength of Seasonality: %.4f" % FsAdd) print("") except Exception as e: print(e, "\n") return
def hist(other_args: List[str], ticker: str, stock: pd.DataFrame, start: datetime): """Plot histogram and density Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="hist", description=""" Histogram with depicted density and rug """, ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) stock = stock["Adj Close"] sns.distplot( stock, bins=35, color="blue", hist=True, hist_kws={"edgecolor": "black"}, kde=True, kde_kws={ "color": "black", "lw": 3, "label": "KDE" }, rug=True, rug_kws={"edgecolor": "orange"}, ) plt.title( f"Histogram with Density of {ticker} from {start.strftime('%Y-%m-%d')}" ) plt.ylabel("Density") plt.xlabel("Share Price") plt.grid(True) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e, "\n") return
def bwm(other_args: List[str], ticker: str, stock: pd.DataFrame, start: datetime): """Box and Whisker plot monthly Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="bwm", description=""" Box and Whisker plot monthly """, ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) stock = stock["Adj Close"] sns.set(style="whitegrid") box_plot = sns.boxplot(x=stock.index.month, y=stock) box_plot.set( xlabel="Month", ylabel="Share Price", title= f"Box-plot per Month of {ticker} from {start.strftime('%Y-%m-%d')}", ) l_months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ] l_ticks = list() for val in box_plot.get_xticklabels(): l_ticks.append(l_months[int(val.get_text()) - 1]) box_plot.set_xticklabels(l_ticks) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e, "\n") return
def rolling(other_args: List[str], ticker: str, stock: pd.DataFrame): """Rolling mean and std deviation Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="rolling", description=""" Rolling mean and std deviation """, ) parser.add_argument( "-w", "--window", dest="rolling_window", type=check_positive, default=10, help="rolling window", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return stock = stock["Adj Close"] rolling_mean = stock.rolling(ns_parser.rolling_window, center=True, min_periods=1).mean() rolling_std = stock.rolling(ns_parser.rolling_window, center=True, min_periods=1).std() _, axMean = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) axMean.plot(stock.index, stock.values, label=ticker, linewidth=2, color="black") axMean.plot(rolling_mean, linestyle="--", linewidth=3, color="blue") axMean.set_xlabel("Time") axMean.set_ylabel("Share Price", color="blue") axMean.legend(["Real values", "Rolling Mean"], loc=2) axMean.tick_params(axis="y", labelcolor="blue") axStd = axMean.twinx() axStd.plot(rolling_std, label="Rolling std", linestyle="--", color="green", linewidth=3) axStd.set_ylabel("Std Deviation") axStd.legend(["Rolling std"], loc=1) axStd.set_ylabel("Share Price standard deviation", color="green") axStd.tick_params(axis="y", labelcolor="green") axMean.set_title("Rolling mean and std with window " + str(ns_parser.rolling_window) + " applied to " + ticker) plt.xlim([stock.index[0], stock.index[-1]]) plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e, "\n") return
def cumulative_distribution_function(other_args: List[str], ticker: str, stock: pd.DataFrame, start: datetime): """Plot cumulative distribution function Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="cdf", description=""" Cumulative distribution function """, ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) stock = stock["Adj Close"] cdf = stock.value_counts().sort_index().div(len(stock)).cumsum() cdf.plot(lw=2) plt.title( f"Cumulative Distribution Function of {ticker} from {start.strftime('%Y-%m-%d')}" ) plt.ylabel("Probability") plt.xlabel("Share Price") minVal = stock.values.min() q25 = np.quantile(stock.values, 0.25) medianVal = np.quantile(stock.values, 0.5) q75 = np.quantile(stock.values, 0.75) data = [ (minVal, q25), (0.25, 0.25), "r", (q25, q25), (0, 0.25), "r", (minVal, medianVal), (0.5, 0.5), "r", (medianVal, medianVal), (0, 0.5), "r", (minVal, q75), (0.75, 0.75), "r", (q75, q75), (0, 0.75), "r", ] plt.plot(*data, ls="--") plt.text(minVal + (q25 - minVal) / 2, 0.27, "Q1", color="r", fontweight="bold") plt.text( minVal + (medianVal - minVal) / 2, 0.52, "Median", color="r", fontweight="bold", ) plt.text(minVal + (q75 - minVal) / 2, 0.77, "Q3", color="r", fontweight="bold") plt.xlim(cdf.index[0], cdf.index[-1]) plt.grid(True) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e, "\n") return
def exponential_smoothing(other_args: List[str], s_ticker: str, df_stock: pd.DataFrame): """ Perform exponential smoothing forecasting Parameters ---------- other_args: List[str] Argparse arguments s_ticker: str Loaded ticker df_stock: pd.DataFrame Loaded stock dataframe """ parser = argparse.ArgumentParser( add_help=False, prog="ets", description=""" Exponential Smoothing, see https://otexts.com/fpp2/taxonomy.html Trend='N', Seasonal='N': Simple Exponential Smoothing Trend='N', Seasonal='A': Exponential Smoothing Trend='N', Seasonal='M': Exponential Smoothing Trend='A', Seasonal='N': Holt’s linear method Trend='A', Seasonal='A': Additive Holt-Winters’ method Trend='A', Seasonal='M': Multiplicative Holt-Winters’ method Trend='Ad', Seasonal='N': Additive damped trend method Trend='Ad', Seasonal='A': Exponential Smoothing Trend='Ad', Seasonal='M': Holt-Winters’ damped method Trend component: N: None, A: Additive, Ad: Additive Damped Seasonality component: N: None, A: Additive, M: Multiplicative """, ) parser.add_argument( "-d", "--days", action="store", dest="n_days", type=check_positive, default=5, help="prediction days.", ) parser.add_argument( "-t", "--trend", action="store", dest="trend", type=check_valid_trend, default="N", help="Trend component: N: None, A: Additive, Ad: Additive Damped.", ) parser.add_argument( "-s", "--seasonal", action="store", dest="seasonal", type=check_valid_seasonal, default="N", help="Seasonality component: N: None, A: Additive, M: Multiplicative.", ) parser.add_argument( "-p", "--periods", action="store", dest="seasonal_periods", type=check_positive, default=5, help="Seasonal periods.", ) parser.add_argument( "-e", "--end", action="store", type=valid_date, dest="s_end_date", default=None, help="The end date (format YYYY-MM-DD) to select - Backtesting", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return # BACKTESTING if ns_parser.s_end_date: if ns_parser.s_end_date < df_stock.index[0]: print( "Backtesting not allowed, since End Date is older than Start Date of historical data\n" ) return if (ns_parser.s_end_date < get_next_stock_market_days( last_stock_day=df_stock.index[0], n_next_days=5 + ns_parser.n_days)[-1]): print( "Backtesting not allowed, since End Date is too close to Start Date to train model\n" ) return future_index = get_next_stock_market_days( last_stock_day=ns_parser.s_end_date, n_next_days=ns_parser.n_days) if future_index[-1] > datetime.datetime.now(): print( "Backtesting not allowed, since End Date + Prediction days is in the future\n" ) return df_future = df_stock[future_index[0]:future_index[-1]] df_stock = df_stock[:ns_parser.s_end_date] # Get ETS model model, title = get_exponential_smoothing_model( df_stock["Adj Close"].values, ns_parser.trend, ns_parser.seasonal, ns_parser.seasonal_periods, ) if model.mle_retvals.success: forecast = [ i if i > 0 else 0 for i in model.forecast(ns_parser.n_days) ] l_pred_days = get_next_stock_market_days( last_stock_day=df_stock["Adj Close"].index[-1], n_next_days=ns_parser.n_days, ) df_pred = pd.Series(forecast, index=l_pred_days, name="Price") if ~np.isnan(forecast).any(): print(f"\n{title}") print("\nFit model parameters:") for key, value in model.params.items(): print(f"{key} {' '*(18-len(key))}: {value}") print("\nAssess fit model:") print(f"AIC: {round(model.aic, 2)}") print(f"BIC: {round(model.bic, 2)}") print(f"SSE: {round(model.sse, 2)}\n") # Plotting plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.plot(df_stock.index, df_stock["Adj Close"], lw=2) # BACKTESTING if ns_parser.s_end_date: plt.title(f"BACKTESTING: {title} on {s_ticker}") else: plt.title(f"{title} on {s_ticker}") plt.xlim( df_stock.index[0], get_next_stock_market_days(df_pred.index[-1], 1)[-1], ) plt.xlabel("Time") plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.plot( [df_stock.index[-1], df_pred.index[0]], [df_stock["Adj Close"].values[-1], df_pred.values[0]], lw=1, c="tab:green", linestyle="--", ) plt.plot(df_pred.index, df_pred, lw=2, c="tab:green") plt.axvspan( df_stock.index[-1], df_pred.index[-1], facecolor="tab:orange", alpha=0.2, ) _, _, ymin, ymax = plt.axis() plt.vlines( df_stock.index[-1], ymin, ymax, linewidth=1, linestyle="--", color="k", ) # BACKTESTING if ns_parser.s_end_date: plt.plot( df_future.index, df_future["Adj Close"], lw=2, c="tab:blue", ls="--", ) plt.plot( [df_stock.index[-1], df_future.index[0]], [ df_stock["Adj Close"].values[-1], df_future["Adj Close"].values[0], ], lw=1, c="tab:blue", linestyle="--", ) if gtff.USE_ION: plt.ion() plt.show() # BACKTESTING if ns_parser.s_end_date: plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.subplot(211) plt.plot( df_future.index, df_future["Adj Close"], lw=2, c="tab:blue", ls="--", ) plt.plot(df_pred.index, df_pred, lw=2, c="green") plt.scatter( df_future.index, df_future["Adj Close"], c="tab:blue", lw=3, ) plt.plot( [df_stock.index[-1], df_future.index[0]], [ df_stock["Adj Close"].values[-1], df_future["Adj Close"].values[0], ], lw=2, c="tab:blue", ls="--", ) plt.scatter(df_pred.index, df_pred, c="green", lw=3) plt.plot( [df_stock.index[-1], df_pred.index[0]], [df_stock["Adj Close"].values[-1], df_pred.values[0]], lw=2, c="green", ls="--", ) plt.title("BACKTESTING: Real data price versus Prediction") plt.xlim( df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1), ) plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.legend(["Real data", "Prediction data"]) plt.xticks([]) plt.subplot(212) plt.axhline(y=0, color="k", linestyle="--", linewidth=2) plt.plot( df_future.index, 100 * (df_pred.values - df_future["Adj Close"].values) / df_future["Adj Close"].values, lw=2, c="red", ) plt.scatter( df_future.index, 100 * (df_pred.values - df_future["Adj Close"].values) / df_future["Adj Close"].values, c="red", lw=5, ) plt.title( "BACKTESTING: Error between Real data and Prediction [%]" ) plt.plot( [df_stock.index[-1], df_future.index[0]], [ 0, 100 * (df_pred.values[0] - df_future["Adj Close"].values[0]) / df_future["Adj Close"].values[0], ], lw=2, ls="--", c="red", ) plt.xlim( df_stock.index[-1], df_pred.index[-1] + datetime.timedelta(days=1), ) plt.xlabel("Time") plt.ylabel("Prediction Error (%)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) plt.legend(["Real data", "Prediction data"]) if gtff.USE_ION: plt.ion() plt.show() # Refactor prediction dataframe for backtesting print df_pred.name = "Prediction" df_pred = df_pred.to_frame() df_pred["Real"] = df_future["Adj Close"] if gtff.USE_COLOR: patch_pandas_text_adjustment() print("Time Real [$] x Prediction [$]") print( df_pred.apply(price_prediction_backtesting_color, axis=1).to_string()) else: print(df_pred[["Real", "Prediction"]].round(2).to_string()) print("") print_prediction_kpis(df_pred["Real"].values, df_pred["Prediction"].values) else: # Print prediction data print_pretty_prediction(df_pred, df_stock["Adj Close"].values[-1]) print("") else: print( "RuntimeWarning: invalid value encountered in double_scalars." ) else: print("ConvergenceWarning: Optimization failed to converge.") except Exception as e: print(e) print("")
df = df / 1_000 denomination = "[$ Thousands]" else: denomination = "" if raw: print_rich_table( df.fillna("-"), headers=list(df.columns), show_index=True, title=f"{item_name} {denomination}", ) else: # This plot has 1 axis if external_axes is None: _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) else: if len(external_axes) != 1: console.print("[red]Expected list of one axis item./n[/red]") return stocks_data, company_tickers (ax, ) = external_axes for company in df.columns: ax.plot(df[company], ls="-", marker="o", label=company) ax.set_title(f"{item_name} {denomination}") ax.legend() theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output()
def fibonacci_retracement( df_stock: pd.DataFrame, period: int = 120, start_date: Any = None, end_date: Any = None, s_ticker: str = "", export: str = "", ): """Calculate fibonacci retracement levels Parameters ---------- df_stock: pd.DataFrame Stock data period: int Days to lookback start_date: Any User picked date for starting retracement end_date: Any User picked date for ending retracement s_ticker:str Stock ticker export: str Format to export data """ ( df_fib, min_date, max_date, min_pr, max_pr, ) = custom_indicators_model.calculate_fib_levels(df_stock, period, start_date, end_date) levels = df_fib.Price fig, ax = plt.subplots(figsize=(plot_autoscale()), dpi=cfp.PLOT_DPI) ax.plot(df_stock["Adj Close"], "b") ax.plot([min_date, max_date], [min_pr, max_pr], c="k") for i in levels: ax.axhline(y=i, c="g", alpha=0.5) for i in range(5): ax.fill_between(df_stock.index, levels[i], levels[i + 1], alpha=0.6) ax.set_ylabel("Price") ax.set_title(f"Fibonacci Support for {s_ticker.upper()}") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax1 = ax.twinx() ax1.set_ylim(ax.get_ylim()) ax1.set_yticks(levels) ax1.set_yticklabels([0, 0.235, 0.382, 0.5, 0.618, 1]) plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) if gtff.USE_ION: plt.ion() plt.show() print_rich_table( df_fib, headers=["Fib Level", "Price"], show_index=False, title="Fibonacci retractment levels", ) console.print("") export_data( export, os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), "fib", df_fib, )
def acf(other_args: List[str], ticker: str, stock: pd.DataFrame, start: datetime): """Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data Parameters ---------- other_args : str Command line arguments to be processed with argparse ticker : str Ticker of the stock stock : pd.DataFrame Stock data """ parser = argparse.ArgumentParser( add_help=False, prog="acf", description=""" Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data """, ) parser.add_argument( "-l", "--lags", dest="lags", type=check_positive, default=15, help="maximum lags to display in plots", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return print(stock.head()) stock = stock["Adj Close"] fig = plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI, constrained_layout=True) spec = gridspec.GridSpec(ncols=2, nrows=2, figure=fig) # Diff Auto-correlation function for original time series ax_acf = fig.add_subplot(spec[0, 0]) sm.graphics.tsa.plot_acf(np.diff(np.diff(stock.values)), lags=ns_parser.lags, ax=ax_acf) plt.title( f"Diff({ticker}) Auto-Correlation from {start.strftime('%Y-%m-%d')}" ) # Diff Partial auto-correlation function for original time series ax_pacf = fig.add_subplot(spec[0, 1]) sm.graphics.tsa.plot_pacf(np.diff(np.diff(stock.values)), lags=ns_parser.lags, ax=ax_pacf) plt.title( f"Diff({ticker}) Partial Auto-Correlation from {start.strftime('%Y-%m-%d')}" ) # Diff Diff Auto-correlation function for original time series ax_acf = fig.add_subplot(spec[1, 0]) sm.graphics.tsa.plot_acf(np.diff(np.diff(stock.values)), lags=ns_parser.lags, ax=ax_acf) plt.title( f"Diff(Diff({ticker})) Auto-Correlation from {start.strftime('%Y-%m-%d')}" ) # Diff Diff Partial auto-correlation function for original time series ax_pacf = fig.add_subplot(spec[1, 1]) sm.graphics.tsa.plot_pacf(np.diff(np.diff(stock.values)), lags=ns_parser.lags, ax=ax_pacf) plt.title( f"Diff(Diff({ticker})) Partial Auto-Correlation from {start.strftime('%Y-%m-%d')}" ) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e, "\n") return
def display_big_mac_index( country_codes: List[str], raw: bool = False, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Display Big Mac Index for given countries Parameters ---------- country_codes : List[str] List of country codes to get for raw : bool, optional Flag to display raw data, by default False export : str, optional Format data, by default "" external_axes : Optional[List[plt.Axes]], optional External axes (3 axes are expected in the list), by default None """ df_cols = ["Date"] df_cols.extend(country_codes) big_mac = pd.DataFrame(columns=df_cols) for country in country_codes: df1 = nasdaq_model.get_big_mac_index(country) if not df1.empty: big_mac[country] = df1["dollar_price"] big_mac["Date"] = df1["Date"] big_mac.set_index("Date", inplace=True) if not big_mac.empty: if external_axes is None: _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) else: if len(external_axes) != 3: logger.error("Expected list of 3 axis items.") console.print("[red]Expected list of 3 axis items./n[/red]") return (ax,) = external_axes big_mac.plot(ax=ax, marker="o") ax.legend() ax.set_title("Big Mac Index (USD)") ax.set_ylabel("Price of Big Mac in USD") theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output() if raw: print_rich_table( big_mac, headers=list(big_mac.columns), title="Big Mac Index" ) console.print("") export_data( export, os.path.dirname(os.path.abspath(__file__)), "bigmac", big_mac ) console.print("") else: logger.error("Unable to get big mac data") console.print("[red]Unable to get big mac data[/red]\n")
def show_ef(stocks: List[str], other_args: List[str]): """Display efficient frontier Parameters ---------- stocks : List[str] List of the stocks to be included in the weights other_args : List[str] argparse other args """ parser = argparse.ArgumentParser( add_help=False, prog="ef", description="""This function plots random portfolios based on their risk and returns and shows the efficient frontier.""", ) parser.add_argument( "-p", "--period", default="3mo", dest="period", help="period to get yfinance data from", choices=period_choices, ) parser.add_argument( "-n", "--number-portfolios", default=300, type=check_non_negative, dest="n_port", help="number of portfolios to simulate", ) try: if other_args: if "-" not in other_args[0]: other_args.insert(0, "-n") ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") return stock_prices = process_stocks(stocks, ns_parser.period) mu = expected_returns.mean_historical_return(stock_prices) S = risk_models.sample_cov(stock_prices) ef = EfficientFrontier(mu, S) _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) # Generate random portfolios n_samples = ns_parser.n_port w = np.random.dirichlet(np.ones(len(mu)), n_samples) rets = w.dot(mu) stds = np.sqrt(np.diag(w @ S @ w.T)) sharpes = rets / stds ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r") plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True) # Find the tangency portfolio ef.max_sharpe() ret_sharpe, std_sharpe, _ = ef.portfolio_performance() ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe") ax.set_title(f"Efficient Frontier simulating {ns_parser.n_port} portfolios") ax.legend() plt.tight_layout() plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) if gtff.USE_ION: plt.ion() plt.show() print("") except Exception as e: print(e) print("")
def historical( other_args: List[str], df_stock: pd.DataFrame, ticker: str, start: datetime, interval: str, similar: List[str], ): """Display historical data from Yahoo Finance Parameters ---------- other_args : List[str] Command line arguments to be processed with argparse df_stock : pd.DataFrame Stock data ticker : str Ticker symbol start : datetime Time start interval : str Time interval similar : List[str] List of similar tickers """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="historical", description= """Historical price comparison between similar companies [Source: Yahoo Finance] """, ) parser.add_argument( "-s", "--similar", dest="l_similar", type=lambda s: [str(item) for item in s.split(",")], default=[], help="similar companies to compare with.", ) parser.add_argument( "-a", "--also", dest="l_also", type=lambda s: [str(item) for item in s.split(",")], default=[], help="apart from loaded similar companies also compare with.", ) parser.add_argument( "-t", "--type", action="store", dest="type_candle", type=check_one_of_ohlca, default="a", # in case it's adjusted close help=( "type of candles: o-open, h-high, l-low, c-close, a-adjusted close." ), ) try: if interval != "1440min": print( "Intraday historical data analysis comparison is not yet available." ) # Alpha Vantage only supports 5 calls per minute, we need another API to get intraday data else: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if ns_parser.l_similar: similar = ns_parser.l_similar similar += ns_parser.l_also plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.title(f"Similar companies to {ticker}") df_stock = yf.download(ticker, start=start, progress=False, threads=False) plt.plot(df_stock.index, df_stock[d_candle_types[ns_parser.type_candle]].values) # plt.plot(df_stock.index, df_stock["Adj Close"].values, lw=2) l_min = [df_stock.index[0]] l_leg = [ticker] l_stocks = similar[:] while l_stocks: l_parsed_stocks = [] for symbol in l_stocks: try: df_similar_stock = yf.download(symbol, start=start, progress=False, threads=False) if not df_similar_stock.empty: plt.plot( df_similar_stock.index, df_similar_stock[d_candle_types[ ns_parser.type_candle]].values, ) l_min.append(df_similar_stock.index[0]) l_leg.append(symbol) l_parsed_stocks.append(symbol) except Exception as e: print("") print(e) print( "Disregard previous error, which is due to API Rate limits from Yahoo Finance." ) print( f"Because we like '{symbol}', and we won't leave without getting data from it." ) for parsed_stock in l_parsed_stocks: l_stocks.remove(parsed_stock) plt.xlabel("Time") plt.ylabel("Share Price ($)") plt.legend(l_leg) plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) # ensures that the historical data starts from same datapoint plt.xlim([max(l_min), df_stock.index[-1]]) plt.show() print("") except SystemExit: print("Similar companies need to be provided", "\n") except Exception as e: print(e, "\n")
def plot_oi( ticker: str, expiry: str, min_sp: float, max_sp: float, calls_only: bool, puts_only: bool, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Plot open interest Parameters ---------- ticker: str Ticker expiry: str Expiry date for options min_sp: float Min strike to consider max_sp: float Max strike to consider calls_only: bool Show calls only puts_only: bool Show puts only export: str Format to export file external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ options = tradier_model.get_option_chains(ticker, expiry) current_price = tradier_model.last_price(ticker) if min_sp == -1: min_strike = 0.75 * current_price else: min_strike = min_sp if max_sp == -1: max_strike = 1.25 * current_price else: max_strike = max_sp if calls_only and puts_only: console.print("Both flags selected, please select one", "\n") return calls = options[options.option_type == "call"][["strike", "open_interest"]] puts = options[options.option_type == "put"][["strike", "open_interest"]] call_oi = calls.set_index("strike")["open_interest"] / 1000 put_oi = puts.set_index("strike")["open_interest"] / 1000 df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True) df_opt = df_opt.rename( columns={"open_interest_x": "OI_call", "open_interest_y": "OI_put"} ) max_pain = op_helpers.calculate_max_pain(df_opt) if external_axes is None: _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI) else: if len(external_axes) != 1: console.print("[red]Expected list of one axis item./n[/red]") return (ax,) = external_axes if not calls_only: put_oi.plot( x="strike", y="open_interest", label="Puts", ax=ax, marker="o", ls="-", ) if not puts_only: call_oi.plot( x="strike", y="open_interest", label="Calls", ax=ax, marker="o", ls="-", ) ax.axvline(current_price, lw=2, ls="--", label="Current Price", alpha=0.7) ax.axvline(max_pain, lw=3, label=f"Max Pain: {max_pain}", alpha=0.7) ax.set_xlabel("Strike Price") ax.set_ylabel("Open Interest (1k) ") ax.set_xlim(min_strike, max_strike) ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}") theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output() export_data( export, os.path.dirname(os.path.abspath(__file__)), "oi_tr", options, )
def plot_adx( s_ticker: str, s_interval: str, df_stock: pd.DataFrame, length: int, scalar: int, drift: int, export: str, ): """Plot ADX indicator Parameters ---------- s_ticker : str Ticker s_interval : str Interval for data df_stock : pd.DataFrame Dataframe of prices length : int Length of window scalar : int Scalar variable drift : int Drift variable export: str Format to export data """ df_ta = trend_indicators_model.adx(s_interval, df_stock, length, scalar, drift) fig, ax = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax0 = ax[0] ax0.plot(df_stock.index, df_stock["Close"].values, "k", lw=2) ax0.set_title(f"Average Directional Movement Index (ADX) on {s_ticker}") ax0.set_xlim(df_stock.index[0], df_stock.index[-1]) ax0.set_ylabel("Share Price ($)") ax0.grid(b=True, which="major", color="#666666", linestyle="-") ax1 = ax[1] ax1.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=2) ax1.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=1) ax1.plot(df_ta.index, df_ta.iloc[:, 2].values, "r", lw=1) ax1.set_xlim(df_stock.index[0], df_stock.index[-1]) ax1.axhline(25, linewidth=3, color="k", ls="--") ax1.legend( [ f"ADX ({df_ta.columns[0]})", f"+DI ({df_ta.columns[1]})", f"- DI ({df_ta.columns[2]})", ], loc="upper left", ) ax1.set_xlabel("Time") ax1.grid(b=True, which="major", color="#666666", linestyle="-") ax1.set_ylim([0, 100]) if gtff.USE_ION: plt.ion() fig.tight_layout() plt.gcf().autofmt_xdate() plt.show() print("") export_data( export, os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), "adx", df_ta, )
def plot_vol( ticker: str, expiry: str, min_sp: float, max_sp: float, calls_only: bool, puts_only: bool, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Plot volume Parameters ---------- ticker: str Ticker expiry: str Expiry date for options min_sp: float Min strike to consider max_sp: float Max strike to consider calls_only: bool Show calls only puts_only: bool Show puts only export: str Format to export file external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ options = tradier_model.get_option_chains(ticker, expiry) current_price = tradier_model.last_price(ticker) if min_sp == -1: min_strike = 0.75 * current_price else: min_strike = min_sp if max_sp == -1: max_strike = 1.25 * current_price else: max_strike = max_sp if calls_only and puts_only: console.print("Both flags selected, please select one", "\n") return calls = options[options.option_type == "call"][["strike", "volume"]] puts = options[options.option_type == "put"][["strike", "volume"]] call_v = calls.set_index("strike")["volume"] / 1000 put_v = puts.set_index("strike")["volume"] / 1000 if external_axes is None: _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI) else: ax = external_axes[0] if not calls_only: put_v.plot( x="strike", y="volume", label="Puts", ax=ax, marker="o", ls="-", c="r", ) if not puts_only: call_v.plot( x="strike", y="volume", label="Calls", ax=ax, marker="o", ls="-", c="g", ) ax.axvline(current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7) ax.grid("on") ax.set_xlabel("Strike Price") ax.set_ylabel("Volume (1k) ") ax.set_xlim(min_strike, max_strike) ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}") theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output() export_data( export, os.path.dirname(os.path.abspath(__file__)), "vol_tr", options, ) console.print("")
def display_correlation_interest( ticker: str, df_data: pd.DataFrame, words: List[str], export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Plot interest over time of words/sentences versus stock price. [Source: Google] Parameters ---------- ticker : str Ticker to check price df_data : pd.DataFrame Data dataframe words : List[str] Words to check for interest for export: str Format to export data external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ # This plot has 1 axis if external_axes is None: _, ax = plt.subplots( figsize=plot_autoscale(), dpi=PLOT_DPI, nrows=2, ncols=1, sharex=True, gridspec_kw={"height_ratios": [1, 2]}, ) else: if len(external_axes) != 1: logger.error("Expected list of one axis item.") console.print("[red]Expected list of one axis item./n[/red]") return (ax, ) = external_axes ax[0].set_title( f"{ticker.upper()} stock price and interest over time on {','.join(words)}" ) ax[0].plot( df_data.index, df_data["Adj Close"].values, c="#FCED00", ) ax[0].set_ylabel("Stock Price") ax[0].set_xlim(df_data.index[0], df_data.index[-1]) colors = theme.get_colors()[1:] for idx, word in enumerate(words): df_interest = google_model.get_mentions(word) ax[1].plot(df_interest.index, df_interest[word], "-", color=colors[idx]) ax[1].set_ylabel("Interest [%]") ax[1].set_xlim(df_data.index[0], df_data.index[-1]) ax[1].legend(words) theme.style_primary_axis(ax[0]) theme.style_primary_axis(ax[1]) if external_axes is None: theme.visualize_output() export_data(export, os.path.dirname(os.path.abspath(__file__)), "interest", df_interest)
def plot_volume_open_interest( ticker: str, expiry: str, min_sp: float, max_sp: float, min_vol: float, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Plot volume and open interest Parameters ---------- ticker: str Stock ticker expiry: str Option expiration min_sp: float Min strike price max_sp: float Max strike price min_vol: float Min volume to consider export: str Format for exporting data external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ current_price = tradier_model.last_price(ticker) options = tradier_model.get_option_chains(ticker, expiry) calls = options[options.option_type == "call"][ ["strike", "volume", "open_interest"] ] puts = options[options.option_type == "put"][["strike", "volume", "open_interest"]] # Process Calls Data df_calls = calls.pivot_table( index="strike", values=["volume", "open_interest"], aggfunc="sum" ).reindex() df_calls["strike"] = df_calls.index df_calls["type"] = "calls" df_calls["open_interest"] = df_calls["open_interest"] df_calls["volume"] = df_calls["volume"] df_calls["oi+v"] = df_calls["open_interest"] + df_calls["volume"] df_calls["spot"] = round(current_price, 2) df_puts = puts.pivot_table( index="strike", values=["volume", "open_interest"], aggfunc="sum" ).reindex() df_puts["strike"] = df_puts.index df_puts["type"] = "puts" df_puts["open_interest"] = df_puts["open_interest"] df_puts["volume"] = -df_puts["volume"] df_puts["open_interest"] = -df_puts["open_interest"] df_puts["oi+v"] = df_puts["open_interest"] + df_puts["volume"] df_puts["spot"] = round(current_price, 2) call_oi = calls.set_index("strike")["open_interest"] / 1000 put_oi = puts.set_index("strike")["open_interest"] / 1000 df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True) df_opt = df_opt.rename( columns={"open_interest_x": "OI_call", "open_interest_y": "OI_put"} ) max_pain = op_helpers.calculate_max_pain(df_opt) if min_vol == -1 and min_sp == -1 and max_sp == -1: # If no argument provided, we use the percentile 50 to get 50% of upper volume data volume_percentile_threshold = 50 min_vol_calls = np.percentile(df_calls["oi+v"], volume_percentile_threshold) min_vol_puts = np.percentile(df_puts["oi+v"], volume_percentile_threshold) df_calls = df_calls[df_calls["oi+v"] > min_vol_calls] df_puts = df_puts[df_puts["oi+v"] < min_vol_puts] else: if min_vol > -1: df_calls = df_calls[df_calls["oi+v"] > min_vol] df_puts = df_puts[df_puts["oi+v"] < -min_vol] if min_sp > -1: df_calls = df_calls[df_calls["strike"] > min_sp] df_puts = df_puts[df_puts["strike"] > min_sp] if max_sp > -1: df_calls = df_calls[df_calls["strike"] < max_sp] df_puts = df_puts[df_puts["strike"] < max_sp] if df_calls.empty and df_puts.empty: console.print( "The filtering applied is too strong, there is no data available for such conditions.\n" ) return # Initialize the matplotlib figure if external_axes is None: _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI) else: ax = external_axes[0] # make x axis symmetric axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"]))) ax.set_xlim(-axis_origin, +axis_origin) g = sns.barplot( x="oi+v", y="strike", data=df_calls, label="Calls: Open Interest", color="lightgreen", orient="h", ) g = sns.barplot( x="volume", y="strike", data=df_calls, label="Calls: Volume", color="green", orient="h", ) g = sns.barplot( x="oi+v", y="strike", data=df_puts, label="Puts: Open Interest", color="pink", orient="h", ) g = sns.barplot( x="volume", y="strike", data=df_puts, label="Puts: Volume", color="red", orient="h", ) # draw spot line s = [float(strike.get_text()) for strike in ax.get_yticklabels()] spot_index = bisect_left(s, current_price) # find where the spot is on the graph spot_line = ax.axhline(spot_index, ls="--", alpha=0.3) # draw max pain line max_pain_index = bisect_left(s, max_pain) max_pain_line = ax.axhline(max_pain_index, ls="-", alpha=0.3, color="red") max_pain_line.set_linewidth(5) # format ticklabels without - for puts g.set_xticks(g.get_xticks()) xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()] g.set_xticklabels(xlabels) ax.set_title( f"{ticker} volumes for {expiry}\n(open interest displayed only during market hours)" ) ax.invert_yaxis() _ = ax.legend() handles, _ = ax.get_legend_handles_labels() handles.append(spot_line) handles.append(max_pain_line) # create legend labels + add to graph labels = [ "Calls open interest", "Calls volume ", "Puts open interest", "Puts volume", "Current stock price", f"Max pain = {max_pain}", ] ax.legend(handles=handles[:], labels=labels, loc="lower left") sns.despine(left=True, bottom=True) theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output() export_data( export, os.path.dirname(os.path.abspath(__file__)), "voi_tr", options, )
def display_government_sells( gov_type: str, past_transactions_months: int = 6, num: int = 10, raw: bool = False, export: str = "", ): """Top buy government trading [Source: quiverquant.com] Parameters ---------- gov_type: str Type of government data between: congress, senate and house past_transactions_months: int Number of months to get trading for num: int Number of tickers to show raw: bool Display raw data export: str Format to export data """ df_gov = quiverquant_model.get_government_trading(gov_type) if df_gov.empty: print(f"No {gov_type} trading data found\n") return df_gov = df_gov.sort_values("TransactionDate", ascending=False) start_date = datetime.now() - timedelta(days=past_transactions_months * 30) df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna() df_gov["Range"] = df_gov["Range"].apply( lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x ) df_gov["min"] = df_gov["Range"].apply( lambda x: x.split("-")[0] .strip("$") .replace(",", "") .strip() .replace(">$", "") .strip() ) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1] .replace(",", "") .strip() .strip("$") .replace(">$", "") .strip() if "-" in x else x.strip("$").replace(",", "").replace(">$", "").strip() ) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]), axis=1, ) df_gov = df_gov.sort_values("TransactionDate", ascending=True) if raw: df = pd.DataFrame( df_gov.groupby("Ticker")["upper"] .sum() .div(1000) .sort_values(ascending=True) .abs() .head(n=num) ) if gtff.USE_TABULATE_DF: print( tabulate( df, headers=["Amount ($1k)"], tablefmt="fancy_grid", showindex=True ) ) else: print(df.to_string()) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head( n=num ).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title( f"{num} most sold stocks over last {past_transactions_months} months" f" (upper bound) for {gov_type}" ) plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False) if gtff.USE_ION: plt.ion() fig.tight_layout() plt.show() print("") export_data(export, os.path.dirname(os.path.abspath(__file__)), "topsells", df_gov)
def display_historical( ticker: str, expiry: str, strike: float, put: bool, raw: bool, chain_id: str, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): """Plot historical option prices Parameters ---------- ticker: str Stock ticker expiry: str Expiry date of option strike: float Option strike price put: bool Is this a put option? raw: bool Print raw data chain_id: str OCC option symbol export: str Format of export file external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ df_hist = tradier_model.get_historical_options( ticker, expiry, strike, put, chain_id ) if raw: print_rich_table( df_hist, headers=[x.title() for x in df_hist.columns], title="Historical Option Prices", ) op_type = ["call", "put"][put] candle_chart_kwargs = { "type": "candle", "style": theme.mpf_style, "volume": True, "xrotation": theme.xticks_rotation, "scale_padding": {"left": 0.3, "right": 1.2, "top": 0.8, "bottom": 0.8}, "update_width_config": { "candle_linewidth": 0.6, "candle_width": 0.8, "volume_linewidth": 0.8, "volume_width": 0.8, }, } if external_axes is None: candle_chart_kwargs["returnfig"] = True candle_chart_kwargs["figratio"] = (10, 7) candle_chart_kwargs["figscale"] = 1.10 candle_chart_kwargs["figsize"] = plot_autoscale() fig, _ = mpf.plot(df_hist, **candle_chart_kwargs) fig.suptitle( f"Historical {strike} {op_type.title()}", x=0.055, y=0.965, horizontalalignment="left", ) theme.visualize_output(force_tight_layout=False) else: if len(external_axes) != 2: console.print("[red]Expected list of 2 axis items./n[/red]") return (ax1, ax2) = external_axes candle_chart_kwargs["ax"] = ax1 candle_chart_kwargs["volume"] = ax2 mpf.plot(df_hist, **candle_chart_kwargs) console.print() if export: export_data( export, os.path.dirname(os.path.abspath(__file__)), "hist", df_hist, )
def display_qtr_contracts(analysis: str, num: int, raw: bool = False, export: str = ""): """Quarterly contracts [Source: quiverquant.com] Parameters ---------- analysis: str Analysis to perform. Either 'total', 'upmom' 'downmom' num: int Number to show raw: bool Flag to display raw data export: str Format to export data """ df_contracts = quiverquant_model.get_government_trading("quarter-contracts") if df_contracts.empty: print("No quarterly government contracts found\n") return tickers = quiverquant_model.analyze_qtr_contracts(analysis, num) if analysis in ("upmom", "downmom"): if raw: if gtff.USE_TABULATE_DF: print( tabulate( pd.DataFrame(tickers.values), headers=["tickers"], tablefmt="fancy_grid", showindex=False, ) ) else: print(tickers.to_string()) else: fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) max_amount = 0 quarter_ticks = [] for symbol in tickers: amounts = ( df_contracts[df_contracts["Ticker"] == symbol] .sort_values(by=["Year", "Qtr"])["Amount"] .values ) qtr = ( df_contracts[df_contracts["Ticker"] == symbol] .sort_values(by=["Year", "Qtr"])["Qtr"] .values ) year = ( df_contracts[df_contracts["Ticker"] == symbol] .sort_values(by=["Year", "Qtr"])["Year"] .values ) ax.plot( np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15 ) if len(amounts) > max_amount: max_amount = len(amounts) quarter_ticks = [ f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr) ] ax.set_xlim([-0.5, max_amount - 0.5]) ax.set_xticks(np.arange(0, max_amount)) ax.set_xticklabels(quarter_ticks) ax.grid() ax.legend(tickers) titles = { "upmom": "Highest increasing quarterly Government Contracts", "downmom": "Highest decreasing quarterly Government Contracts", } ax.set_title(titles[analysis]) ax.set_xlabel("Date") ax.set_ylabel("Amount ($1M)") fig.tight_layout() if gtff.USE_ION: plt.ion() plt.show() elif analysis == "total": if gtff.USE_TABULATE_DF: print( tabulate( tickers, headers=["Total"], tablefmt="fancy_grid", floatfmt=".2e" ) ) else: print(tickers.to_string()) export_data( export, os.path.dirname(os.path.abspath(__file__)), "qtrcontracts", df_contracts ) print("")
async def donchian_command(ctx, ticker="", upper_length="25", lower_length="100", start="", end=""): """Displays chart with donchian channel [Yahoo Finance]""" try: # Debug if cfg.DEBUG: print( f"!stocks.ta.donchian {ticker} {upper_length} {lower_length} {start} {end}" ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not upper_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") upper_length = float(upper_length) if not lower_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") lower_length = float(lower_length) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = volatility_model.donchian(df_stock, upper_length, lower_length) # Output Data fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(df_stock.index, df_stock["Adj Close"].values, color="k", lw=3) ax.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=1.5, label="upper") ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--") ax.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=1.5, label="lower") ax.set_title(f"{ticker} donchian") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_xlabel("Time") ax.set_ylabel("Price ($)") ax.legend( [ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]]) ax.fill_between( df_ta.index, df_ta.iloc[:, 0].values, df_ta.iloc[:, 2].values, alpha=0.1, color="b", ) ax.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() plt.savefig("ta_donchian.png") uploaded_image = gst_imgur.upload_image("ta_donchian.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: print(f"Image URL: {image_link}") title = "Stocks: Donchian-Channels " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_donchian.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Donchian-Channels", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
def plot_pattern_recognition( ticker: str, resolution: str, export: str, external_axes: Optional[List[plt.Axes]] = None, ): """Plot pattern recognition signal Parameters ---------- ticker : str Ticker to display pattern recognition on top of the data resolution : str Resolution of data to get pattern recognition from export: str Format of export file external_axes : Optional[List[plt.Axes]], optional External axes (1 axis is expected in the list), by default None """ pattern = finnhub_model.get_pattern_recognition(ticker, resolution) export_data( export, os.path.dirname(os.path.abspath(__file__)), "pr", pattern, ) l_segments = [] for i in pattern: a_part = ("", "") if "aprice" in pattern[i]: if pattern[i]["aprice"] != 0 and not math.isnan( pattern[i]["aprice"]): a_part = ( datetime.utcfromtimestamp( pattern[i]["atime"]).strftime("%Y-%m-%d"), pattern[i]["aprice"], ) b_part = ("", "") if "bprice" in pattern[i]: if pattern[i]["bprice"] != 0 and not math.isnan( pattern[i]["bprice"]): b_part = ( datetime.utcfromtimestamp( pattern[i]["btime"]).strftime("%Y-%m-%d"), pattern[i]["bprice"], ) c_part = ("", "") if "cprice" in pattern[i]: if pattern[i]["cprice"] != 0 and not math.isnan( pattern[i]["cprice"]): c_part = ( datetime.utcfromtimestamp( pattern[i]["ctime"]).strftime("%Y-%m-%d"), pattern[i]["cprice"], ) d_part = ("", "") if "dprice" in pattern[i]: if pattern[i]["dprice"] != 0 and not math.isnan( pattern[i]["dprice"]): d_part = ( datetime.utcfromtimestamp( pattern[i]["dtime"]).strftime("%Y-%m-%d"), pattern[i]["dprice"], ) segment = (a_part, b_part, c_part, d_part) l_segment = list(segment) while ("", "") in l_segment: l_segment.remove(("", "")) segm = tuple(l_segment) l_segments.append(segm) start_time = 999999999999 for i in pattern: if pattern[i]["atime"] < start_time: start_time = pattern[i]["atime"] df_stock = yf.download( ticker, start=datetime.utcfromtimestamp(start_time).strftime("%Y-%m-%d"), progress=False, ) df_stock["date_id"] = (df_stock.index.date - df_stock.index.date.min()).astype("timedelta64[D]") df_stock["date_id"] = df_stock["date_id"].dt.days + 1 df_stock["OC_High"] = df_stock[["Open", "Close"]].max(axis=1) df_stock["OC_Low"] = df_stock[["Open", "Close"]].min(axis=1) candle_chart_kwargs = { "type": "candle", "style": theme.mpf_style, "volume": False, "alines": l_segments, "xrotation": theme.xticks_rotation, "scale_padding": { "left": 0.3, "right": 1, "top": 0.8, "bottom": 0.8 }, "update_width_config": { "candle_linewidth": 0.6, "candle_width": 0.8, "volume_linewidth": 0.8, "volume_width": 0.8, }, "warn_too_much_data": 10000, } # This plot has 2 axes if not external_axes: candle_chart_kwargs["returnfig"] = True candle_chart_kwargs["figratio"] = (10, 7) candle_chart_kwargs["figscale"] = 1.10 candle_chart_kwargs["figsize"] = plot_autoscale() (fig, ax) = mpf.plot(df_stock, **candle_chart_kwargs) fig.suptitle( f"\n{ticker}", x=0.055, y=0.965, horizontalalignment="left", ) theme.visualize_output(force_tight_layout=False) else: if len(external_axes) != 1: logger.error("Expected list of one axis item.") console.print("[red]Expected list of 1 axis items./n[/red]") return (ax, ) = external_axes candle_chart_kwargs["ax"] = ax mpf.plot(df_stock, **candle_chart_kwargs) for ix in range(len(pattern.columns)): console.print( f"From {l_segments[ix][0][0]} to {l_segments[ix][-1][0]}") console.print( f"Pattern: {pattern[0]['patternname']} ({pattern[0]['patterntype']})", "\n")
def candle(s_ticker: str, other_args: List[str]): """Shows candle plot of loaded ticker Parameters ---------- s_ticker: str Ticker to display other_args: str Argparse arguments """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="candle", description="Displays candle chart of loaded ticker", ) parser.add_argument( "-s", "--start_date", dest="s_start", type=valid_date, default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), help="Start date for candle data", ) parser.add_argument( "--plotly", dest="plotly", action="store_true", default=False, help="Flag to show interactive plot using plotly.", ) # TODO: Add option to customize plot even further try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if not s_ticker: print("No ticker loaded. First use `load {ticker}`", "\n") return df_stock = trend.load_ticker(s_ticker, ns_parser.s_start) df_stock["ma20"] = df_stock["Close"].rolling(20).mean().fillna(method="bfill") df_stock["ma50"] = df_stock["Close"].rolling(50).mean().fillna(method="bfill") df_stock = trend.find_trendline(df_stock, "OC_High", "high") df_stock = trend.find_trendline(df_stock, "OC_Low", "low") if not ns_parser.plotly: mc = mpf.make_marketcolors( up="green", down="red", edge="black", wick="black", volume="in", ohlc="i", ) s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=True) ap0 = [] if "OC_High_trend" in df_stock.columns: ap0.append( mpf.make_addplot(df_stock["OC_High_trend"], color="g"), ) if "OC_Low_trend" in df_stock.columns: ap0.append( mpf.make_addplot(df_stock["OC_Low_trend"], color="b"), ) if gtff.USE_ION: plt.ion() mpf.plot( df_stock, type="candle", mav=(20, 50), volume=True, title=f"\n{s_ticker} - Starting {ns_parser.s_start.strftime('%Y-%m-%d')}", addplot=ap0, xrotation=10, style=s, figratio=(10, 7), figscale=1.10, figsize=(plot_autoscale()), update_width_config=dict( candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0 ), ) else: fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.06, subplot_titles=(f"{s_ticker}", "Volume"), row_width=[0.2, 0.7], ) fig.add_trace( go.Candlestick( x=df_stock.index, open=df_stock.Open, high=df_stock.High, low=df_stock.Low, close=df_stock.Close, name="OHLC", ), row=1, col=1, ) fig.add_trace( go.Scatter( x=df_stock.index, y=df_stock["ma20"], name="MA20", mode="lines", line=go.scatter.Line(color="royalblue"), ), row=1, col=1, ) fig.add_trace( go.Scatter( x=df_stock.index, y=df_stock["ma50"], name="MA50", mode="lines", line=go.scatter.Line(color="black"), ), row=1, col=1, ) if "OC_High_trend" in df_stock.columns: fig.add_trace( go.Scatter( x=df_stock.index, y=df_stock["OC_High_trend"], name="High Trend", mode="lines", line=go.scatter.Line(color="red"), ), row=1, col=1, ) if "OC_Low_trend" in df_stock.columns: fig.add_trace( go.Scatter( x=df_stock.index, y=df_stock["OC_Low_trend"], name="Low Trend", mode="lines", line=go.scatter.Line(color="green"), ), row=1, col=1, ) fig.add_trace( go.Bar( x=df_stock.index, y=df_stock.Volume, name="Volume", marker_color="black", ), row=2, col=1, ) fig.update_layout( yaxis_title="Stock Price ($)", xaxis=dict( rangeselector=dict( buttons=list( [ dict( count=1, label="1m", step="month", stepmode="backward", ), dict( count=3, label="3m", step="month", stepmode="backward", ), dict( count=1, label="YTD", step="year", stepmode="todate" ), dict( count=1, label="1y", step="year", stepmode="backward", ), dict(step="all"), ] ) ), rangeslider=dict(visible=False), type="date", ), ) fig.show() print("") except Exception as e: print(e, "\n")